KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » А. Григорьев - О чём не пишут в книгах по Delphi

А. Григорьев - О чём не пишут в книгах по Delphi

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн А. Григорьев, "О чём не пишут в книгах по Delphi" бесплатно, без регистрации.
Перейти на страницу:

 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 :=

   AnsiUpperCase(StringReplace(Connection.Msg, #0,

   '#0', [rfReplaceAll])) + ' (Overlapped server)'#0;

  // Смещение - ноль, осталось отправить полную длину

  Connection.Offset := 0;

  Connection.BytesLeft := Length(Connection.Msg);

  // Формируем буфер из строки Connection.Msg

  Buf.Len := Connection.BytesLeft;

  Buf.Buf := Point(Connection.Msg);

  // Отправляем строку

  if WSASend(Connection.ClientSocket, @Buf, 1, NumBytes, 0,

   @Connection.Overlapped, SendMsgCompleted) = SOCKET_ERROR then

  begin

   it WSAGetLastError <> WSA_IO_PENDING then

   begin

    ServerForm.AddMessageToLog('Клиент ' + Connection.ClientAddr +

     ' - ошибка при отправке строки: ' + GetErrorString);

    ServerForm.RemoveConnection(Connection);

   end;

  end;

 end

 else

 begin

  // Connection.BytesLeft < 0 - строка прочитана частично

  Inc(Connection.Offset, cdTransferred);

  // Формируем буфер из непрочитанного остатка строки

  Buf.Len := Connection.BytesLeft;

  Buf.Buf := PChar(Connection.Msg) + Connection.Offset;

  // Читаем остаток строки

  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) групповых адресов, и, пока количество групповых адресов не превышает этот предел, могут осуществлять аппаратную выборку пакетов с учетом групповых адресов.

Windows 95 и NT 4 используют сетевые карты в режиме программной выборки пакетов. Windows 98 и 2000 и выше по умолчанию устанавливают сетевые карты в режим аппаратной выборки пакетов. При этом Windows 2000 может переключать карту в режим программной выборки, если число групповых адресов, с которых компьютер должен принимать пакеты, превышает ее аппаратные возможности. Windows 98 такой возможностью не обладает, поэтому программа, выполняемая в этой среде, может столкнуться с ситуацией, когда сокет не сможет присоединиться к групповому адресу из-за нехватки аппаратных ресурсов сетевой карты (программа при этом получит ошибку WSAENOBUFS).

WinSock предоставляет достаточно широкие возможности по управлению многоадресной рассылкой, но для их использования необходимо, чтобы выбранный сетевой протокол поддерживал все эти возможности. Поддержка многоадресной рассылки протоколом IP достаточно скудна по сравнению, например, с протоколами, применяющимися в сетях ATM. Здесь мы будем рассматривать только те возможности WinSock по поддержке многоадресной рассылки, которые совместимы с протоколом IP.

Протокол TCP не поддерживает многоадресную рассылку, поэтому все, что далее будет сказано, относится только к протоколу UDP. Отметим также, что при многоадресной рассылке через границы подсетей маршрутизаторы должны поддерживать передачу многоадресных пакетов. Глава "Многоадресная рассылка" в [3], к сожалению, содержит множество неточностей. Далее мы будем обращать внимание на эти неточности, чтобы облегчить чтение этой книги.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*