How to use UdpClient.BeginReceive in a loop
我想这么做
1 2 3 4 | for (int i = 0; i < 100; i++ ) { Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint); } |
但我不使用
我一直得到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | public class UdpReceiver { private UdpClient _client; public System.Net.Sockets.UdpClient Client { get { return _client; } set { _client = value; } } private IPEndPoint _endPoint; public System.Net.IPEndPoint EndPoint { get { return _endPoint; } set { _endPoint = value; } } private int _packetCount; public int PacketCount { get { return _packetCount; } set { _packetCount = value; } } private string _buffers; public string Buffers { get { return _buffers; } set { _buffers = value; } } private Int32 _counter; public System.Int32 Counter { get { return _counter; } set { _counter = value; } } private Int32 _maxTransmission; public System.Int32 MaxTransmission { get { return _maxTransmission; } set { _maxTransmission = value; } } public UdpReceiver(UdpClient udpClient, IPEndPoint ipEndPoint, string buffers, Int32 counter, Int32 maxTransmission) { _client = udpClient; _endPoint = ipEndPoint; _buffers = buffers; _counter = counter; _maxTransmission = maxTransmission; } public void StartReceive() { _packetCount = 0; _client.BeginReceive(new AsyncCallback(Callback), null); } private void Callback(IAsyncResult result) { try { byte[] buffer = _client.EndReceive(result, ref _endPoint); // Process buffer MainWindow.Log(Encoding.ASCII.GetString(buffer)); _packetCount += 1; if (_packetCount < _maxTransmission) { _client.BeginReceive(new AsyncCallback(Callback), null); } } catch (ObjectDisposedException ex) { MainWindow.Log(ex.ToString()); } catch (SocketException ex) { MainWindow.Log(ex.ToString()); } catch (System.Exception ex) { MainWindow.Log(ex.ToString()); } } } |
给出了什么?
顺便说一下,总的想法是:
似乎
有几件事你可以做,使使用
下面的代码分为三部分。第一部分和最后一部分应该分别位于入口和出口点的主循环中。第二部分应该在您创建的新线程中。
一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | // 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; |
除此之外,您还可以检查
注:
您在研究此问题时可能发现的msdn-exmaple代码需要一个额外的用户定义类udpstate。这不是.NET库类。这似乎让很多人在研究这个问题时感到困惑。
不必严格设置超时以使应用程序完全退出,但它们将允许您在该循环中执行其他操作,而不是永远阻塞应用程序。
listener.close()命令很重要,因为它强制udpclient引发异常并退出循环,从而允许处理thread.abort()。如果没有这个,在侦听器线程超时或接收到UDP包导致代码继续通过udpclient.receive()块之前,您可能无法正确地终止该线程。
为了增加这个无价的答案,这里有一个正在工作和测试的代码片段。(这里是Unity3D的上下文,当然是任何C。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | // 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"); } |
先看一下msdn。它们提供了很好的例子。http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.beginreceive.aspx
我会在后台线程上进行网络通信,这样它就不会阻塞应用程序中的任何其他内容。
BeginReceive的问题是,您必须在某个时间点调用EndReceive(否则,您的等待句柄就在附近),调用EndReceive将阻塞,直到接收完成。这就是为什么只将通信放在另一个线程上更容易的原因。
我认为您不应该在循环中使用它,而应该在调用BeginReceive回调时再次调用BeginReceive,如果您希望将该数限制为100,则将一个公共变量保留为Count。
您必须在另一个线程(或任务)上执行网络操作、文件操作以及依赖于其他事物而不是您自己的程序的事情,因为它们可能冻结您的程序。原因是代码按顺序执行。你在一个不好的循环中使用它。每当调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | 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); } } |