Получение и обработка непрерывных пакетов с использованием UDP

Это моя текущая настройка (с использованием UDP):

void OnDataReceived(IAsyncResult result)
{

    IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);

    byte[] buffer = socket.EndReceive(result, ref ep);

    Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));

    //process packet

    socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);

}

Мне было интересно, что произойдет, если я сразу же вызову socket.BeginReceive после вызова EndReceive, а затем обработаю пакет, чтобы получить непрерывный поток пакетов, подобный этому:

void OnDataReceived(IAsyncResult result)
{

    IPEndPoint ep = new IPEndPoint(IPAddress.Any, 0);

    byte[] buffer = socket.EndReceive(result, ref ep);
    socket.BeginReceive(new AsyncCallback(OnDataReceived), socket);

    Packet p = new Packet(Encoding.ASCII.GetString(buffer, 0, buffer.Length));

    //process packets

}

Если пакет получен, как только я вызываю BeginReceive, будет ли это как-то конфликтовать с текущей обработкой пакета?

Кроме того, если бы это не конфликтовало, переход на TCP сделал бы это неработоспособным?


person Xortrox    schedule 01.12.2012    source источник


Ответы (1)


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

Вот пример кода, как использовать отдельный поток для обработки входящих данных UDP. Он неполный, но должен дать вам представление о том, как его настроить.

    private Thread _udpReadThread;
    private volatile bool _terminateThread;

    public event DataEventHandler OnDataReceived;
    public delegate void DataEventHandler(object sender, DataEventArgs e);

    private void CreateUdpReadThread()
    {
        _udpReadThread = new Thread(UdpReadThread) { Name = "UDP Read thread" };
        _udpReadThread.Start(new IPEndPoint(IPAddress.Any, 1234));
    }

    private void UdpReadThread(object endPoint)
    {
        var myEndPoint = (EndPoint)endPoint;
        var udpListener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
        udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);

        // Important to specify a timeout value, otherwise the socket ReceiveFrom() 
        // will block indefinitely if no packets are received and the thread will never terminate
        udpListener.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 100);
        udpListener.Bind(myEndPoint);

        try
        {
            while (!_terminateThread)
            {
                try
                {
                    var buffer = new byte[1024];
                    var size = udpListener.ReceiveFrom(buffer, ref myEndPoint);
                    Array.Resize(ref buffer, size);

                    // Let any consumer(s) handle the data via an event
                    FireOnDataReceived(((IPEndPoint)(myEndPoint)).Address, buffer);
                }
                catch (SocketException socketException)
                {
                    // Handle socket errors
                }
            }
        }
        finally
        {
            // Close Socket
            udpListener.Shutdown(SocketShutdown.Both);
            udpListener.Close();
        }
    }

    public class DataEventArgs : EventArgs
    {
        public byte[] Data { get; private set; }
        public IPAddress IpAddress { get; private set; }

        public DataEventArgs(IPAddress ipaddress, byte[] data)
        {
            IpAddress = ipaddress;
            Data = data;
        }
    }
person Jakob Möllås    schedule 01.12.2012
comment
Это кажется правильным ответом, но я не могу проголосовать за него из-за моей репутации 11. В настоящее время я использую подход Socket.BeginReceive, который вызывает функцию, привязанную к новому аргументу AsyncCallback, когда данные были получены. (Я считаю, что это позволяет завершить текущий вызов OnDataReceived, в то же время имея возможность прослушивать новые данные). Моя главная проблема, вероятно, немного глупа, но я беспокоюсь о том, как работают несколько вызовов одной функции из нескольких потоков, когда это делается одновременно - person Xortrox; 02.12.2012
comment
На самом деле не обращайте на это внимания, я как бы нашел решение: stackoverflow.com/questions/10467070/ - person Xortrox; 02.12.2012
comment
Не вызывает ли это много ненужного перераспределения памяти? Вы выделяете свой буфер каждый проход по циклу, независимо от того, используется он или нет. - person Loren Pechtel; 05.09.2013
comment
Да, здесь можно немного оптимизировать, выделяя только когда что-то получено. - person Jakob Möllås; 07.09.2013