// Читаем остаток строки
Flags := 0;
if WSARecv(Connection.ClientSocket, @Buf, 1, NumBytes, Flags,
@Connection.Overlapped, ReadMsgCompleted) = SOCKET_ERROR then
begin
if WSAGetLastError <> WSA_IO_PENDING then
begin
ServerForm.AddMessageToLog('Клиент ' + Connection.ClientAddr +
' - ошибка при чтении строки: ' + GetErrorString);
ServerForm.RemoveConnection(Connection);
end;
end;
end;
end;
// Функция SendMsgCompleted используется в качестве функции завершения
// для перекрытой отправки строки.
// Во многом она аналогична функции ReadLenCompleted
procedure SendMsgCompleted(dwError: DWORD; cdTransferred: DWORD; lpOverlapped: PWSAOverlapped; dwFlags: DWORD); stdcall;
var
Connection: PConnection;
Buf: TWSABuf;
NumBytes, Flags: DWORD;
begin
Connection := ServerForm.GetConnectionByOverlapped(lpOverlapped);
if Connection = nil then
begin
ServerForm.AddMessageToLog(
'Внутренняя ошибка программы - не найдено соединение');
Exit;
end;
if dwError <> 0 then
begin
ServerForm.AddMessageToLog('Клиент ' + Connection.ClientAddr +
' - ошибка при отправке строки: ' + GetErrorString(dwError));
ServerForm.RemoveConnection(Connection);
Exit;
end;
Dec(Connection.BytesLeft, cdTransferred);
if Connection.BytesLeft < 0 then
begin
ServerForm.AddMessageToLog('Клиент ' + Connection.ClientAddr +
' — внутренняя ошибка программы: отправлено больше байтов, ' +
'чем ожидалось');
ServerForm.RemoveConnection(Connection);
end
else if Connection.BytesLeft = 0 then
begin
// Строка отправлена целиком. Выводим сообщение об этом.
ServerForm.AddMessageToLog('Клиенту ' + Connection.ClientAddr +
' отправлена строка: ' + Connection.Msg);
// Очищаем строку, чтобы зря не занимала память
Connection.Msg := '';
// Теперь будем снова читать длину строки
Connection.Offset := 0;
Connection.BytesLeft := SizeOf(Integer);
// Читать будем в Connection.MsgSize
Buf.Len := Connection.BytesLeft;
Buf.Buf := @Connection.MsgSize;
Flags := 0;
if WSARecv(Connection.ClientSocket, @Buf, 1, NumBytes, Flags,
@Connection.Overlapped, ReadLenCompleted) = SOCKET_ERROR then
begin
if WSAGetLastError <> WSA_IO_PENDING then
begin
ServerForm.AddMessageToLog('Клиент ' + Connection.ClientAddr +
' - ошибка при чтении длины строки: ' + GetErrorString);
ServerForm.RemoveConnection(Connection);
end;
end;
end
else
begin
// Строка отправлена не полностью
Inc(Connection.Offset, cdTransferred);
// Формируем буфер из остатка строки
Buf.Len := Connection.BytesLeft;
Buf.Buf := PChar(Connection.Msg) + Connection.Offset;
if WSASend(Connection.ClientSocket, @Buf, 1, NumBytes, 0,
@Connection.Overlapped, SendMsgCompleted) = SOCKET_ERROR then
begin
if WSAGetLastError <> WSA_IO_PENDING then
begin
ServerForm.AddMessageToLog('Клиент ' + Connection.СlientAddr +
' - ошибка при отправке строки: ' + GetErrorString);
ServerForm.RemoveConnection(Connection);
end;
end;
end;
end;
Чтобы это все заработало, остался последний штрих: нить нужно время от времени переводить в состояние ожидания. Мы будем это делать, вызывая SleepEx
с нулевым тайм-аутом по сигналам от таймера. В получившемся сервере трудно увидеть все преимущества перекрытого ввода-вывода. Это и неудивительно, потому что его главное достоинство — высокая производительность при большом количестве подключений. Перекрытый ввод-вывод ориентирован на создание серверов, интенсивно взаимодействующих с многими клиентами, а на таком маленьком сервере, как OverlappedServer
, он выглядит несколько тяжеловесно, хотя и позволяет получить вполне работоспособный вариант.
2.2.11. Многоадресная рассылка
При описании стека протоколов TCP/IP мы упоминали протокол IGMP - дополнение к протоколу IP, позволяющее назначать нескольким узлам групповые адреса. С помощью этого протокола можно группе сокетов назначить один IP-адрес, и тогда все пакеты, отправленные на этот адрес, будут получать все сокеты, входящие в группу. Заметим, что не следует путать группы сокетов в терминах IGMP, и группы сокетов в терминах WinSock (поддержка групп сокетов в WinSock пока отсутствует, существуют только зарезервированные для этого параметры в некоторых функциях).
Мы уже говорили, что сетевая карта получает все IP-пакеты, которые проходят через ее подсеть, но выбирает из них только те, которые соответствуют назначенному ей MAC- и IP-адресу. Существуют два режима работы сетевых карт. В первом выборка пакетов осуществляется аппаратными средствами карты, во втором — программными средствами драйвера. Аппаратная выборка осуществляется быстрее и не загружает центральный процессор, но ее возможности ограничены. В частности, у некоторых старых карт отсутствует аппаратная поддержка IGMP, поэтому они не могут получать пакеты, отправленные на групповой адрес, без переключения в режим программной выборки. Более современные сетевые карты способны запоминать несколько (обычно 16 или 32) групповых адресов, и, пока количество групповых адресов не превышает этот предел, могут осуществлять аппаратную выборку пакетов с учетом групповых адресов.
Читать дальше
Конец ознакомительного отрывка
Купить книгу