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 количество прочитанных
// на данном этапе байтов и на такую же величину уменьшаем
// количество оставшихся.
Inc(Connection.Offset, Res);
Dec(Connection.BytesLeft, Res);
// Если количество оставшихся байтов равно нулю, можно переходить
// к следующему этапу.
if Connection.BytesLeft = 0 then
begin
// Проверяем корректность принятой длины строки
if Connection.MsgSize <= 0 then
begin
AddMessageToLog('Неверная длина строки от клиента ' +
Connection.ClientAddr + ': ' + IntToStr(Connection.MsgSize));
RemoveConnection;
Exit;
end;
// Следующий этап - это чтение самой строки
Connection.Phase := tpReceiveString;
// Пока на этом этапе не прочитано ни одного байта
Connection.Offset := 0;
// Осталось прочитать Connection.MsgSize байтов
Connection.BytesLeft := Connection.MsgSize;
// Сразу выделяем память под строку