如何在循环中使用 UdpClient.BeginReceive
我想这样做
for (int i = 0; i < 100; i++ )
{
Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
}
,但我必须使用 UdpClient.BeginReceive
,而不是使用 UdpClient.Receive
。问题是,我该怎么做?使用 BeginReceive
的示例并不多,而且 MSDN 示例根本没有帮助。我应该使用 BeginReceive
,还是只是在单独的线程下创建它?
我一直收到 ObjectDisposeException
异常。我只收到发送的第一个数据。下一个数据将抛出异常。
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());
}
}
}
什么给?
顺便说一句,总体思路是:
- 创建tcpclient manager。
- 开始使用udpclient发送/接收数据。
- 当所有数据都发送完毕后,tcpclient manager将通知接收者所有数据已发送,并且udpclient连接应该关闭。
I want to do this
for (int i = 0; i < 100; i++ )
{
Byte[] receiveBytes = receivingUdpClient.Receive(ref RemoteIpEndPoint);
}
But instead of using UdpClient.Receive
, I have to use UdpClient.BeginReceive
. The problem is, how do I do that? There aren't a lot of samples using BeginReceive
, and the MSDN example is not helping at all. Should I use BeginReceive
, or just create it under a separate thread?
I consistently get ObjectDisposedException
exception. I only get the first data sent. The next data will throw exception.
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());
}
}
}
What gives?
By the way, the general idea is:
- Create tcpclient manager.
- Start sending/receiving data using udpclient.
- When all data has been sent, tcpclient manager will signal receiver that all data has been sent, and udpclient connection should be closed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
看起来
UdpClient.BeginReceive()
和UdpClient.EndReceive()
没有得到很好的实现/理解。当然,与 TcpListener 的实现方式相比,使用起来要困难得多。您可以采取多种措施来更好地使用
UdpClient.Receive()
。首先,在底层套接字客户端上设置超时将使控制失败(出现异常),从而允许控制流继续或按照您的意愿循环。其次,通过在新线程上创建 UDP 侦听器(我没有显示其创建过程),可以避免UdpClient.Receive()
函数的半阻塞效应,并且可以有效地如果您做得正确,请稍后中止该线程。下面的代码分为三部分。第一部分和最后一部分应分别位于主循环的入口点和出口点。第二部分应该位于您创建的新线程中。
一个简单的例子:
除了所有这些之外,您还可以检查
UdpClient.Available > 0
以确定在执行UdpClient.Receive()
之前是否有任何 UDP 请求排队 - 这完全消除了阻塞方面。我确实建议您谨慎尝试此操作,因为此行为没有出现在 Microsoft 文档中,但似乎确实有效。注意:
在研究此问题时,您可能会发现 MSDN 示例代码需要额外的 用户定义类 - UdpState。这不是 .NET 库类。这似乎让很多人在研究这个问题时感到困惑。
超时不必严格设置为使您的应用程序能够完全退出,但它们将允许您在该循环中执行其他操作,而不是永远阻塞。
listener.Close() 命令很重要,因为它强制 UdpClient 抛出异常并退出循环,允许 Thread.Abort() 得到处理。如果没有这个,您可能无法正确终止侦听器线程,直到超时或收到 UDP 数据包,导致代码继续通过 UdpClient.Receive() 块。
为了补充这个无价的答案,这里有一个有效且经过测试的代码片段。 (这里是在 Unity3D 环境中,当然也适用于任何 c#。)
It would seem that
UdpClient.BeginReceive()
andUdpClient.EndReceive()
are not well implemented/understood. And certainly compared to how the TcpListener is implemented, are a lot harder to use.There are several things that you can do to make using the
UdpClient.Receive()
work better for you. Firstly, setting timeouts on the underlying socket Client will enable control to fall through (to an exception), allowing the flow of control to continue or be looped as you like. Secondly, by creating the UDP listener on a new thread (the creation of which I haven't shown), you can avoid the semi-blocking effect of theUdpClient.Receive()
function and you can effectively abort that thread later if you do it correctly.The code below is in three parts. The first and last parts should be in your main loop at the entry and exit points respectively. The second part should be in the new thread that you created.
A simple example:
In addition to all this, you can also check
UdpClient.Available > 0
in order to determine if any UDP requests are queued prior to executingUdpClient.Receive()
- this completely removes the blocking aspect. I do suggest that you try this with caution as this behaviour does not appear in the Microsoft documentation, but does seem to work.Note:
The MSDN exmaple code you may have found while researching this problem requires an additional user defined class - UdpState. This is not a .NET library class. This seems to confuse a lot of people when they are researching this problem.
The timeouts do not strictly have to be set to enable your app to exit completely, but they will allow you to do other things in that loop instead of blocking forever.
The listener.Close() command is important because it forces the UdpClient to throw an exception and exit the loop, allowing Thread.Abort() to get handled. Without this you may not be able to kill off the listener thread properly until it times out or a UDP packet is received causing the code to continue past the UdpClient.Receive() block.
Just to add to this priceless answer, here's a working and tested code fragment. (Here in a Unity3D context but of course for any c#.)
我认为您不应该在循环中使用它,而是每当调用 BeginReceive 回调时,您都会再次调用 BeginReceive ,并且如果您想将数量限制为 100,则保留一个公共变量用于计数。
I think you should not use it in a loop but instead whenever the BeginReceive callback is called you call BeginReceive once more and you keep a public variable for count if you want to limit the number to 100.
先看看MSDN。他们提供了很好的例子。
http://msdn.microsoft.com/en -us/library/system.net.sockets.udpclient.beginreceive.aspx
have look at MSDN first. They provide good example.
http://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.beginreceive.aspx
我会在后台线程上进行网络通信,这样它就不会阻塞应用程序中的任何其他内容。
BeginReceive 的问题是您必须在某个时刻调用 EndReceive(否则您将有等待句柄) - 并且调用 EndReceive 将阻塞,直到接收完成。这就是为什么将通信放在另一个线程上更容易。
I would do network communication on a background thread, so that it doesn't block anything else in your application.
The issue with BeginReceive is that you must call EndReceive at some point (otherwise you have wait handles just sitting around) - and calling EndReceive will block until the receive is finished. This is why it is easier to just put the communication on another thread.
您必须在另一个线程上执行网络操作、文件操作以及依赖于其他事物而不是您自己的程序的此类操作(或 任务),因为它们可能会冻结您的程序。原因是您的代码是按顺序执行的。
您在循环中使用了它,这不太好。每当调用
BeginRecieve
回调时,您都应该再次调用它。查看以下代码:You have to do network operations, file manipulations and such things that are dependent to other things rather than your own program on another thread (or task) because they may freeze your program. The reason for that is that your code executes sequentially.
You have used it in a loop which is not fine. Whenever
BeginRecieve
callback is invoked you should call it again. Take a look at the following code: