概述:我已经设置了一个服务器和一个客户端,两者都尝试使用 UDP 发现对方。当服务器启动时,它会发送一条多播消息 (239.1.1.1) 表明它处于活动状态。当客户端启动时,它会发送一条多播消息 (239.1.1.2) 表明它处于活动状态。服务器和客户端都订阅彼此的多播消息以接收其传输。这样,无论哪个应用程序(服务器或客户端)首先启动,都会通知其中一个应用程序的存在。
在客户端我执行以下操作:
问题: 一切工作正常,除了两个接收套接字最终都会获得服务器对客户端的(非多播)响应。我不清楚这是否是预期的行为。我可以将两个接收插座减少为一个吗? #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);
您的问题的答案是“是的,这是预期的行为”。您无需打开单独的套接字即可在同一端口上接收单播数据包。
PS
让您的服务器加入多播组来侦听新客户端似乎有些过分 - 您可以让服务器定期向客户端多播地址发送一个信标,表示“我在这里”(例如,每 30 秒一次)。
receiveSocket.Bind(new IPEndPoint(IPAddress.Any, 50000));
您的接收套接字绑定到任何地址,这意味着它们将接收单播、广播和多播流量。您可以绑定到接口地址以仅接收单播流量,也可以仅绑定到多播组以仅接收多播流量。
发送 UDP 数据报时,您可以指定目标地址,可以是多播或单播。因此,您可以将服务器和客户端代码减少到各一个套接字。
虽然我不确定它是否解决了您的问题,但我希望客户端和服务器都能在“相同”的 IP 多播地址(例如 239.1.1.1)上进行通信。目前看来您已经为客户端和服务器各分配了一个地址,如果/当您引入新客户端时会发生什么?
Bonjour 或 Avahi 这样的服务发现协议,而不是自己滚动,因为它们已经解决了很多问题。