// к взаимной блокировке: главная нить ждала бы, когда завершится
// нить TListenThread, а та, в свою очередь - когда главная нить
// выполнит Synchronize. Чтобы этого не происходило, организуется
// ожидание с локальной петлей сообщений.
if Assigned(FListenThread) then
begin
FListenThread.StopServer;
while Assigned(FListenThread) do
begin
Application.ProcessMessages;
Sleep(10);
end;
end;
end;
Данный метод вызывается в обработчике нажатия кнопки Остановитьи при завершении приложения. Сервер можно многократно останавливать и запуска вновь, не завершая приложение.
Чтобы увидеть все возможности сервера, потребуется новый клиент. На компакт-диске он называется EventSelectClient, но "EventSelect" в данном случае означает только то, что клиент является парным к серверу EventSelectServer. Сам клиент функцию WSAEventSelect
не использует, поскольку она неудобна, когда нужно работать только с одним сокетом. Поэтому клиент работает в асинхронном режиме, основанном на сообщениях, т.е. посредством функции WSAAsyncSelect
.
Клиент может получать от сервера сообщения двух видов: те. которые сервер посылает в ответ на запросы клиента, и те, которые он посылает по собственной инициативе. Но различить эти сообщения клиент не может: например, если клиент отправляет запрос серверу, а потом получает от него сообщение, он не может определить, то ли это сервер ответил на его запрос, то ли именно в этот момент сервер сам отправил клиенту свое сообщение. Соответственно, сообщения обоих типов читает один и тот же код.
Примечание
В принципе, протокол мог бы быть определен таким образом, что ответы на запросы клиента и сообщения, посылаемые сервером по собственной инициативе, имели бы разный формат, по которому их можно было бы различить и читать по-разному. Но даже при этом форматы нельзя различить, пока сообщение не будет прочитано хотя бы частично, так что начало чтения будет выполняться единообразно в любом случае.
Подключение клиента к серверу выполняется точно так же, как в листинге 2.16, за исключением того, что после выполнения функции connect
сокет переводится в асинхронный режим, и его события FD_READ
и FD_CLOSE
связываются с сообщением WM_SOCKETMESSAGE
. Обработчик этого сообщения приведен в листинге 2.66.
Листинг 2.66. Получение данных клиентом
procedure TESClientForm.WMSocketMessage(var Msg: TWMSocketMessage);
const
// Размер буфера для получения данных
RecvBufSize = 4096;
var
// Буфер для получения данных
RecvBuf: array[0..RecvBufSize - 1] of Byte;
RecvRes: Integer;
P: Integer;
begin
// Защита от "тупой" ошибки
if Msg.Socket <> FSocket then
begin
MessageDlg('Внутренняя ошибка программы — неверный сокет',
mtError, [mbOK], 0);
Exit;
end;
if Msg.SockError <> 0 then
begin
MessageDlg('Ошибка при взаимодействии с сервером'#13#10 +
GetErrorString(Msg.SockError), mtError, [mbOK], 0);
OnDisconnect;
Exit;
end;
case Msg.SockEvent of
FD_READ:
// Получено сообщение от сервера
begin
// Читаем столько, сколько можем
RecvRes := recv(FSocket, RecvBuf, RecvBufSize, 0);
if RecvRes > 0 then
begin
// Увеличиваем строку на размер прочитанных данных
P := Length(FRecvStr);
SetLength(FRecvStr, P + RecvRes);
// Копируем в строку полученные данные
Move(RecvBuf, FRecvStr[Р + 1], RecvRes);
// В строке может оказаться несколько строк от сервера,
// причем последняя может прийти не целиком.
// Ищем в строке символы #0, которые, согласно протоколу,
// являются разделителями строк.
P := Pos(#0, FRecvStr));
while P > 0 do
begin
AddMessageToRecvMemo('Сообщение от сервера: ' +
Copy(FRecvStr, 1, P - 1));
// Удаляем из строкового буфера выведенную строку
Delete(FRecvStr, 1, P);
P := Pos(#0, FRecvStr);
end;
end
else if RecvRes = 0 then
begin
MessageDlg('Сервер закрыл соединение'#13#10 +
GetErrorString, mtError, [mbOK], 0);
OnDisconnect;
end
else
begin
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
MessageDlg('Ошибка при получении данных от клиента'#13#10 +
GetErrorString, mtError, [mbOK], 0);
OnDisconnect;
end;
end;
end;
FD_CLOSE: begin
MessageDlg('Сервер закрыл соединение', mtError, [mbOK], 0);
shutdown(FSocket, SD_BOTH);
OnDisconnect;
Читать дальше
Конец ознакомительного отрывка
Купить книгу