В WinSock 2 для присоединения сокета к группе объявлена функция
WSAJoinLeaf
, прототип которой приведен в листинге 2.80.
Листинг 2.80. Функция
WSAJoinLeaf
// ***** описание на C++ *****
SOCKET WSAJoinLeaf(SOCKET s, const struct sockaddr FAR *name, int namelen, LPWSABUF lpCallerData, LPWSABUF lpCalleeData, LPQOS lpSQOS, LPQOS lpGQOS, DWORD dwFlags);
// ***** описание на Delphi *****
function WSAJoinLeaf(S: TSocket; var Name: TSockAddr; NameLen: Integer; lpCallerData, lpCalleeData: PWSABuf; lpSQOS, lpGQOS: PQOS; dwFlags: DWORD): TSocket;
Параметры
lpCallerData
и
lpCalleeData
задают буферы, в которые помещаются данные, передаваемые и получаемые при присоединении к группе. Протокол IP не поддерживает передачу таких данных, поэтому при его использовании эти параметры должны быть равны
nil
. Параметры
lpSQOS
и
lpGQOS
относятся к качеству обслуживания, которое мы здесь не рассматриваем, поэтому их мы тоже полагаем равными
nil
.
Параметр
S
определяет сокет, который присоединяется к группе,
Name
— адрес группы,
NameLen
— размер буфера с адресом. Параметр
dfFlags
определяет, будет ли сокет служить для отправки данных (
JL_SENDER_ONLY
, 1), для получения данных (
JL_RECEIVER_ONLY
, 2) или и для отправки, и для получения (
JL_BOTH
, 4).
Функция возвращает сокет, который создан для взаимодействия с группой. В протоколах типа ATM подключение к группе похоже на установление связи в TCP, и функция
WSAJoinLeaf
, подобно функции
accept
, создаёт новый сокет, подключенный к группе. В случае UDP новый сокет не создается, и функция
WSAJoinLeaf
возвращает значение переданного ей параметра
S
.
Номер порта в параметре
Name
игнорируется. Для получения групповых сообщений используется тот интерфейс, который система назначает для этого по умолчанию.
Чтобы прекратить членство сокета в группе, в которую он был добавлен с помощью
WSAJoinLeaf
, нужно закрыть его посредством функции
closesocket
. Если сокет, для которого вызывается функция
WSAJoinLeaf
, находится в асинхронном режиме, то при успешном присоединении сокета к группе возникнет событие
FD_CONNECT
(в [3] написано, что в одноранговых плоскостях управления
FD_CONNECT
не возникает — это не соответствует действительности). Но в случае ненадежного протокола UDP возникновение этого события говорит лишь о том, что было отправлено IGMP-сообщение, извещающее о включении сокета в группу (это сообщение должны получить все маршрутизаторы сети, чтобы потом правильно передавать групповые сообщения в другие подсети). Однако
FD_CONNECT
не гарантирует, что это сообщение успешно принято всеми маршрутизаторами.
UDP-сокет, присоединившийся к многоадресной группе, не должен "подключаться" к какому-либо адресу с помощью функции
connect
или
WSAConnect
. Соответственно, для отправки данных такой сокет может использовать только
sendto
и
WSASendTo
. Сокет, присоединившийся к группе, может отправлять данные на любой адрес, но если используется поддержка качества обслуживания, она работает только при отправке данных на групповой адрес сокета. Отправка данных на групповой адрес не требует присоединения к группе, причем для сокета, отправляющего данные, нет никакой разницы между отправкой данных на обычный адрес и на групповой. И в том и в другом случае используется функция
sendto
или
WSASendto
(или
sendWSASend
с предварительным вызовом
connect
). Никаких дополнительных действий для отправки данных на групповой адрес выполнять не требуется. Порт при этом также указывается. Как мы уже видели, номер порта при добавлении сокета в группу не указывается, но сам сокет перед этим должен быть привязан к какому-либо порту. При отправке группового сообщения его получат только те сокеты, входящие в группу, чей порт привязки совпадает с портом, указанным в адресе назначения сообщения.
Если сокет, отправляющий сообщение на групповой адрес, сам является членом этой группы, он, в зависимости от настроек, может получать или не получать свое сообщение. Это определяется его параметром
IP_MULTICAST_LOOP
,
имеющим тип
BOOL
. По умолчанию этот параметр равен
True
— это значит, что сокет будет получать свои собственные сообщения. С помощью функции
setsockopt
можно изменить значение этого параметра на
False
, и тогда сокет не будет принимать свои сообщения.
Параметром
IP_MULTICAST_LOOP
следует пользоваться осторожно, т.к. он не поддерживается в Windows NT 4 и требует Windows 2000 или выше. В Windows 9x/МЕ он тоже не поддерживается (хотя упоминания об этом в MSDN нет).
В разд. 2.1.4 мы говорили, что каждый IP-пакет в своем заголовке имеет целочисленный параметр TTL (Time То Live). Его значение определяет, сколько маршрутизаторов может пройти данный пакет. По умолчанию групповые пакеты имеют TTL, равный 1, т.е. могут распространяться только в пределах непосредственно примыкающих подсетей. Целочисленный параметр сокета
IP_MULTICAST_TTL
позволяет программе изменить это значение.
У функции
WSAJoinLeaf
не предусмотрены параметры для задания адреса сетевого интерфейса, через который следует получать групповые сообщения, поэтому всегда используется интерфейс, выбираемый системой для этих целей по умолчанию. Выбрать интерфейс, который система будет назначать по умолчанию, можно с помощью параметра сокета
IP_MULTICAST_IF
. Этот параметр имеет тип
TSockAddr
, причем значимыми полями структуры в данном случае являются
sin_family
и
sin_addr
, а значение поля
sin_port
игнорируется.
Значения констант
IP_MULTICAST_IF
,
IP_MULTICAST_TTL
и
IP_MULTICAST_LOOP
также зависят от версии WinSock. В WinSock 1 они должны быть равны 2, и 4, а в WinSock 2 — 9, 10 и 11 соответственно.
2.2.12. Дополнительные функции
В этом разделе мы рассмотрим некоторые функции, относящиеся в WinSock к дополнительным. В WinSock 1 эти функции вместе со всеми остальными экспортируются библиотекой WSock32.dll, а в WinSock 2 они вынесены в отдельную библиотеку MSWSock.dll (в эту же библиотеку вынесены некоторые устаревшие функции типа
EnumProtocols
).
Начнем мы знакомство с этими функциями с функции
WSARecvEx
(которая, кстати, является расширенной версией функции
recv
, а отнюдь не
WSARecv
, как это можно заключить из ее названия), имеющей следующий прототип:
function WSARecvEx(s: TSocket; var buf; len: Integer; var flags: Integer): Integer;
Видно, что она отличается от обычной функции
recv
только тем, что флаги передаются через параметр-переменную вместо значения. В функции
WSARecvEx
этот параметр не только входной, но и выходной; функция может модифицировать его. Ранее мы познакомились с функцией
WSARecv
, которая также может модифицировать переданные ей флаги, но условия, при которых эти две функции модифицируют флаги, различаются.
При использовании TCP (а также любого другого потокового протокола) флаги не изменяются функцией, и результат работы
WSARecvEx
эквивалентен результату работы
recv
.
Как мы уже не раз говорили, дейтаграмма UDP должна быть прочитана из буфера сокета целиком. Если в буфере, переданном функции
recv
или
recvfrom
, недостаточно места для получения дейтаграммы, эти функции завершаются с ошибкой. При этом в буфер помещается та часть дейтаграммы, которая может в нем поместиться, а оставшаяся часть дейтаграммы теряется. Функция
WSARecvEx
отличается от
recv
только тем, что в случае, когда размер буфера меньше размера дейтаграммы, она завершается без ошибки (возвращая при этом размер прочитанной части дейтаграммы, т.е. размер буфера) и добавляет флаг
MSG_PARTIAL
к параметру
flags
. Остаток дейтаграммы при этом также теряется. Таким образом,
WSARecvEx
дает альтернативный способ проверки того, что дейтаграмма не поместилась в буфер, и в некоторых случаях этот способ может оказаться удобным.