Чтение онлайн

ЖАНРЫ

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:

 ClientSocket := accept(FServerSocket, @ClientAddr, @AddrLen);

 if ClientSocket = INVALID_SOCKET then

 begin

// Если произошедшая ошибка - WSAEWOULDBLOCK, это просто означает,

// что на данный момент подключений нет, а вообще все в порядке,

// поэтому ошибку WSAEWOULDBLOCK мы просто игнорируем. Прочие же

// ошибки могут произойти только в случае серьезных проблем,

// которые
требуют остановки сервера.

if WSAGetLastError <> WSAEWOULDBLOCK then

begin

MessageDlg('Ошибка при подключении клиента:'#13#10 +

GetErrorString + #13#10'Сервер будет остановлен', mtError, [mbOK], 0);

ClearConnections;

closesocket(FServerSocket);

OnStopServer;

end;

 end

 else

 begin

// Создаем запись для нового подключения и заполняем ее

New(NewConnection);

NewConnection.ClientSocket := ClientSocket;

NewConnection.СlientAddr :=

Format('%u.%u.%u.%u:%u', [

Ord(ClientAddr.sin_addr.S_un_b.s_b1),

Ord(ClientAddr.sin_addr.S_un_b.s_b2),

Ord(ClientAddr.sin_addr.S_un_b.s_b3),

Ord(ClientAddr.sin_addr.S_un_b.s_b4),

ntohs(ClientAddr.sin_port)]);

NewConnection.Phase := tpReceiveLength;

NewConnection.Offset := 0;

NewConnection.BytesLeft := SizeOf(Integer);

// Добавляем запись нового соединения в список

FConnections.Add(NewConnection);

AddMessageToLog('Зафиксировано подключение с адреса ' +

NewConnection.ClientAddr);

 end;

 // Обрабатываем все существующие подключения.

 // Цикл идет от конца списка к началу потому, что в ходе

 // обработки соединение может быть удалено из списка.

 for I := FConnections.Count - 1 downto 0 do processConnection(I);

end;

Обратите внимание, что сокет, созданный функцией

accept
, нигде не переводится в неблокирующий режим. Это связано с тем, что такой сокет наследует свойства слушающего сокета, поэтому он в данном случае сразу создается неблокирующим.

Собственно взаимодействие сервера с клиентом вынесено в метод

ProcessConnection
(листинг 2.33). который осуществляет чтение данных от клиента и отправку данных в соответствии с этапом, на котором остановилось взаимодействие. При реализации этого метода необходимо просто аккуратно следить за тем, куда и сколько данных нужно передать.

Листинг 2.33. Метод
ProcessConnection

//
Обработка клиента. Index задает индекс записи в списке

procedure TServerForm.ProcessConnection(Index: Integer);

var

 // Вспомогательная переменная, чтобы не приводить каждый раз

 // FConnections[Index] к PConnection

 Connection: PConnection;

 // Результат вызова recv и send

 Res: Integer;

 // Вспомогательная процедура, освобождающая ресурсы, связанные

 // с клиентом и удаляющая запись подключения из списка

 procedure RemoveConnection;

 begin

closesocket(Connection.ClientSocket);

Dispose(Connection);

FConnections.Delete(Index);

 end;

begin

 Connection := PConnection(PConnections[Index]);

 // Проверяем, на каком этапе находится взаимодействие с клиентом.

 // Используется оператор if, а не case, потому, что в случае case

 // выполняется только одна альтернатива, а в нашем случае в ходе

 // выполнения этапа он может завершиться, и взаимодействие

 // перейдет к следующему этапу. Использование if позволяет выполнить

 // все три этапа, если это возможно, а не один из них.

 if Connection.Phase = tpReceiveLength then

 begin

// Этап получения от клиента длины строки. При выполнении этого

// этапа сервер получает от клиента длину строки и размещает ее

// в поле Connection.MsgSize. Здесь приходится учитывать, что

// теоретически даже такая маленькая (4 байта) посылка может

// быть разбита на несколько пакетов, поэтому за один раз этот

// этап не будет завершен, и второй раз его придется продолжать,

// загружая оставшиеся байты. Connection.Offset — количество

// уже прочитанных на данном этапе байтов - одновременно является

// смещением, начиная с которого заполняется буфер.

Res := recv(Connection.ClientSocket, (PChar(@Connection.MsgSize) +

Connection.Offset)^, Connection.BytesLeft, 0);

if Res > 0 then

begin

// Если Res > 0, это означает, что получено Res байтов.

// Соответственно, увеличиваем на Res количество прочитанных

Поделиться с друзьями: