UdpClient на локальной машине

Я новичок в кодировании C # UDP, и у меня есть некоторое «странное» поведение при локальном использовании клиента UDP на моем компьютере. Я хочу отправить данные UDP с одного порта (11000) на моем компьютере на другой порт (12000) на том же компьютере.

Это фрагмент моего кода:

public class MyClass
{
    //Creates a UdpClient for reading incoming data.
    private UdpClient udpClient;
    private Thread thread;
    private const String IPADDR = "127.0.0.1";

    public MyClass()
    {
        udpClient = new UdpClient(11000);
        udpClient.Connect(IPAddress.Parse(IPADDR), 12000);


        this.thread = new Thread(new ThreadStart(this.Execute));
        this.thread.Name = "Udp";
        this.thread.Start();

        SendData("The quick brown fox jumps over the lazy dog");

    }

    private void Execute()
    {
        try
        {

            // Blocks until a message returns on this socket from a remote host.
            IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Any, 0);
            Byte[] receiveBytes = this.udpClient.Receive(ref remoteIpEndPoint);

            Console.WriteLine("Data received");

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


    public void SendData(String data)
    {
        Console.WriteLine("Sending...");
        try
        {
            this.udpClient.Send(System.Text.Encoding.ASCII.GetBytes(data), data.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(String.Format("Exception {0}", e.Message));
        }
    }
}

Если я запускаю это, я получаю исключение:

Sending...
System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host 
at System.Net.Sockets.Socket.ReceiveFrom(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags, EndPoint& remoteEP)
at System.Net.Sockets.UdpClient.Receive(IPEndPoint& remoteEP)
at test.MyClass.Execute() in C:\Temp\test\Class1.cs:line 40

Исключение, кажется, происходит в этой строке:

Byte[] receiveBytes = this.udpClient.Receive(ref remoteIpEndPoint);

В момент SendData() Receive генерирует исключение. Когда я не выполняю отправку, я не получаю исключения. Похоже, что отправка вызывает возврат с исключением.

Когда я использую реальный IP-адрес своего компьютера, у меня такое же поведение. Однако, когда я использую любой другой IP-адрес, даже если он не назначен ни одному компьютеру (например, 192.168.10.10), он работает хорошо: он отправляет строку, а Receive() продолжает ждать входящих данных.


person Filip    schedule 17.03.2015    source источник
comment
Можете ли вы добавить код, вызывающий MyClass?   -  person Jack Hughes    schedule 17.03.2015
comment
Код вызывается через конструктор, что я и имел в виду, когда сказал, что ему нужно лучше спроектировать его.   -  person Velja Radenkovic    schedule 17.03.2015
comment
Между прочим: как человек, задающий вопрос, одна из ваших обязанностей состоит в том, чтобы давать обратную связь по полученным ответам. Если вы считаете, что какой-либо ответ полезен (или конкретно бесполезен), вы должны использовать кнопки голосования, чтобы указать это. Если вы считаете, что какой-либо отдельный ответ на самом деле ответил на ваш вопрос наилучшим образом, вы должны принять этот ответ, нажав кнопку с галочкой рядом с ответом.   -  person Peter Duniho    schedule 26.03.2015


Ответы (2)


Ошибка возникает из-за того, что на порту, на который вы отправляете, нет открытого сокета.

В целях простого тестирования связи UDP вы можете исправить свой пример кода, просто изменив порт, на который вы отправляете. т.е. измените вызов метода Connect() на это:

udpClient.Connect(IPAddress.Parse(IPADDR), 11000);

Это заставит сокет отправить самому себе. Ваш код будет успешно работать только с этим изменением.


Для справки, вот пример простейшего демонстрационного кода, в котором используется UdpClient:

UdpClient client = new UdpClient(0, AddressFamily.InterNetwork);

byte[] datagram = Encoding.ASCII.GetBytes("hello world!");

IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Loopback, ((IPEndPoint)client.Client.LocalEndPoint).Port);

client.Send(datagram, datagram.Length, ipEndPoint);
datagram = client.Receive(ref ipEndPoint);

Console.WriteLine("Received: \"" + Encoding.ASCII.GetString(datagram) + "\"");

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

person Peter Duniho    schedule 17.03.2015
comment
Как это отправляет с порта 11000 на 12000? Я хочу отправить данные UDP с одного порта (11000) на моем компьютере на другой порт (12000) на том же компьютере. Разве это не начало вопроса? Этот ответ: 1. Не отвечает на вопрос. 2. Не имеет практического значения. - person Velja Radenkovic; 17.03.2015
comment
Что я хотел сделать, просто для тестирования, так это отправить данные из моей программы в другую стороннюю программу, обе на одной машине. Итак, не отправляя в мою собственную программу. - person Filip; 17.03.2015
comment
@Filip: ну, для этого вам нужны две программы. Вы выложили только одну программу. Пожалуйста, отредактируйте свой вопрос, чтобы сделать его однозначным; на данный момент не ясно, что вы на самом деле спрашиваете. - person Peter Duniho; 17.03.2015
comment
Проблема действительно заключается в том, что порт назначения не был «открыт». т.е. Я сделал ошибку в конфигурации входного порта той другой программы. Когда его входной UDP настроен на 127.0.0.1/12000, моя собственная программа больше не генерирует исключение. Спасибо за подсказку...! Тем не менее, для меня все еще есть кое-что странное: почему я не получаю исключение для случайно выбранного IP-адреса назначения, который не назначен ни одному компьютеру в нашей сети? На самом деле, у человека должна быть такая же ситуация, не так ли? (порт назначения не открыт, поэтому можно ожидать исключения...) - person Filip; 17.03.2015
comment
Это отличный вопрос. По сути, базовый сетевой транспорт имеет полную информацию о доступных портах на вашем компьютере, поэтому, если вы попытаетесь отправить или получить в/из несуществующий порт, транспорт может сгенерировать ошибку. Но UDP по замыслу просто игнорирует сбои доставки в целом, и поэтому, когда вы пытаетесь отправить на несуществующий порт по какому-то удаленному адресу, отсутствие подтверждения — это нормально. Следовательно, в этом случае нет ошибки. - person Peter Duniho; 17.03.2015
comment
Спасибо Петр за объяснение. На самом деле, это немного неожиданное поведение для меня: UDP не устанавливает соединение, поэтому можно было бы ожидать, что UDP не беспокоится о наличии удаленного хоста/порта, будь то на локальной машине или на удаленном хосте. Очевидно, он ведет себя по-другому для соединений на локальной машине. Хорошо знать... - person Filip; 18.03.2015

Вы пытаетесь отправить из одного сокета (UdpClient, который является оболочкой сокета) самому себе. Это не то, как все работает в UDP (и в других IP-протоколах). Вам нужно 2 розетки. Один для отправки один для получения. Метод подключения плохо назван, так как протокол UDP не имеет соединения, но это не ваша вина.

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

Вам нужно лучше разработать свою программу, но это будет работать:

class Program
{
    static void Main(string[] args)
    {
        MyClass mc = new MyClass();
    }
}

public class MyClass
{
    //Creates a UdpClient for reading incoming data.
    private UdpClient udpClient;
    private UdpClient recipient;
    private Thread thread;
    private const String IPADDR = "127.0.0.1";

    public MyClass()
    {
        recipient = new UdpClient(new IPEndPoint(IPAddress.Parse(IPADDR), 12000));
        this.thread = new Thread(new ThreadStart(this.Execute));
        this.thread.Name = "Udp";
        this.thread.Start();

        udpClient = new UdpClient(11000);
        udpClient.Connect(IPAddress.Parse(IPADDR), 12000);

        SendData("The quick brown fox jumps over the lazy dog");
    }

    private void Execute()
    {
        try
        {
            // Blocks until a message returns on this socket from a remote host.
            IPEndPoint remoteIpEndPoint = new IPEndPoint(IPAddress.Parse(IPADDR), 11000);

            Byte[] receiveBytes = this.recipient.Receive(ref remoteIpEndPoint);

            Console.WriteLine("Data received: " + Encoding.ASCII.GetString(receiveBytes));

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


    public void SendData(String data)
    {
        Console.WriteLine("Sending...");
        try
        {
            this.udpClient.Send(System.Text.Encoding.ASCII.GetBytes(data), data.Length);
        }
        catch (Exception e)
        {
            Console.WriteLine(String.Format("Exception {0}", e.Message));
        }
   }
}
person Velja Radenkovic    schedule 17.03.2015
comment
На самом деле сокеты UDP всегда находятся в состоянии прослушивания, дейтаграммы буферизуются (так что вам обязательно не нужно уже вызывать ReceiveFrom() или что-то подобное перед отправкой дейтаграммы), а сокет UDP, безусловно, может получить дейтаграмму от самого себя. Другой вопрос, имеет ли смысл посылать самому себе; это полезно для тестирования кода, но, очевидно, имеет ограниченную ценность в реальном приложении. Но это не объясняет исключение, которое получает OP, что значительно ограничивает полезность этого ответа (поскольку он не отвечает на фактический вопрос). - person Peter Duniho; 17.03.2015
comment
Итак, 1 сокет UDP может отправлять сам себе с порта udp 11000 на 12000? Продолжайте, пожалуйста. Я хотел бы услышать о 2 портовых сокетах. - person Velja Radenkovic; 17.03.2015
comment
Я никогда этого не говорил. Я сказал, что сокет UDP может отправлять данные самому себе. Если он привязан к порту 11000, то он должен отправить на порт 11000, чтобы получить то, что он отправил себе. - person Peter Duniho; 17.03.2015
comment
@VeljaRadenkovic: Итак, вы действительно хотели сказать, что один сокет не может быть привязан к двум портам? Тогда скорректируйте свой ответ. - person jgauffin; 17.03.2015
comment
Извините, я не понимаю, что непонятно? Пожалуйста, отредактируйте, если вы считаете, что это неясно, я не против. Очевидно, что у вас не может быть 1 самостоятельная отправка/самополучение сокета с одного порта на другой порт, это уже убивает определение сокета. Или предложите мне отредактировать, и я это сделаю. - person Velja Radenkovic; 17.03.2015