简体   繁体   English

是否可以有多个具有相同 localEndPoint 的 UDP c# 套接字?

[英]is it possible to have multiple UDP c# sockets with same localEndPoint?


I wrote a TCP client/server code that works fine.我写了一个工作正常的 TCP 客户端/服务器代码。 a TCP socket listener accept connections and create many sockets with same localEndPoint and various RemoteEndPoint. TCP 套接字侦听器接受连接并创建许多具有相同 localEndPoint 和各种 RemoteEndPoint 的套接字。 now I like to expand to UDP but I have a problem in seerver code.现在我想扩展到 UDP,但我在服务器代码中遇到了问题。 i must bind UDP socket to a localEndPoint for each client object.我必须将 UDP 套接字绑定到每个客户端对象的 localEndPoint。 but I encounter an error: I can't bind multiple UDP sockets with same localEndPoint.但我遇到了一个错误:我无法使用相同的 localEndPoint 绑定多个 UDP 套接字。 I found that is nature of UDP sockets according to Wiki :根据Wiki ,我发现这是 UDP 套接字的性质:

A UDP server does not create new child processes for every concurrently served client, but the same process handles incoming data packets from all remote clients sequentially through the same socket. UDP 服务器不会为每个并发服务的客户端创建新的子进程,但同一进程通过同一套接字顺序处理来自所有远程客户端的传入数据包。 It implies that UDP sockets are not identified by the remote address, but only by the local address, although each message has an associated remote address.这意味着 UDP 套接字不是由远程地址标识的,而是仅由本地地址标识的,尽管每条消息都有一个关联的远程地址。

so can I create a distinct UDP socket for each Client like I do for TCP sockets?那么我可以像为 TCP 套接字那样为每个客户端创建一个不同的 UDP 套接字吗?
note: please explain with socket class if possible not UDPClient class.注意:如果可能,请用socket类解释,而不是UDPClient类。

    private void SocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Accept:
                case SocketAsyncOperation.Connect:
                    UDP.Bind(TCP.LocalEndPoint);

code explanation: I thought I can bind a UDP socket in both sides (client/server) after establishing TCP connection (connect/accept).代码解释:我以为我可以在建立TCP连接(连接/接受)后在双方(客户端/服务器)绑定一个UDP套接字。 it is fine for only one client on server.服务器上只有一个客户端是可以的。 for new clients server throws an Exception because it can't bind multiple sockets with one localEndPoint .对于新客户端,服务器会抛出异常,因为它无法将多个套接字与一个 localEndPoint 绑定。 and clients can be more than all available ports.并且客户端可以超过所有可用端口。 so i can't bind a UDP socket to a distinct localEndPoint for each client.所以我无法为每个客户端将 UDP 套接字绑定到不同的 localEndPoint。
I knew that i can create a unique UDP socket and pass receiving messages to respective client object on server but its a bit nasty.我知道我可以创建一个唯一的 UDP 套接字并将接收消息传递给服务器上的相应客户端对象,但这有点讨厌。 thanks!谢谢!

You could run a Task per unique source IP:Port combination.您可以为每个唯一的源 IP:Port 组合运行一个任务。 This would allow you to easily maintain a state machine for each unique incoming source IP:PORT combination.这将允许您轻松维护每个唯一传入源 IP:PORT 组合的状态机。 System.IO.Pipelines makes it really easy to tie this together. System.IO.Pipelines可以很容易地将它们结合在一起。

