Почему сокет UDP, подписанный на группу многоадресной рассылки, принимает сообщение без многоадресной рассылки?

Обзор. Я настроил сервер и клиент, где оба пытаются обнаружить друг друга с помощью UDP. Когда сервер запускается, он отправляет многоадресное сообщение (239.1.1.1) о том, что он активен. Когда клиент запускается, он отправляет многоадресное сообщение (239.1.1.2) о том, что он активен. И сервер, и клиент подписаны на многоадресные сообщения друг друга для получения своих передач. Таким образом, независимо от того, какое приложение (сервер или клиент) запускается первым, одно или другое будет уведомлено об их существовании.

На стороне клиента я делаю следующее:

  1. Настройте прослушивающий сокет для подписки и получения многоадресных сообщений от сервера.
  2. Настройте принимающий сокет для получения ответов сервера на многоадресное
    сообщение клиента согласно пункту 3 ниже.
  3. Отправьте сообщение многоадресной рассылки (чтобы сервер получил и ответил), что клиент работает.
  4. Получите ответ сервера на многоадресное сообщение клиентов, отправленное в # 3.

Вопрос: Все работает нормально, за исключением того, что оба принимающих сокета в конечном итоге получают ответ сервера (без многоадресной рассылки) клиенту. Я не понимаю, ожидаемое это поведение или нет. Могу ли я уменьшить два приемных гнезда до одного? №1 подписан на многоадресную рассылку сервера, а №2 просто прослушивает прямую передачу с сервера на тот же порт (сообщение без многоадресной рассылки с сервера). Могу ли я безопасно удалить вторую приемную розетку?

См. Исходный код ниже (я удалил обработку исключений для упрощения представления кода).

Код клиента:

// 1. Set up a socket and asynchronously listen for server startup multicasts.
Socket listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
    ProtocolType.Udp);
listenSocket.SetSocketOption(SocketOptionLevel.Socket,
    SocketOptionName.ReuseAddress, 1);
listenSocket.Bind(new IPEndPoint(IPAddress.Any, 50000));
listenSocket.SetSocketOption(SocketOptionLevel.IP,SocketOptionName.AddMembership,
    new MulticastOption(IPAddress.Parse("239.1.1.1")));
EndPoint clientEndPoint = new IPEndPoint(0, 0);
listenSocket.BeginReceiveFrom(receiveBuffer, 0, receiveBuffer.Length,
    SocketFlags.None, ref clientEndPoint,
    new AsyncCallback(OnServerMessageReceived), (object)this);

// 2. Set up socket to receive the server's response to client's multicast.
Socket receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
    ProtocolType.Udp);
receiveSocket.SetSocketOption(SocketOptionLevel.Socket,
    SocketOptionName.ReuseAddress, 1);
receiveSocket.Bind(new IPEndPoint(IPAddress.Any, 50000));
receiveSocket.ReceiveTimeout = 3000; // Timeout after 3 seconds.

// 3. Send a multicast message for server to respond to.
Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
    ProtocolType.Udp);
EndPoint multicastEndPoint = new IPEndPoint(IPAddress.Parse("239.1.1.2"), 50000);
sendSocket.SendTo(packet, packet.Length, SocketFlags.None, multicastEndPoint);

// 4. Wait for server to respond to the multicast message (timeout = 3 seconds).
byte[] receiveBuff = new byte[2048];
EndPoint serverEndPoint = new IPEndPoint(0, 0);
int bytesRead = receiveSocket.ReceiveFrom(receiveBuff, ref serverEndPoint);

Код сервера:

// Receive multicast message sent from client (in asynchronous callback method).
EndPoint clientEndPoint = new IPEndPoint(0, 0);
int bytesRead = listenSocket.EndReceiveFrom(asyncResult, ref clientEndPoint);

// Send response back to the client (change port to 50000).
EndPoint destination = new IPEndPoint((clientEndPoint as IPEndPoint).Address,
    50000);
Socket responseSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram,
    ProtocolType.Udp);
responseSocket.SendTo(response, response.Length, SocketFlags.None, destination);

