C# 重用UdpClient与处置它
我正在创建一个聊天应用程序,其中用户位于路由器(NAT)后面。因此,主要问题是向这些客户发送消息。服务器在接收消息时没有问题 这是我的协议(规则):C# 重用UdpClient与处置它,c#,sockets,endpoint,hole-punching,C#,Sockets,Endpoint,Hole Punching,我正在创建一个聊天应用程序,其中用户位于路由器(NAT)后面。因此,主要问题是向这些客户发送消息。服务器在接收消息时没有问题 这是我的协议(规则): UdpServer侦听来自客户端a的udp数据包 服务器从客户端A接收数据包,并用数据包回复以打开NAT 服务器现在成为客户机(服务器不断向客户机a发送数据包,客户机回复服务器,通知它收到了他的消息)我在NAT打开后反转客户机和服务器。这是因为客户端A始终可以通过tcp向服务器发送消息 如果服务器发现客户端A没有回复其不断发送的数据包,则会将客户端
UdpServer
侦听来自客户端a的udp数据包
服务器
从客户端A
接收数据包,并用数据包回复以打开NAT服务器
现在成为客户机(服务器不断向客户机a
发送数据包,客户机回复服务器,通知它收到了他的消息)我在NAT打开后反转客户机和服务器。这是因为客户端A始终可以通过tcp向服务器发送消息
服务器
发现客户端A
没有回复其不断发送的数据包,则会将客户端A
标记为断开连接,并停止发送数据包// 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客户端更新其端点时,会出现异常。我经常需要向
客户机a
或客户机Z
发送消息,这就是我存储其端点以了解如何到达它的原因如何重新使用udpClient更新其端点,以便能够访问我感兴趣的客户端?发现了问题。当我回复客户我得到了回复时,我是这样做的:
udpServer.Connect(ep); // <---------------- this was the problem
udpServer.Send(someData);
我应该再次推荐直接使用
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();
}
private static readonly List clients=new List();
私有静态只读SocketAsyncEventArgs receiveEventArgs=new-SocketAsyncEventArgs();
专用静态插座;
私有静态void Main(字符串[]args)
{
receiveEventArgs.Completed+=OnReceive;
套接字=新套接字(AddressFamily.InterNetwork、SocketType.Dgram、ProtocolType.Udp);
socket.SetSocketOption(SocketOptionLevel.socket,SocketOptionName.ReuseAddress,true);
socket.Bind(新的IPEndPoint(IPAddress.Any,6000));
接收();
while(true)
{
睡眠(3000);
锁定(客户端)
{
foreach(客户端中的var端点)
SendTo(Encoding.ASCII.GetBytes(“PING”),端点);
}
}
}
私有静态void Receive()
{
receiveEventArgs.SetBuffer(新字节[256],0,256);
receiveEventArgs.RemoteEndPoint=新的IPEndPoint(IPAddress.Any,6000);
socket.ReceiveFromAsync(receiveEventArgs);
}
私有静态void OnReceive(对象发送方,SocketAsyncEventArgs e)
{
如果(例如,ByTestTransferred>0)
{
//你的回答是什么?
var isReply=真/假;
如果(isReply)
{
//做事
}
其他的
锁定(客户端)
{
clients.Add(如RemoteEndPoint);
}
}
其他的
{
锁定(客户端)
{
删除(例如RemoteEndPoint);
}
}
接收();
}
为什么不更改c
的值,而不是创建一个新的UdpClient
?该方法是否出现异常?由于某种原因,如果我更改端点的值,客户端将不会接收数据包。也许我做错了什么,继续尝试。我建议转到Socket
,尤其是SendTo
方法。
udpServer.Connect(ep); // <---------------- this was the problem
udpServer.Send(someData);
udpServer.Client.SentTo(data, ep)
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();
}