Чтение онлайн

на главную - закладки

Жанры

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:
Примечание

Вообще говоря, ситуация, когда сервер не отправит данные за один раз, и их отправка растянется на несколько итераций петли сообщений, настолько редка, что при разработке сервера, к которому не предъявляются повышенные требования по надежности, ее можно было бы вообще не учитывать. Соответственно, возможность получения сервером нового уведомления о поступлении данных до того, как на старое сообщение будет дан ответ, возможна только тогда, когда клиент не соблюдает принятый протокол и посылает несколько сообщений подряд, не дожидаясь ответа.

Наш пример призван продемонстрировать наиболее надежный к подобным действиям клиента сервер, поэтому мы его напишем "по всем правилам".

Как обычно, работа сервера начинается с инициализации слушающего сокета, выполняющейся при нажатии кнопки Запустить (листинг 2.50).

Листинг 2.50. Инициализация сервера, основанного на сообщениях

procedure TServerForm.BtnStartServerClick(Sender: TObject);

var

 // Адрес, к которому привязывается слушающий сокет

 ServerAddr: TSockAddr;

begin

 // Формируем адрес для привязки.

 FillChar(ServerAddr.sin_zero, SizeOf(ServerAddr.sin_zero), 0);

 ServerAddr.sin_family := AF_INET;

 ServerAddr.sin_addr.S_addr := INADDR_ANY;

 try

ServerAddr.sin_port := htons(StrToInt(EditPortNumber.Text));

if ServerAddr.sin_port = 0 then

begin

MessageDlg('Номер порта должен находиться в диапазоне 1-65535',

mtError, [mbOK], 0);

Exit;

end;

// Создание сокета

FServerSocket := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

if FServerSocket = INVALID_SOCKET then

begin

MessageDlg('Ошибка при создании сокета:'#13#10 +

GetErrorString, mtError, [mbOK], 0);

Exit;

end;

// Привязка сокета к адресу

if bind(FServerSocket, ServerAddr, SizeOf(ServerAddr)) = SOCKET_ERROR then

begin

MessageDlg('Ошибка при привязке сокета к адресу:'#13#10 +

GetErrorString, mtError, [mbOK], 0);

closesocket(FServerSocket);

Exit;

end;

// Перевод сокета в режим прослушивания

if listen(FServerSocket, SOMAXCONN) = SOCKET_ERROR then

begin

MessageDlg('Ошибка при переводе сокета в режим прослушивания:'#13#10 +

GetErrorString, mtError, [mbOK], 0);

closesocket(FServerSocket);

Exit;

end;

//
Связь слушающего сокета с событием FD_ACCEPT

if WSAAsyncSelect(FServerSocket, Handle,

WM_ACCEPTMESSAGE, FD_ACCEPT) = SOCKET_ERROR then

begin

MessageDlg('Ошибка при установке асинхронного режима ' +

'cлушающего сокета:'#13#10 + GetErrorString, mtError, [mbOK], 0);

closesocket(FServerSocket);

Exit;

end;

// Перевод элементов управления в состояние "Сервер работает"

LabelPortNumber.Enabled := False;

EditPortNumber.Enabled := False;

BtnStartServer.Enabled := False;

LabelServerState.Caption := 'Сервер работает';

 except

on EConvertError do

// Это исключение может возникнуть только в одном месте -

// при вызове StrToInt(EditPortNumber.Text)

MessageDlg('"' + EditPortNumber.Text +

'" не является целый числом', mtError, [mbOK], 0);

on ERangeError do

// Это исключение может возникнуть только в одном месте -

// при присваивании значения номеру порта

MessageDlg('Номер порта должен находиться в диапазоне 1-65535',

mtError, [mbOK], 0);

 end;

end;

Этот код мало чем отличается от того, что мы уже видели (сравните, например, с листингами 2.19 и 2.30). Единственное существенное отличие здесь — вызов функции

WSAAsyncSelect
после перевода сокета в режим прослушивания. Этот вызов связывает событие
FD_ACCEPT
с сообщением
WM_ACCEPTMESSAGE
.

Сообщение

WM_ACCEPTMESSAGE
нестандартное, мы должны сами определить его. Использовать это сообщение сервер будет только для определения момента подключения нового клиента, определять момент прихода данных мы будем с помощью другого сообщения —
WM_SOCKETMESSAGE
, которое тоже нужно определить. И, чтобы легче было писать обработчики для этих сообщений, объявим тип
TWMSocketMessage
, "совместимый" с типом
TMessage
(листинг 2.51).

Листинг 2.51. Сообщения, связанные с сокетами, и тип
TWMSocketMessage
Поделиться:
Популярные книги

Дракон с подарком

Суббота Светлана
3. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
6.62
рейтинг книги
Дракон с подарком

Бывшие. Война в академии магии

Берг Александра
2. Измены
Любовные романы:
любовно-фантастические романы
7.00
рейтинг книги
Бывшие. Война в академии магии

Мастер клинков. Начало пути

Распопов Дмитрий Викторович
1. Мастер клинков
Фантастика:
фэнтези
9.16
рейтинг книги
Мастер клинков. Начало пути

Имя нам Легион. Том 8

Дорничев Дмитрий
8. Меж двух миров
Фантастика:
боевая фантастика
рпг
аниме
5.00
рейтинг книги
Имя нам Легион. Том 8

Измена. Право на счастье

Вирго Софи
1. Чем закончится измена
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Измена. Право на счастье

Начальник милиции 2

Дамиров Рафаэль
2. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции 2

Камень. Книга 4

Минин Станислав
4. Камень
Фантастика:
боевая фантастика
7.77
рейтинг книги
Камень. Книга 4

Измена. Мой заклятый дракон

Марлин Юлия
Любовные романы:
любовно-фантастические романы
7.50
рейтинг книги
Измена. Мой заклятый дракон

Предатель. Цена ошибки

Кучер Ая
Измена
Любовные романы:
современные любовные романы
5.75
рейтинг книги
Предатель. Цена ошибки

Звездная Кровь. Изгой

Елисеев Алексей Станиславович
1. Звездная Кровь. Изгой
Фантастика:
боевая фантастика
попаданцы
рпг
5.00
рейтинг книги
Звездная Кровь. Изгой

Рождение победителя

Каменистый Артем
3. Девятый
Фантастика:
фэнтези
альтернативная история
9.07
рейтинг книги
Рождение победителя

Барону наплевать на правила

Ренгач Евгений
7. Закон сильного
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Барону наплевать на правила

Камень. Книга шестая

Минин Станислав
6. Камень
Фантастика:
боевая фантастика
7.64
рейтинг книги
Камень. Книга шестая

Чужая дочь

Зика Натаэль
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Чужая дочь