【发布时间】:2013-03-18 20:12:13
【问题描述】:
我正在用 C# 编写一个网络库,最初使用的是 .NET 3.5 Framework。我最近决定切换到 .NET 4.5,但开始遇到发送 UDP 数据包的问题。我遇到的是如果 UDP 数据包发送得太快,Socket.SendToAsync 方法以SocketError 或AddressFamilyNotSupported 完成,并且永远不会发送数据包。
如果我将项目切换到 .NET 3.5,无论我多么努力地重复它,我都不会遇到这个问题。这也可以在 .NET 4.0 中重现。
这是我为重现该问题而组建的项目的link。如果您向“ClientSnd”或“ServerSnd”按钮发送垃圾邮件,您将看到错误发生。将项目切换到 .NET 3.5 并随心所欲地发送垃圾邮件……完全没有问题。
我无法找到有关此问题的很多有用信息。有什么想法吗?
编辑(从演示问题的示例项目中添加代码):
这是客户端和服务器都发生绑定的地方:
byte[] clientBuffer = new byte[32768];
byte[] serverBuffer = new byte[32768];
IPEndPoint clientLocalEndPoint = GetLocalIPEndPoint(0, AddressFamily.InterNetwork);
IPEndPoint serverLocalEndPoint = GetLocalIPEndPoint(6337, AddressFamily.InterNetwork);
m_ClientSocket.ExclusiveAddressUse = true;
m_ServerSocket.ExclusiveAddressUse = true;
m_ClientSocket.Bind(clientLocalEndPoint);
m_ServerSocket.Bind(serverLocalEndPoint);
m_ClientSendArgs.RemoteEndPoint = GetRemoteIPEndPoint("127.0.0.1", 6337, AddressFamily.InterNetwork);
m_ClientRecvArgs.RemoteEndPoint = m_ClientSocket.LocalEndPoint;
m_ServerSendArgs.RemoteEndPoint = GetRemoteIPEndPoint("127.0.0.1", ((IPEndPoint)m_ClientSocket.LocalEndPoint).Port, AddressFamily.InterNetwork);
m_ServerRecvArgs.RemoteEndPoint = m_ServerSocket.LocalEndPoint;
m_ClientSendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnClientCompletion);
m_ClientRecvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnClientCompletion);
m_ServerSendArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnServerCompletion);
m_ServerRecvArgs.Completed += new EventHandler<SocketAsyncEventArgs>(OnServerCompletion);
m_ClientRecvArgs.SetBuffer(clientBuffer, 0, clientBuffer.Length);
m_ServerRecvArgs.SetBuffer(serverBuffer, 0, serverBuffer.Length);
ClientReceive();
ServerReceive();
GetRemoteIPEndPoint 和 GetLocalIPEndPoint 方法:
private static IPEndPoint GetRemoteIPEndPoint(string address, int port, AddressFamily addressFamily)
{
IPAddress[] ipAddresses = null;
ipAddresses = Dns.GetHostAddresses(address);
List<IPEndPoint> ipEndPointList = new List<IPEndPoint>();
for (int i = 0; i < ipAddresses.Length; i++)
{
IPAddress ipAddress = ipAddresses[i];
if (ipAddress.AddressFamily == addressFamily)
{
IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);
ipEndPointList.Add(ipEndPoint);
}
}
return ipEndPointList.ToArray()[0];
}
private static IPEndPoint GetLocalIPEndPoint(int port, AddressFamily addressFamily)
{
IPEndPoint localEndPoint = null;
switch (addressFamily)
{
case AddressFamily.InterNetwork:
{
localEndPoint = new IPEndPoint(IPAddress.Any, port);
break;
}
case AddressFamily.InterNetworkV6:
{
localEndPoint = new IPEndPoint(IPAddress.IPv6Any, port);
break;
}
}
return localEndPoint;
}
由于无论谁发送数据(客户端或服务器)都会发生这种情况,因此我将重点关注作为发送者的客户端:
点击ClientSnd按钮:
private void Button_ClientSnd_Click(object sender, RoutedEventArgs e)
{
lock (SyncRoot)
{
byte[] buffer = Encoding.ASCII.GetBytes("Hello there. Just testing. Nothing to see here. Move along.");
m_ClientSendQueue.Enqueue(buffer);
if (!m_ClientTransmitting)
{
m_ClientTransmitting = true;
ClientSendBuffer();
}
}
}
客户端的发送方法:
private void ClientSendBuffer()
{
lock (SyncRoot)
{
if (m_ClientSendQueue.Count > 0)
{
byte[] buffer = m_ClientSendQueue.Dequeue();
m_ClientSendArgs.SetBuffer(buffer, 0, buffer.Length);
ClientSend();
}
else
{
m_ClientTransmitting = false;
}
}
}
private void ClientSend()
{
if (!m_ClientSocket.SendToAsync(m_ClientSendArgs))
{
OnClientCompletion(this, m_ClientSendArgs);
}
}
客户端的完成回调:
private void OnClientCompletion(object sender, SocketAsyncEventArgs e)
{
SocketError socketError = e.SocketError;
if (socketError != SocketError.Success)
{
ClientConsoleWrite("SocketError: {0}\r\n", socketError);
}
switch (e.LastOperation)
{
case SocketAsyncOperation.SendTo:
{
if (socketError == SocketError.Success)
{
ClientConsoleWrite("Client message sent!\r\n");
}
ClientSendBuffer();
break;
}
case SocketAsyncOperation.ReceiveFrom:
{
int bytesTransferred = e.BytesTransferred;
byte[] buffer = new byte[bytesTransferred];
Buffer.BlockCopy(e.Buffer, e.Offset, buffer, 0, bytesTransferred);
string message = Encoding.ASCII.GetString(buffer);
ClientConsoleWrite("Message received: {0}\r\n", message);
ClientReceive();
break;
}
}
}
【问题讨论】:
-
您能否将相关的sn-ps发布到您的问题中。许多人没有时间下载、提取和运行示例项目。主要是您可以发布您在示例代码中设置地址的位置吗?
-
我明白了。我在我的问题中发布了相关的 sn-ps。
-
在 GetRemoteIPEndPoint 你有
catch { },你为什么要扔掉你所有的异常?您可能发生了异常,错过了它,并返回 null 作为地址。删除那个 try-catch,看看你得到了什么错误。 -
我从我创建的实际处理异常的支持库中复制了 sn-p,但我没有删除 try/catch,而是将空的 catch 留在那里。我删除了它,但它没有任何区别,因为在绑定套接字时调用了一次 GetRemoteIPEndPoint。此问题在发送数据“太快”后发生,并且只能在 .NET 4.0 和 4.5 上重现。
-
我注意到从 UI 调用它时似乎会发生这种情况。如果我单击“ClientSnd”按钮然后按住 Enter 发送垃圾邮件,则会出现此问题。如果我创建一个发送缓冲区 10,000 次的 while 循环,然后单击一次“ClientSnd”按钮(新的 while 循环所在的位置),它工作正常。如果我再次单击该按钮并将缓冲区再发送 10,000 次,它就会失败。
标签: c# .net sockets networking udp