C# 如何在循环中使用UdpClient.BeginReceive

C# 如何在循环中使用UdpClient.BeginReceive,c#,udpclient,beginreceive,C#,Udpclient,Beginreceive,我想这样做 for (int i = 0; i < 100; i++ ) { Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint); } 有什么好处 顺便说一下,总的想法是: 创建tcpclient管理器 开始使用udpclient发送/接收数据 当所有数据都已发送时,tcpclient manager将向接收器发出所有数据已发送的信号,udpclient连接应关闭 我认为您不应该在循环中使用

我想这样做

for (int i = 0; i < 100; i++ )
{
    Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
}
有什么好处

顺便说一下,总的想法是:

  • 创建tcpclient管理器
  • 开始使用udpclient发送/接收数据
  • 当所有数据都已发送时,tcpclient manager将向接收器发出所有数据已发送的信号,udpclient连接应关闭

  • 我认为您不应该在循环中使用它,而是在调用BeginReceive回调时,您可以再次调用BeginReceive,如果您想将数字限制为100,则可以保留一个公共变量进行计数。

    请先查看MSDN。他们提供了很好的例子。

    我会在后台线程上进行网络通信,这样它就不会阻止应用程序中的任何其他内容


    BeginReceive的问题是,您必须在某个时刻调用EndReceive(否则您的等待句柄就在旁边),并且调用EndReceive将被阻塞,直到接收完成。这就是为什么将通信放在另一个线程上更容易的原因。
    UdpClient.BeginReceive()
    UdpClient.EndReceive()
    似乎没有得到很好的实现/理解。当然,与TcpListener的实现方式相比,使用起来要困难得多

    要使使用
    UdpClient.Receive()
    更好地工作,您可以做几件事。首先,在底层套接字客户机上设置超时将使控制失效(导致异常),从而允许控制流继续或循环。其次,通过在一个新线程上创建UDP侦听器(我没有显示创建的线程),可以避免
    UdpClient.Receive()
    函数的半阻塞效应,并且如果操作正确,以后可以有效地中止该线程

    下面的代码分为三部分。第一部分和最后一部分应该分别位于入口点和出口点的主循环中。第二部分应该在您创建的新线程中

    一个简单的例子:

    // Define this globally, on your main thread
    UdpClient listener = null;
    // ...
    
    
    // ...
    // Create a new thread and run this code:
    
    IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999);
    byte[] data = new byte[0];
    string message = "";
    
    listener.Client.SendTimeout = 5000;
    listener.Client.ReceiveTimeout = 5000;
    
    listener = new UdpClient(endPoint);
    while(true)
    {
        try
        {
            data = listener.Receive(ref endPoint);
            message = Encoding.ASCII.GetString(data);
        }
        catch(System.Net.Socket.SocketException ex)
        {
            if (ex.ErrorCode != 10060)
            {
                // Handle the error. 10060 is a timeout error, which is expected.
            }
        }
    
        // Do something else here.
        // ...
        //
        // If your process is eating CPU, you may want to sleep briefly
        // System.Threading.Thread.Sleep(10);
    }
    // ...
    
    
    // ...
    // Back on your main thread, when it's exiting, run this code
    // in order to completely kill off the UDP thread you created above:
    listener.Close();
    thread.Close();
    thread.Abort();
    thread.Join(5000);
    thread = null;
    
    除此之外,您还可以检查
    UdpClient.Available>0
    ,以确定在执行
    UdpClient.Receive()
    之前是否有任何UDP请求排队-这完全消除了阻塞方面。我建议您谨慎尝试,因为这种行为不会出现在Microsoft文档中,但似乎确实有效

    注:

    您在研究此问题时可能发现的问题需要额外的-UdpState。这不是.NET库类。这似乎让很多人在研究这个问题时感到困惑

    不必严格地设置,以使您的应用程序完全退出,但它们将允许您在该循环中执行其他操作,而不是永远阻塞

    该命令很重要,因为它强制UdpClient抛出异常并退出循环,从而允许处理Thread.Abort()。否则,您可能无法正确终止侦听器线程,直到它超时或收到UDP数据包,导致代码继续通过UdpClient.Receive()块


    为了补充这个无价的答案,这里有一个工作和测试的代码片段。(此处为Unity3D上下文,但当然适用于任何c#)


    您必须执行网络操作、文件操作等依赖于其他内容的操作,而不是在另一个线程(或)上执行您自己的程序,因为它们可能会冻结您的程序。原因是代码按顺序执行。 您在循环中使用了它,这是不好的。无论何时调用
    BeginRecieve
    回调,您都应该再次调用它。请看以下内容:


    您当然知道UDP是一种协议,它可以释放数据包,并且不保证唯一性或顺序,因此尝试从特定端点接收100个数据包并不一定意味着您收到了相同的100个数据包,按顺序发送?也许你应该使用TCP?我很清楚这一点。原因是,我想分析两方之间的连接,即带宽估计。socket beginreceive(异步)in loop很难实现,相反,请尝试使用NetworkStream.DataAvailable组合同步loop。提示:您可以对同一套接字使用BeginSend(异步)。好的例子?他们甚至懒得提及wtf是UdpState(虽然我发现了这一点,但想到他们让这件简单的事情溜走了,我仍然感到不安。我想知道他们允许溜走的其他主要事情是什么。)在C#6中,使用。如下所示:
    catch(System.Net.Sockets.SocketException ex)when(ex.ErrorCode==10060)
    // Define this globally, on your main thread
    UdpClient listener = null;
    // ...
    
    
    // ...
    // Create a new thread and run this code:
    
    IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 9999);
    byte[] data = new byte[0];
    string message = "";
    
    listener.Client.SendTimeout = 5000;
    listener.Client.ReceiveTimeout = 5000;
    
    listener = new UdpClient(endPoint);
    while(true)
    {
        try
        {
            data = listener.Receive(ref endPoint);
            message = Encoding.ASCII.GetString(data);
        }
        catch(System.Net.Socket.SocketException ex)
        {
            if (ex.ErrorCode != 10060)
            {
                // Handle the error. 10060 is a timeout error, which is expected.
            }
        }
    
        // Do something else here.
        // ...
        //
        // If your process is eating CPU, you may want to sleep briefly
        // System.Threading.Thread.Sleep(10);
    }
    // ...
    
    
    // ...
    // Back on your main thread, when it's exiting, run this code
    // in order to completely kill off the UDP thread you created above:
    listener.Close();
    thread.Close();
    thread.Abort();
    thread.Join(5000);
    thread = null;
    
    // minmal flawless UDP listener per PretorianNZ
    
    using System.Collections;
    using System;
    using System.Net.Sockets;
    using System.Net;
    using System.Threading;
    
    void Start()
       {
       listenThread = new Thread (new ThreadStart (SimplestReceiver));
       listenThread.Start();
       }
    
    private Thread listenThread;
    private UdpClient listenClient;
    private void SimplestReceiver()
       {
       Debug.Log(",,,,,,,,,,,, Overall listener thread started.");
    
       IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Any, 1260);
       listenClient = new UdpClient(listenEndPoint);
       Debug.Log(",,,,,,,,,,,, listen client started.");
    
       while(true)
          {
          Debug.Log(",,,,, listen client listening");
    
          try
             {
             Byte[] data = listenClient.Receive(ref listenEndPoint);
             string message = Encoding.ASCII.GetString(data);
             Debug.Log("Listener heard: " +message);
             }
          catch( SocketException ex)
             {
             if (ex.ErrorCode != 10060)
                Debug.Log("a more serious error " +ex.ErrorCode);
             else
                Debug.Log("expected timeout error");
             }
    
          Thread.Sleep(10); // tune for your situation, can usually be omitted
          }
       }
    
    void OnDestroy() { CleanUp(); }
    void OnDisable() { CleanUp(); }
    // be certain to catch ALL possibilities of exit in your environment,
    // or else the thread will typically live on beyond the app quitting.
    
    void CleanUp()
       {
       Debug.Log ("Cleanup for listener...");
    
       // note, consider carefully that it may not be running
       listenClient.Close();
       Debug.Log(",,,,, listen client correctly stopped");
    
       listenThread.Abort();
       listenThread.Join(5000);
       listenThread = null;
       Debug.Log(",,,,, listener thread correctly stopped");
       }
    
    public static bool messageReceived = false;
    
    public static void ReceiveCallback(IAsyncResult ar)
    {
      UdpClient u = (UdpClient)((UdpState)(ar.AsyncState)).u;
      IPEndPoint e = (IPEndPoint)((UdpState)(ar.AsyncState)).e;
    
      Byte[] receiveBytes = u.EndReceive(ar, ref e);
      string receiveString = Encoding.ASCII.GetString(receiveBytes);
    
      Console.WriteLine("Received: {0}", receiveString);
      messageReceived = true;
    }
    
    public static void ReceiveMessages()
    {
      // Receive a message and write it to the console.
      IPEndPoint e = new IPEndPoint(IPAddress.Any, listenPort);
      UdpClient u = new UdpClient(e);
    
      UdpState s = new UdpState();
      s.e = e;
      s.u = u;
    
      Console.WriteLine("listening for messages");
      u.BeginReceive(new AsyncCallback(ReceiveCallback), s);
    
      // Do some work while we wait for a message. For this example,
      // we'll just sleep
      while (!messageReceived)
      {
        Thread.Sleep(100);
      }
    }