В следующий раз событие может возникнуть только в том случае, если функция send
(или sendto
) не смогла положить данные в буфер из-за нехватки места в нем (в этом случае функция вернет значение, меньшее, чем размер переданных данных, или завершится с ошибкой WSAEWOULBBLOCK
). Как только в выходном буфере сокета снова появится свободное место, возникнет событие FD_WRITE
, показывающее, что программа может продолжить отправку данных. Если же программа отправляет данные не очень большими порциями и относительно редко, не переполняя буфер, то второй раз событие FD_WRITE
не возникнет никогда.
Событие FD_ACCEPT
во многом похоже на FD_READ
, за исключением того, что оно возникает не при получении данных, а при подключении клиента. После постановки сообщения о событии FD_ACCEPT
в очередь новые сообщения о FD_ACCEPT
для данного сокета в очередь не ставятся, пока не будет вызвана функция accept
или WSAAccept
. При вызове одной из этих функций сообщение о событии вновь помещается в очередь окна, если в очереди подключений после вызова функции остаются подключения.
Событие FD_CONNECT
возникает при установлении соединения для сокетов, поддерживающих соединение. Для клиентских сокетов оно возникает после завершения процедуры установления связи, начатой с помощью функции connect
, для серверных — после создания нового сокета с помощью функции accept
(событие возникает именно на новом сокете, а не на том, который находится в режиме ожидания подключения). В MSDN написано, что оно должно возникать также и после выполнения connect
для сокетов, не поддерживающих соединение, однако для UDP практика это не подтверждает. Событие FD_CONNECT
также возникает, если при попытке установить соединение произошла ошибка (например, оказался недоступен указанный сетевой адрес). Поэтому при получении этого события необходимо анализировать старшее слово параметра lParam
, чтобы понять, удалось ли установить соединение.
Событие 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);
Читать дальше
Конец ознакомительного отрывка
Купить книгу