// Следующий этап - снова получение длины строки от клиента
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;
Параметры у функций почти одинаковы. Первый задает сокет, параметры которого следует узнать или изменить. Второй указывает, параметр какого уровня следует узнать или изменить. Третий задает сам параметр сокета. Параметр
optval
содержит указатель на буфер, в котором хранится значение параметра, a
optlen
— размер этого буфера (разные параметры имеют различные типы и поэтому размер буфера может быть разным). Функция
getsockopt
сохраняет значение параметра в буфере, заданном указателем
optval
. Длина буфера передается через параметр
optlen
, и через него же возвращается размер, реально понадобившийся для хранения параметра. У функции
setsockopt
параметр
optval
содержит указатель на буфер, хранящий новое значение параметра сокета, a
optlen
— размер этого буфера.
Чаще всего параметры сокета имеют целый или логический тип. В обоих случаях параметр
optval
должен содержать указатель на значение типа
Integer
. Для логического типа любое ненулевое значение интерпретируется
True
, нулевое — как
False
. Два достаточно важных параметра сокета — размеры входного и выходного буфера. Это параметры уровня сокета (
SOL_SOCKET
), их номера задаются константами
SO_RCVBUF
и
SO_SNDBUF
. Например, чтобы получить размер входного буфера сокета, нужно выполнить код листинга 2.34.
После выполнения этого кода размер буфера будет содержаться в переменной
Val
.
Немного поэкспериментировав, можно обнаружить, что размер входного и выходного буфера равен 8192 байтам как для TCP, так и для UDP. Тем не менее это не мешает отправлять и получать дейтаграммы большего размера (для UDP), а также накапливать в буфере больший объем информации (для TCP). При получении данных это достигается за счет использования более низкоуровневых буферов, чем буфер самого сокета. Можно даже установить входной буфер сокета равным нулю — тогда все поступившие данные будут храниться в низкоуровневых буферах. Однако делать так не рекомендуется, т.к. при этом снижается производительность.