TTimeVal = record
tv_sec: LongInt;
tv_usec: LongInt;
end;
Эта структура служит для задания времени ожидания. Поле tv_sec
tv_usec
— число микросекунд. Так, чтобы задать интервал ожидания, равный 1,5 с, нужно присвоить полю tv_sec
значение 1, а полю tv_usec
— значение 500 000. Параметр timeout
функции select
должен содержать указатель на заполненную подобным образом структуру, определяющую, сколько времени функция будет ожидать, пока хотя бы один из сокетов не будет готов к требуемой операции. Если этот указатель равен nil
, ожидание будет бесконечным.Мы потратили достаточно много времени, выясняя структуру параметров функции select
Функция select
readfds
, готовность означает, что функции recv
или recvfrom
будут выполнены без блокирования. В случае UDP это означает, что во входном буфере сокета есть данные, которые можно прочитать. При использовании TCP функции recv
и recvfrom
могут быть выполнены без задержки еще в двух случаях: когда партнер закрыл соединение (в этом случае функции вернут 0), а также когда соединение некорректно разорвано (в этом случае функции вернут SOCKET_ERROR
). Кроме того, если сокет, включенный в множество readfds
, находится в состоянии ожидания соединения (в которое он переведен с помощью функции listen
), то для него состояние готовности означает, что очередь соединений не пуста и функция accept
будет выполнена без задержек.Для сокетов, входящих в множество writefds
select
, чтобы понять, установлено ли соединение.) Наличие свободного места в буфере не гарантирует того, что функции send
или sendto
не будут блокировать вызвавшую их нить, т.к. программа может попытаться передать больший объем информации, чем размер свободного места в буфере на момент вызова функции. В этом случае функции send
и sendto
вернут управление вызвавшей их нити только после того, как часть данных будет отправлена, и в буфере сокета освободится достаточно места.Следует отметить, что большинство протоколов обмена устроено таким образом, что при их реализации проблема переполнения выходного буфера практически никогда не возникает. Чаще всего клиент и сервер обмениваются небольшими пакетами, причем сервер посылает клиенту только ответы на его запросы, а клиент не посылает новый запрос до тех пор. пока не получит ответ на предыдущий. В этом случае гарантируется, что пакеты будут уходить о выходного буфера быстрее (или, по крайней мере, не медленнее), чем программа будет их туда помещать. Поэтому заботиться о том, чтобы в выходном буфере было место, приходится достаточно редко.
И наконец, последнее множество exceptfds
Функция select
readfds
, writefds
и exceptfds
модифицируются функцией: в них остаются только те сокеты, которые находятся в состоянии готовности. При вызове функции любые два из этих трех указателей могут быть равны nil
, если программу не интересует готовность сокетов по соответствующим критериям. Один и тот же сокет может входить в несколько множеств.В листинге 2.23 приведен пример кода TCP-сервера, взаимодействующего с несколькими клиентами в рамках одной нити и работающего по простой схеме "запрос-ответ".
select
var
Sockets: array of TSocket;
Addr: TSockAddr;
Data: TWSAData;
Len, I, J: Integer;
FDSet: TFDSet;
begin
WSAStartup($101, Data);
SetLength(Sockets, 1);
Sockets[0] := socket(AF_INET, SOCK_STREAM, 0);
Addr.sin_family := AF_INET;