Connection.BytesLeft := Length(Connection.Msg);
end;
end
else if Res = 0 then
begin
AddMessageToLog('Клиент ' + Connection.ClientAddr +
' закрыл соединение');
RemoveConnection;
Exit;
end
else
// Как обычно, "ошибку" WSAEWOULDBLOCK просто игнорируем
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
AddMessageToLog('Ошибка при получении данных от клиента ' +
Connection.ClientAddr + ': ' + GetErrorString);
RemoveConnection;
Exit;
end;
end;
if Connection.Phase = tpSendString then
begin
// Следующий этап — отправка строки. Код примерно такой же,
// как и в предыдущем этапе, но вместо recv используется send.
// Кроме того, отсутствует проверка на Res = 0, т.к. при
// использовании TCP send никогда не возвращает 0.
Res :=
send(Connection.ClientSocket, Connection.Msg[Connection.Offset + 1],
Connection.BytesLeft, 0);
if Res > 0 then
begin
Inc(Connection.Offset, Res);
Dec(Connection.BytesLeft, Res);
// Если Connection.BytesLeft = 0, значит, строка отправлена
// полностью.
if Connection.BytesLeft = 0 then
begin
AddMessageToLog('Клиенту ' + Connection.ClientAddr +
' отправлена строка: ' + Connection.Msg);
// Очищаем строку, престо сэкономить память
Connection.Msg := '';
// Следующий этап - снова получение длины строки от клиента
Connection.Phase := tpReceiveLength;
// Получено - 0 байт
Connection.Offset := 0;
// Осталось прочитать столько, сколько занимает целое число
Connection.BytesLeft := SizeOf(Integer);
end;
end
else
if WSAGetLastError <> WSAEWOULDBLOCK then
begin
AddMessageToLog('Ошибка при отправке данных клиенту ' +
Connection.ClientAddr + ': ' + GetErrorString);
RemoveConnection;
Exit;
end;
end;
end;
В итоге мы получили сервер, достаточно устойчивый как к подключению множества клиентов, так и к нарушению протокола со стороны клиента. Для самостоятельной работы рекомендуем подумать о том, как можно сделать UDP-чат на неблокирующих сокетах. На самом деле он мало чем будет отличаться от рассмотренного чата на основе select
. Просто при использовании select
проверка возможности неблокирующего чтения из сокета проверяется предварительным вызовом этой функции, а в случае неблокирующих сокетов сначала вызывается recvfrom
, а потом проверяется, было что-то прочитано, или же операция не может быть выполнена потому, что блокировки запрещены. Во всем остальном использование select
и неблокирующих сокетов очень похоже, причем не только в данном случае, но и вообще.
2.1.17. Параметры сокета
Каждый сокет обладает рядом параметров (опций), которые влияют на его работу. Существуют параметры уровня сокета, которые относятся к сокету как к объекту безотносительно используемого протокола и его уровня. Впрочем, некоторые параметры уровня сокета применимы не ко всем протоколам. Здесь мы не будем рассматривать все параметры сокета, а ограничимся лишь изложением методов доступа к ним и познакомимся с некоторыми самыми интересными параметрами.
Для получения текущего значения параметров сокета предусмотрена функция getsockopt
, для изменения — setsockopt
. Прототипы этих функций выглядят следующим образом:
function getsockopt(s: TSocket; level, optname: Integer; optval: PChar; var optlen: Integer): Integer;
function setsockopt(s: TSocket; level, optname: Integer; optval: PChar; optlen: Integer): Integer;