Читаем О чём не пишут в книгах по Delphi полностью

Событие FD_CLOSE возникает только для сокетов, поддерживающих соединение, при разрыве такого соединения нормальным образом или в результате ошибки связи. Если удаленная сторона дня завершения соединения использует функцию shutdown, то FD_CLOSE возникает после вызова этой функции с параметром SD_SEND. При этом соединение закрыто еще не полностью, удаленная сторона еще может получать данные, поэтому при обработке FD_CLOSE можно попытаться отправить те данные, которые в этом нуждаются. Однако гарантии, что вызов функции отправки не завершится неудачей, нет, т.к. удаленная сторона может закрывать сокет сразу, не прибегая к shutdown.

Рекомендуемая последовательность действий при завершении связи такова. Сначала клиент завершает отправку данных через сокет, вызывая функцию shutdown с параметром SD_SEND. Сервер при этом получает событие FD_CLOSE. Сервер отсылает данные клиенту (при этом клиент получает одно или несколько событий FD_READ), а затем также завершает отправку данных с помощью shutdown с параметром SD_SEND. Клиент при этом получает событие FD_CLOSE, в ответ на которое закрывает сокет с помощью closesocket. Сервер, в свою очередь, сразу после вызова shutdown также вызывает closesocket. В листинге 2.49 приведен пример кода сервера, использующего асинхронные сокеты. Сервер работает в режиме запрос-ответ, т.е. посылает какие-то данные клиенту только в ответ на его запросы. Константа WM_SOCKETEVENT, определенная в коде для сообщений, связанных с сокетом, может, в принципе, иметь и другие значения.

Листинг 2.49. Пример простого сервера на асинхронных сокетах

unit Unit1;


interface


uses

 Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, WinSock;


const

 WM_SOCKETEVENT = WM_USER + 1;


type

 TForm1 = class(TForm)

  procedure FormCreate(Sender: TObject);

  procedure FormDestroy(Sender: TObjеct);

 private

  ServSock: TSocket;

  procedure WMSocketEvent(var Msg: TMessage); message WM_SOCKETEVENT;

 end;


var

 Form1: TForm1;


implementation


{$R *.DFM}


procedure TForm1.FormCreate(Sender: TObject);

var

 Data: TWSAData;

 Addr: TSockAddr;

begin

 WSAStartup($101, Data);

 // Обычная последовательность действий по созданию сокета,

 // привязке его к адресу и установлению на прослушивание

 ServSock := socket(AF_INET, SOCK_STREAM, 0);

 Addr.sin_family := AF_INET;

 Addr.sin_addr.S_addr := INADDR_ANY;

 Addr.sin_port := htons(3320);

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

 bind(ServSock, Addr, SizeOf(Addr));

 listen(ServSock, SOMAXCONN);

 // Перевод сокета в асинхронный режим. Кроме события FD_ACCEPT

 // указаны также события FD_READ и FD_CLOSE, которые никогда не

 // возникают на сокете, установленном в режим прослушивания.

 // Это сделано потому, что сокеты, созданные с помощью функции

 // accept, наследуют асинхронный режим, установленный для

 // слушающего сокета. Таким образом, не придется вызывать

 // функцию WSAAsyncSelect для этих сокетов - для них сразу

 // будет назначен обработчик событий FD_READ и FD_CLOSE.

 WSAAsyncSelect(ServSock, Handle, WM_SOCKETEVENT, FD_READ or FD_ACCEPT or FD_CLOSE);

end;


procedure TForm1.FormDestroy(Sender: TObject);

begin

 closesocket(ServSock);

 WSACleanup;

end;


procedure TForm1.WMSocketEvent(var Msg: TMessage);

var

 Sock: TSocket;

 SockError: Integer;

begin

 Sock := TSocket(Msg.WParam);

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже