Почему UDP-сокет, подписанный на группу многоадресной рассылки, принимает не-групповое сообщение? - PullRequest
0 голосов
/ 21 июля 2009

Обзор: Я настроил сервер и клиент, где оба пытаются обнаружить друг друга с помощью 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);

Ответы [ 4 ]

3 голосов
/ 29 июля 2009

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

PS

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

2 голосов
/ 06 февраля 2010
receiveSocket.Bind(new IPEndPoint(IPAddress.Any, 50000));

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

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

0 голосов
/ 29 июля 2009

Лучшим вариантом было бы использовать протокол обнаружения службы, такой как Добрый день или Авахи , чем накатить свой собственный, поскольку они решили уже много проблем.

0 голосов
/ 21 июля 2009

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

...