Событие 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
, определенная в коде для сообщений, связанных с сокетом, может, в принципе, иметь и другие значения.
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);