Отправка и получение пакетов UDP между двумя программами на одном компьютере - PullRequest
35 голосов
/ 27 марта 2009

Можно ли заставить две отдельные программы взаимодействовать на одном и том же компьютере (только в одну сторону) через UDP через localhost / 127 ..., используя один и тот же порт #?

Мы работаем над студенческим проектом, в котором нам нужно отправлять пакеты UDP, содержащие некоторую телеметрию, между двумя компьютерами. Программа, которая генерирует эти пакеты, является проприетарной, но я работаю над программой-получателем самостоятельно на C #, используя System.Net.Sockets.UdpClient и System.Net.IPEndPoint .

Это прекрасно работает во время собраний нашей группы, когда у нас подключено несколько компьютеров, на которых мы можем запускать две программы по отдельности. Но это не очень полезно, когда я дома и пытаюсь расширить программу обработки телеметрии, поскольку у меня есть только один компьютер (мне нужен канал для тестирования программы обработки). Я не могу установить программу ни на один из школьных компьютеров.

Когда я пытаюсь запустить обе программы на моем компьютере одновременно (запуск моей программы последним), я получаю исключение SocketException, в котором говорится, что обычно разрешено только одно использование каждого порта. Это заставляет меня поверить, что должен быть какой-то способ совместного использования порта (хотя имеет смысл, что только одна программа может использовать порт на компьютере одновременно, у меня нет проблем с запуском нескольких интернет-браузеров одновременно (и я предположим, что они используют порт 80 для http)).

ПРАВКА РЕДАКТИРОВАНИЯ:

sipwiz был прав, и спасибо Kalmi за указатель на UdpClient.Client.Bind (). Однако в то время мы рассматриваем возможность использования другой программы, которая генерирует аналогичные пакеты и с которой мы можем использовать порт на одном компьютере, используя мой первый (хотя и наивный) подход с привязкой клиента UDP в ctor. Извините за то, что сняли отметку с вашего ответа, sysrqb.

Ответы [ 8 ]

75 голосов
/ 27 марта 2009

Вы можете подключиться к порту несколько раз, используя опцию сокета ReuseAddress.

UdpClient udpClient = new UdpClient();
udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

Вам необходимо установить такую ​​же опцию и на сокете UDP-сервера.

34 голосов
/ 28 марта 2009

Я не ожидал, что это будет возможно, но .. хорошо .. sipwiz был прав.

Это можно сделать очень легко. (Пожалуйста, проголосуйте за sipwiz!)

IPEndPoint localpt = new IPEndPoint(IPAddress.Any, 6000);

//Failed try
    try
    {
        var u = new UdpClient(5000);
        u.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

        UdpClient u2 = new UdpClient(5000);//KABOOM
        u2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    }
    catch (Exception)
    {
        Console.WriteLine("ERROR! You must call Bind only after setting SocketOptionName.ReuseAddress. \n And you must not pass any parameter to UdpClient's constructor or it will call Bind.");
    }

//This is how you do it (kudos to sipwiz)
    UdpClient udpServer = new UdpClient(localpt); //This is what the proprietary(see question) sender would do (nothing special) 

    //!!! The following 3 lines is what the poster needs...(and the definition of localpt (of course))
    UdpClient udpServer2 = new UdpClient();
    udpServer2.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    udpServer2.Client.Bind(localpt);
5 голосов
/ 29 октября 2014

Вот полный код из ответов Tarnay Kálmán и sipwiz:

Код сервера:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UdpBroadcastTest
{
    class Program
    {
        static void Main(string[] args)
        {

            Console.WriteLine("Sender");
            // This constructor arbitrarily assigns the local port number.

            UdpClient udpClient = new UdpClient();
            udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            udpClient.Connect("localhost", 11000);
            try
            {
                string message = String.Empty;
                do
                {
                    message = Console.ReadLine();
                    // Sends a message to the host to which you have connected.
                    Byte[] sendBytes = Encoding.ASCII.GetBytes(message);

                    udpClient.Send(sendBytes, sendBytes.Length);
                } while (message != String.Empty);

                udpClient.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            Console.WriteLine("Press Any Key to Continue");
            Console.ReadKey();
        }
    }
}

Код клиента:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace UdpReciever
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Receiver");
            // This constructor arbitrarily assigns the local port number.
            UdpClient udpClient = new UdpClient();
            udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
            udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, 11000));
            try
            {
                //IPEndPoint object will allow us to read datagrams sent from any source.
                IPEndPoint RemoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);

                string message = String.Empty;
                do
                {

                    // Blocks until a message returns on this socket from a remote host.
                    Byte[] receiveBytes = udpClient.Receive(ref RemoteIpEndPoint);
                    message = Encoding.ASCII.GetString(receiveBytes);

                    // Uses the IPEndPoint object to determine which of these two hosts responded.
                    Console.WriteLine("This is the message you received: " +
                                                 message);
                    //Console.WriteLine("This message was sent from " +
                    //                            RemoteIpEndPoint.Address.ToString() +
                    //                            " on their port number " +
                    //                            RemoteIpEndPoint.Port.ToString());
                }
                while (message != "exit");
                udpClient.Close();
                //udpClientB.Close();

            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }

            Console.WriteLine("Press Any Key to Continue");
            Console.ReadKey();
        }
    }
}
2 голосов
/ 27 марта 2009

Возможно, вы сможете разместить несколько IP-адресов на вашей сетевой карте или в петле и связать сервер и клиент с разными IP-адресами?

Или же подход с виртуальной машиной определенно сработает.

1 голос
/ 27 марта 2009

Только одна программа может привязывать к порту одновременно. Несколько программ могут подключать к одному порту в другой системе, но локальный порт, к которому привязаны ваши различные браузеры, назначается случайным образом.

Если вы не хотите делать некрасивое взаимодействие между процессами или перехватывать пакеты, невозможно связать несколько программ с одним портом.

0 голосов
/ 03 февраля 2012

Даже изменяя ваш код, чтобы я мог передать IP-адрес, я получаю то же сообщение об ошибке, что кажется, что вы не можете привязать к одному и тому же порту, и можно использовать только один порт Вот пример кода, который я использовал на вашем примере и изменил для захвата моего ip с моего локального компьютера. IPAddress ipAddress = Dns.Resolve (Dns.GetHostName ()). AddressList [0]; IPEndPoint ipLocalEndPoint = новый IPEndPoint (ipAddress, 11000);

        //IPEndPoint localpt = new IPEndPoint(ipLocalEndPoint);

        UdpClient udpServer = new UdpClient(ipLocalEndPoint);
        udpServer.Client.SetSocketOption(
            SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        udpServer.Connect(ipLocalEndPoint);
        UdpClient udpServer2 = new UdpClient();
        udpServer2.Client.SetSocketOption(
            SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

        udpServer2.Client.Bind(ipLocalEndPoint); // <<---------- Exception here

это вызовет исключение для метода Bind () .. извините.

0 голосов
/ 02 августа 2009

привязать две программы, то есть отправителя и получателя к одному и тому же порту на localhost.dats простой ответ.

0 голосов
/ 27 марта 2009

Мой совет: не передавайте номер порта в конструктор UdpClient. Из документации ((немного разреженно, я знаю ...) похоже, что если вы это сделаете, UdpClient попытается привязать к этому порту (который, как упоминал sysrqb, не положено). (Если вы этого не сделаете, я полагаю, что UdpClient будет прослушивать произвольный порт для любых ответов. Вы также можете выбрать порт, который, как вы знаете, не используется.)

Когда вы вызываете Connect (), вам нужно передать номер порта, на котором сервер прослушивает вкл.

...