person Elan    schedule 21.07.2009    source источник
comment
Почему в среде многоадресной рассылки серверу нужно знать о клиенте (ах)?   -  person MyItchyChin    schedule 21.07.2009
comment
Единственная причина, по которой я вообще использую многоадресную рассылку, - это установить обнаружение между всеми клиентами и одним сервером. После завершения обнаружения я переключаюсь на TCP / IP (WCF netTcpBinding), где сервер может отправлять сообщения клиентам. Клиенты также могут отправлять серверу уведомления об определенных событиях на стороне клиента.   -  person Elan    schedule 22.07.2009
comment
Вы упомянули другой (многоадресный) адрес клиента. Вы хотите сказать, что у клиента 1 будет один адрес, а у клиента 2 - другой?   -  person    schedule 02.02.2010
comment
Если вы используете многоадресную рассылку для обнаружения, почему бы не использовать обнаружение WCF? это кажется намного проще, и кажется, что это именно то, что вам нужно: msdn.microsoft.com/en-us/library/dd456791 (v = vs.110) .aspx   -  person nflash    schedule 03.01.2014


Ответы (4)


Ответ на ваш вопрос: «Да, это ожидаемое поведение». Вам не нужно открывать отдельный сокет для приема одноадресных пакетов на один и тот же порт.

PS

Кажется излишним, когда ваши серверы присоединяются к группе многоадресной рассылки для прослушивания новых клиентов - вы можете просто заставить серверы регулярно передавать маяк на многоадресный адрес клиента, говоря «Я здесь» (скажем, каждые 30 секунд).

person caf    schedule 29.07.2009
comment
IPAddress.Any в объекте IPEndPonint, который вы отправляете в Bind, означает - вы можете привязать этот сокет к порту 50000 на любом из имеющихся у вас сетевых интерфейсов. Если у вас есть несколько интерфейсов и вы хотите выполнить привязку к определенному интерфейсу, вы можете указать ip этого интерфейса вместо IPAddress.Any. Это не означает, что «этот сокет может получать любые UDP-пакеты, предназначенные для любого IP-адреса». - person Indy9000; 29.07.2009
comment
Благодарю за ваш ответ. 1) Я думал, что при подписке на мутадресную рассылку будут приниматься только мультивещания, отправленные на адрес, на который подписан. Подхватывается ли одноадресное сообщение, потому что целевой адрес одноадресного сообщения - это тот же компьютер, который прослушивает тот же порт? 2) У меня есть один сервер, который слушает новых клиентов. Разве повторный маяк не вызовет ненужную нагрузку на сеть? После того, как все клиенты завершат обнаружение, маяк больше не нужен. Сервер не знает, сколько существует клиентов. Однако клиенты могут продолжать попытки, пока они не подключатся к серверу. - person Elan; 29.07.2009
comment
1) Да - точно так же, как если вы установите параметры сокета для приема широковещательных рассылок, вы все равно продолжите получать одноадресные рассылки. 2) Поддержка групп многоадресной рассылки также создает нагрузку на сеть, что значительно упростит логику как клиента, так и сервера. - person caf; 29.07.2009

receiveSocket.Bind(new IPEndPoint(IPAddress.Any, 50000));

Ваши принимающие сокеты привязаны к ЛЮБОМУ адресу, что означает, что они будут получать одноадресный, широковещательный и многоадресный трафик. Вы можете привязаться к адресу интерфейса, чтобы получать только одноадресный трафик, и вы можете привязаться только к группе многоадресной рассылки, чтобы получать только многоадресный трафик.

При отправке дейтаграммы UDP вы можете указать адрес назначения, который может быть многоадресным или одноадресным. Таким образом, вы можете сократить и серверный, и клиентский код до одного сокета каждый.

person Steve-o    schedule 06.02.2010

Хотя я не уверен, что это решает вашу проблему, я ожидаю, что и клиент, и сервер будут разговаривать по одному и тому же IP-адресу многоадресной рассылки (например, 239.1.1.1). На данный момент похоже, что вы дали клиенту и серверу по одному адресу, и что произойдет, если / когда вы введете нового клиента?

person Brian Agnew    schedule 21.07.2009
comment
Я намеренно дал клиенту и серверу разные адреса многоадресной рассылки, чтобы клиент не принимал отправляемые им многоадресные сообщения (предназначенные для сервера) и наоборот. Клиент подписан на многоадресные сообщения от сервера, а сервер подписан на многоадресные сообщения от клиентов. Каждый раз, когда запускается новый клиент, он отправляет многоадресное сообщение в поисках сервера. Сервер отвечает своим IP-адресом и информацией о порте, что затем завершает процесс обнаружения и позволяет клиенту и серверу установить TCP-соединение (с использованием WCF). - person Elan; 22.07.2009

Лучшим вариантом было бы использовать протокол обнаружения служб, например Bonjour < / a> или Avahi, чем самостоятельно, поскольку они решили множество проблем. уже.

person Indy9000    schedule 29.07.2009