First set up a UDPListener Task that kind of mimics a TCPListener , for each new IP:PORT spin up a Task to process that port, create a Pipe and eveytime data comes from that IPEndpoint, put the data into the PipeWriter首先设置一个类似于TCPListenerUDPListener任务,对于每个新 IP:PORT 启动一个任务来处理该端口,创建一个 Pipe 并且每个时间数据来自该 IPEndpoint,将数据放入PipeWriter

    static async Task StartUdpListener()
    {
        // Use a Dictionary to match packets from given connections to give Pipes
        ConcurrentDictionary<string, Pipe> connections = new ConcurrentDictionary<string, Pipe>();

        var udpServer = new UdpClient(new IPEndPoint(IPAddress.Any, 33000));
        while (true)
        {
            // Wait for some data to arrive
            var result = await udpServer.ReceiveAsync();

            if(connections.ContainsKey(result.RemoteEndPoint.ToString()))
            {
                // If we have seen this IPEndpoint before send the traffic to the pipe
                // the task associated with that Pipe willpick the traffic up
                connections.TryGetValue(result.RemoteEndPoint.ToString(), out var p);
                await p.Writer.WriteAsync(result.Buffer);
            }
            else
            {
                // If we have not seen it, make the pipe, stick the data in the pipe
                // and spin up a task to Read/Process the data
                var p = new Pipe();
                connections.TryAdd(result.RemoteEndPoint.ToString(), p);
                await p.Writer.WriteAsync(result.Buffer);
                _ = Task.Run(() => UdpServerClient(result.RemoteEndPoint.ToString(), p));
            }
        }
    }

That's a simplistic view of what the kernel does when it receives a TCPPacket, it sticks it in a socket buffer for you to read via a stream.这是内核在接收 TCPPacket 时执行的操作的一个简单视图,它将它粘贴在套接字缓冲区中供您通过流读取。

A UDP Server Client task would look something like this: UDP Server Client任务看起来像这样:

    static async Task UdpServerClient(string serverName,Pipe p)
    {
        while (true)
        {
            var readResult = await p.Reader.ReadAsync();
            var message = Encoding.ASCII.GetString(readResult.Buffer.FirstSpan.ToArray());
            Console.WriteLine($"Server: {serverName} Received: {message}");
            p.Reader.AdvanceTo(readResult.Buffer.End);
        }
    }

And for completeness, a few clients, these would normally be on different machines, but for the sake of simplicity we'll run them as Tasks.为了完整起见,一些客户端通常位于不同的机器上,但为了简单起见,我们将它们作为任务运行。

    static async Task UdpClientClient(string messageToSend)
    {
        var client = new UdpClient();
        client.Connect("127.0.0.1", 33000);
        for(var i=0;i<5;i++)
        {
            var message = ASCIIEncoding.ASCII.GetBytes(messageToSend + " #"+ i.ToString());
            await client.SendAsync(message, message.Length);
            await Task.Delay(1000);
        }
    }
}

Link them all together:将它们链接在一起:

    static async Task Main(string[] args)
    {
        _ = Task.Run(() => StartUdpListener());

        _ = UdpClientClient("hi Server!");
        _ = UdpClientClient("I am here server...");
        await UdpClientClient("Me too server!");
    }

And you get this:你会得到这个:

Server: 127.0.0.1:53183 Received: Me too server! #0
Server: 127.0.0.1:53182 Received: I am here server... #0
Server: 127.0.0.1:53181 Received: hi Server! #0
Server: 127.0.0.1:53182 Received: I am here server... #1
Server: 127.0.0.1:53183 Received: Me too server! #1
Server: 127.0.0.1:53181 Received: hi Server! #1
Server: 127.0.0.1:53182 Received: I am here server... #2
Server: 127.0.0.1:53183 Received: Me too server! #2
Server: 127.0.0.1:53181 Received: hi Server! #2
Server: 127.0.0.1:53182 Received: I am here server... #3
Server: 127.0.0.1:53181 Received: hi Server! #3
Server: 127.0.0.1:53183 Received: Me too server! #3
Server: 127.0.0.1:53183 Received: Me too server! #4
Server: 127.0.0.1:53181 Received: hi Server! #4
Server: 127.0.0.1:53182 Received: I am here server... #4

Of course you need error checking, seeing if the client is still there etc.当然,您需要进行错误检查,查看客户端是否仍然存在等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM