Можно ли иметь несколько сокетов UDP c# с одним и тем же localEndPoint? - PullRequest
0 голосов
/ 09 января 2020


Я написал код клиент / сервер TCP, который работает нормально. прослушиватель сокетов TCP принимает соединения и создает множество сокетов с одним и тем же localEndPoint и различными RemoteEndPoint. теперь мне нравится расширяться до UDP, но у меня есть проблема в коде сервера. я должен привязать сокет UDP к localEndPoint для каждого объекта клиента. но я сталкиваюсь с ошибкой: я не могу связать несколько сокетов UDP с одним и тем же localEndPoint. Я обнаружил, что это природа сокетов UDP согласно Wiki :

Сервер UDP не создает новых дочерних процессов для каждого одновременно обслуживаемого клиента, но тот же процесс обрабатывает входящие пакеты данных со всех удаленных клиентов последовательно через один и тот же сокет. Это означает, что UDP-сокеты не идентифицируются по удаленному адресу, а только по локальному адресу, хотя каждое сообщение имеет связанный удаленный адрес.

, поэтому можно создать отдельный сокет UDP для каждый клиент, как я для TCP-сокетов?
примечание: объясните, если возможно, классом 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);

объяснение кода: я думал, что могу связать UDP-сокет в обе стороны (клиент / сервер) после установления TCP-соединения (соединение / принятие). это хорошо только для одного клиента на сервере. для новых клиентов сервер генерирует исключение, потому что он не может связать несколько сокетов с одним localEndPoint. и клиенты могут быть больше, чем все доступные порты. поэтому я не могу привязать сокет UDP к отдельному localEndPoint для каждого клиента.
Я знал, что могу создать уникальный сокет UDP и передавать приемные сообщения соответствующему объекту клиента на сервере, но это немного неприятно. спасибо!

1 Ответ

1 голос
/ 09 января 2020

Вы можете запустить задачу для уникального исходного IP: комбинация портов. Это позволит вам легко поддерживать конечный автомат для каждой уникальной комбинации входящего IP-адреса: PORT. System.IO.Pipelines позволяет очень легко объединить ie.

Сначала создайте UDPListener задачу, которая имитирует TCPListener, для каждого нового IP: PORT ускоряет задачу для обработки этот порт, создайте канал и данные eveytime поступают из этой 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));
            }
        }
    }

Это упрощенное c представление о том, что делает ядро, когда оно получает TCPPacket, оно вставляет его в буфер сокетов для чтения через поток.

Задача 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);
        }
    }

И для полноты, для некоторых клиентов это обычно на разных машинах, но ради простоты мы будем запускать их как Задачи.

    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);
        }
    }
}

Свяжите их все вместе:

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

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

И вы получите это:

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

Конечно, вам нужна проверка на наличие ошибок, если клиент все еще там и т. Д. c.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...