Если размер буфера, определяемого параметром Buf
, меньше, чем первая находящаяся во входном буфере сокета дейтаграмма, то копируется только часть дейтаграммы, помещающаяся в буфере, a recvfrom
завершается с ошибкой ( WSAGetLastError
при этом вернет ошибку WSAEMSGSSIZE
). Оставшаяся часть дейтаграммы при этом безвозвратно теряется, при следующем вызове recvfrom
будет прочитана следующая дейтаграмма. Этой проблемы легко избежать, т.к. длина дейтаграммы в UDP не может превышать 65 507 байтов. Достаточно подготовить буфер соответствующей длины, и и в него гарантированно поместится любая дейтаграмма.
Другой способ избежать подобной проблемы — использовать флаг MSG_PEEK
. В этом случае дейтаграмма не удаляется из входного буфера сокета, а значение, возвращаемое функцией recvfrom
, равно длине дейтаграммы. При этом в буфер, заданный параметром Buf
, копируется та часть дейтаграммы, которая в нем помещается. Программа может действовать следующим образом: вызвать recvfrom
с флагом MSG_PEEK
, выделить память, требуемую для хранения дейтаграммы, вызвать recvfrom
без флага MSG_PEEK
, чтобы прочитать дейтаграмму целиком и удалить ее из входного буфера сокета. Этот метод сложнее, а 65 507 байтов — не очень большая по нынешним меркам память, поэтому легче все-таки заранее приготовить буфер фиксированной длины. Функция recvfrom
непригодна для тех сокетов, которые еще не привязаны к адресу, поэтому перед вызовом этой функции должна быть вызвана либо функция bind
, либо функция, которая осуществляет неявную привязку сокета к адресу (например, sendto
).
Протокол UDP не поддерживает соединения в том смысле, в котором их поддерживает TCP, но библиотека сокетов позволяет частично имитировать такие соединения, Для этого служит функция connect
, имеющая следующий прототип:
function connect(s: TSocket; var name: TSockAddr; namelen: Integer): Integer;
Параметр s
задает сокет, который должен быть "соединен" с удаленным адресом. Адрес задается параметром name аналогично тому, как он задаётся в параметре addr
функции sendto
. Параметр namelen
содержит длину структуры, описывающей адрес, и должен быть равен SizeOf(TSockAddr)
. Функция возвращает ноль при успешном завершении и SOCKET_ERROR
— в случае ошибки. Вызов функции connect
в случае UDP устанавливает фильтр для входящих дейтаграмм. Дейтаграммы, адрес отправителя которых не совпадает с адресом, заданным в функции connect
, игнорируются: новые дейтаграммы не помещаются во входной буфер сокета, а те, которые находились там на момент вызова connect
, удаляются из него. Функция connect
не проверяет, существует ли адрес, с которым сокет "соединяется", и может успешно завершиться, даже если узла с таким IP-адресом нет.
Программа может вызывать connect неограниченное число раз с разными адресами. Если параметр name задает IP-адрес INADDR_ANY
и нулевой порт, то сокет "отсоединяется", т.е. все фильтры для него снимаются, и он ведет себя так же, как сокет, для которого не была вызвана функция connect
. Для сокетов, не привязанных к адресу, connect
неявно вызывает bind
.
После вызова connect
для отправки данных можно использовать функцию send
со следующим прототипом:
function send(s: TSocket; var Buf; len, flags: Integer): Integer;
От функции sendto
она отличается отсутствием параметров addrto
и tolen
. При использовании send
дейтаграмма отправляется по адресу, заданному при вызове connect
. В остальном эти функции ведут себя одинаково, функция sendto
при работе с "соединенным" сокетом ведет себя так же, как с несоединенным, т.е. отправляет дейтаграмму по адресу, определяемому параметром addrlen
, а не по адресу, заданному при вызове connect
.
Получение данных через "соединенные" сокеты может также осуществляться с помощью функции reсv
, имеющей следующий прототип:
function recv(s: TSocket; var Buf; len, flags: Integer): Integer;
От своего аналога recvfrom
она отличается только отсутствием параметров from
и fromlen
, через которые передается адрес отправителя дейтаграммы.
Рис. 2.1.Последовательность действий программы при обмене данными с помощью UDP
Строго говоря, функцию recv
можно использовать и для несоединенных сокетов, но при этом программе остается неизвестным адрес отправителя. В случае же "соединенных" сокетов адрес отправителя заранее известен — это адрес, заданный в функции connect
, а дейтаграммы всех других отправителей будут отбрасываться. Функция recvfrom
также пригодна для "соединенных" сокетов, но адрес отправителя, который она возвращает, в данном случае может быть только тот, который определен в функции connect
.
Читать дальше
Конец ознакомительного отрывка
Купить книгу