重用 UdpClient 与处置它
Reuse UdpClient vs Disposing it
我正在创建一个用户位于路由器 (NAT) 后面的聊天应用程序。所以主要问题是向这些客户发送消息。服务器接收消息没有问题
这是我的协议(规则):
UdpServer
侦听来自 client A
的 udp 数据包
Server
从 client A
接收数据包并回复数据包以打开 NAT
Server
现在成为客户端(服务器不断向 client A
发送数据包,客户端回复服务器通知它已收到消息)现在 NAT 已打开,我将客户端和服务器反转。这是因为client A
总能通过tcp向服务器发送消息
如果 Server
发现 client A
没有回复它不断发送的数据包,它会将 client A
标记为断开连接并停止发送数据包。
这是代码
// class that holds infor about client
class ClientBehindNat
{
public IPEndPoint EndpointListening;
public string Id;
}
侦听新客户端(连接)的线程:
while(true)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 1234);
// create a new udpServer
using(var udpServer = new UdpClient())
{
// allow reusing port 1234
udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// wait to receive data
var data = udpServer.Receive(ref ep);
// analyze data make sure it is reliable....
// maybe data is a reply from a client
if(data.IsReply())
{
// mark that client replied
// ....
continue;
}
else
{
// save new client
ConnectedClients.Add(new ClientBehindNat(){EndpointListening=ep});
}
udpServer.Connect(ep);
udpServer.Send( // data notifying client that we got his message)
}
}
不断向客户端发送udp数据包以保持nat打开的线程
// send packets every 15 seconds in order to leave nat open
timer.elapsed += (a,b)=> {
foreach (var c in connectedClients)
{
// create a copy of udp server so that client can get our response
using (var copySocket = new UdpClient())
{
// allow reusing port 1234
copySocket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
copySocket.Client.Bind(new IPEndPoint(IPAddress.Any, 1234));
// log that we will send this request
// Requests[someId] = ....
// send it
copySocket.Send(someData, someData.Length, c.EndpointListening);
// first thread should be responsible for receiving this response
// dispose this client
}
}
}
这就是问题
一切正常但是为每个请求创建一个新的套接字(UdpClient)是不是很糟糕?如果我收到 100 个请求,我将创建 100 个新套接字并将其全部处理掉。当我尝试重用更新其端点的 udp 客户端时,出现异常。我经常需要向 Client A
或 Client Z
发送消息,这就是为什么我存储它的端点以知道如何到达它的原因。 如何重用更新其端点的 udpClient,以便我能够联系到我感兴趣的客户端?
找到问题了。当我回复客户时,我得到了回复,我是这样做的:
udpServer.Connect(ep); // <---------------- this was the problem
udpServer.Send(someData);
如果我从不连接并使用@Sourush 声明的 SneTo 方法,它使我能够继续侦听数据包。因此现在我有:
udpServer.Client.SentTo(data, ep)
我应该再次推荐直接使用 Socket
s,它给你很大的灵活性。查看以下使用一个套接字进行发送和接收的 UDP 连接的示例:
private static readonly List<EndPoint> clients = new List<EndPoint>();
private static readonly SocketAsyncEventArgs receiveEventArgs = new SocketAsyncEventArgs();
private static Socket socket;
private static void Main(string[] args)
{
receiveEventArgs.Completed += OnReceive;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.Bind(new IPEndPoint(IPAddress.Any, 6000));
Receive();
while (true)
{
Thread.Sleep(3000);
lock (clients)
{
foreach (var endPoint in clients)
socket.SendTo(Encoding.ASCII.GetBytes("PING"), endPoint);
}
}
}
private static void Receive()
{
receiveEventArgs.SetBuffer(new byte[256], 0, 256);
receiveEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 6000);
socket.ReceiveFromAsync(receiveEventArgs);
}
private static void OnReceive(object sender, SocketAsyncEventArgs e)
{
if (e.BytesTransferred > 0)
{
// Is reply?
var isReply = true/false;
if (isReply)
{
// Do domething
}
else
lock (clients)
{
clients.Add(e.RemoteEndPoint);
}
}
else
{
lock (clients)
{
clients.Remove(e.RemoteEndPoint);
}
}
Receive();
}
我正在创建一个用户位于路由器 (NAT) 后面的聊天应用程序。所以主要问题是向这些客户发送消息。服务器接收消息没有问题
这是我的协议(规则):
UdpServer
侦听来自client A
的 udp 数据包
Server
从client A
接收数据包并回复数据包以打开 NATServer
现在成为客户端(服务器不断向client A
发送数据包,客户端回复服务器通知它已收到消息)现在 NAT 已打开,我将客户端和服务器反转。这是因为client A
总能通过tcp向服务器发送消息如果
Server
发现client A
没有回复它不断发送的数据包,它会将client A
标记为断开连接并停止发送数据包。
这是代码
// class that holds infor about client
class ClientBehindNat
{
public IPEndPoint EndpointListening;
public string Id;
}
侦听新客户端(连接)的线程:
while(true)
{
IPEndPoint ep = new IPEndPoint(IPAddress.Any, 1234);
// create a new udpServer
using(var udpServer = new UdpClient())
{
// allow reusing port 1234
udpServer.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
// wait to receive data
var data = udpServer.Receive(ref ep);
// analyze data make sure it is reliable....
// maybe data is a reply from a client
if(data.IsReply())
{
// mark that client replied
// ....
continue;
}
else
{
// save new client
ConnectedClients.Add(new ClientBehindNat(){EndpointListening=ep});
}
udpServer.Connect(ep);
udpServer.Send( // data notifying client that we got his message)
}
}
不断向客户端发送udp数据包以保持nat打开的线程
// send packets every 15 seconds in order to leave nat open
timer.elapsed += (a,b)=> {
foreach (var c in connectedClients)
{
// create a copy of udp server so that client can get our response
using (var copySocket = new UdpClient())
{
// allow reusing port 1234
copySocket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
copySocket.Client.Bind(new IPEndPoint(IPAddress.Any, 1234));
// log that we will send this request
// Requests[someId] = ....
// send it
copySocket.Send(someData, someData.Length, c.EndpointListening);
// first thread should be responsible for receiving this response
// dispose this client
}
}
}
这就是问题
一切正常但是为每个请求创建一个新的套接字(UdpClient)是不是很糟糕?如果我收到 100 个请求,我将创建 100 个新套接字并将其全部处理掉。当我尝试重用更新其端点的 udp 客户端时,出现异常。我经常需要向 Client A
或 Client Z
发送消息,这就是为什么我存储它的端点以知道如何到达它的原因。 如何重用更新其端点的 udpClient,以便我能够联系到我感兴趣的客户端?
找到问题了。当我回复客户时,我得到了回复,我是这样做的:
udpServer.Connect(ep); // <---------------- this was the problem
udpServer.Send(someData);
如果我从不连接并使用@Sourush 声明的 SneTo 方法,它使我能够继续侦听数据包。因此现在我有:
udpServer.Client.SentTo(data, ep)
我应该再次推荐直接使用 Socket
s,它给你很大的灵活性。查看以下使用一个套接字进行发送和接收的 UDP 连接的示例:
private static readonly List<EndPoint> clients = new List<EndPoint>();
private static readonly SocketAsyncEventArgs receiveEventArgs = new SocketAsyncEventArgs();
private static Socket socket;
private static void Main(string[] args)
{
receiveEventArgs.Completed += OnReceive;
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
socket.Bind(new IPEndPoint(IPAddress.Any, 6000));
Receive();
while (true)
{
Thread.Sleep(3000);
lock (clients)
{
foreach (var endPoint in clients)
socket.SendTo(Encoding.ASCII.GetBytes("PING"), endPoint);
}
}
}
private static void Receive()
{
receiveEventArgs.SetBuffer(new byte[256], 0, 256);
receiveEventArgs.RemoteEndPoint = new IPEndPoint(IPAddress.Any, 6000);
socket.ReceiveFromAsync(receiveEventArgs);
}
private static void OnReceive(object sender, SocketAsyncEventArgs e)
{
if (e.BytesTransferred > 0)
{
// Is reply?
var isReply = true/false;
if (isReply)
{
// Do domething
}
else
lock (clients)
{
clients.Add(e.RemoteEndPoint);
}
}
else
{
lock (clients)
{
clients.Remove(e.RemoteEndPoint);
}
}
Receive();
}