Затем выполняется собственно добавление сокета в группу. В WinSock 12 для этого потребуется функция
setsockopt
с параметром
IP_ADD_MEMBERSHIP
, в качестве уровня следует указать
IPPROTO_IP
. При этом через параметр
optval
передается указатель на запись
ip_mreq
, описанную так, как показано в листинге 2.79.
Листинг 2.79. Тип
TIPMreq
// ***** Описание на C++ *****
struct ip_mreq {
struct in_addr imr_multiaddr;
struct in_addr imr_interface;
}
// *****
Описание на Delphi *****
TIPMreq = packed record
IMR_MultiAddr: TSockAddr;
IMR_Interface: TSockAddr
end;
Поле
IMR_MultiAddr
задает групповой адрес, к которому присоединяется сокет. У этой структуры должны быть заполнены поля
sin_family
(значением
AF_INET
) и
sin_addr
. Номер порта здесь указывать не нужно, значение этого поля игнорируется. Поле
IMR_Interface
определяет адрес сетевого интерфейса, через который будет вестись прием многоадресной рассылки. Если программу устраивает интерфейс, выбираемый системой по умолчанию, значение поля
IMR_Interface.sin_addr
должно быть
INADDR_ANY
(на компьютерах с одним сетевым интерфейсом обычно используется именно это значение). Но если у компьютера несколько сетевых интерфейсов, которые связывают его с разными сетями, интерфейс для получения групповых пакетов, выбираемый системой по умолчанию, может быть связан не с той сетью, из которой они реально ожидаются. В этом случае программа может явно указать IP-адрес того интерфейса, через который данный сокет должен принимать групповые пакеты. Как и в поле
IMR_MultiAddr
, в поле
IMR_Interface
задействованы только поля
sin_familу
и
sin_addr
, а остальные поля игнорируются.
Для прекращения членства сокета в группе служит та же функция
setsockopt
, но с параметром
IP_DROP_MEMBERSHIP
. Через параметр
optval
при этом также передается структура
ip_mreq
, значимые поля которой должны быть заполнены так же, как и при добавлении данного сокета в данную группу. Несмотря на то, что структура
ip_mreq
относится к WinSock 1, в модуле WinSock ее описание отсутствует. Константы
IP_ADD_MEMBERSHIP
и
IP_DROP_MEMBERSHIP
в этом модуле объявлены, но работать с ними следует с осторожностью, потому что они должны иметь разные значения в WinSock 1 и WinSock 2. В WinSock 1 они должны иметь значения 5 и 6 соответственно, а в WinSock 2 — 12 и 13. Из-за этого нужно внимательно следить, чтобы значения соответствовали той библиотеке, из которой импортируется функция setsockopt: 5 и 6 — для WSock32.dll и 12 и 13 — для WS2_32.dll.
В 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, т.е. могут распространяться только в пределах непосредственно примыкающих подсетей. Целочисленный параметр сокета