C#.NET 4.0/4.5 UDP发送问题
我正在用C#编写一个网络库,最初使用的是.NET3.5框架。我最近决定切换到.NET4.5,但开始遇到发送UDP数据包的问题。我遇到的问题是,如果UDP数据包发送得太快,C#.NET 4.0/4.5 UDP发送问题,c#,.net,sockets,networking,udp,C#,.net,Sockets,Networking,Udp,我正在用C#编写一个网络库,最初使用的是.NET3.5框架。我最近决定切换到.NET4.5,但开始遇到发送UDP数据包的问题。我遇到的问题是,如果UDP数据包发送得太快,Socket.SendToAsync方法会以AddressFamilyNotSupported的SocketError完成,并且数据包永远不会发送 如果我将项目切换到.NET3.5,无论我多么努力地重复,我都不会遇到这个问题。这也可以在.NET4.0中复制 下面是我为重现这个问题而组织的项目的一个例子。如果对“ClientSnd
Socket.SendToAsync
方法会以AddressFamilyNotSupported
的SocketError
完成,并且数据包永远不会发送
如果我将项目切换到.NET3.5,无论我多么努力地重复,我都不会遇到这个问题。这也可以在.NET4.0中复制
下面是我为重现这个问题而组织的项目的一个例子。如果对“ClientSnd”或“ServerSnd”按钮发送垃圾邮件,您将看到出现错误。将项目切换到.NET3.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();
由于无论是谁发送数据(客户端还是服务器),都会发生这种情况,因此我将重点关注作为发送者的客户端:
单击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;
}
}
}
客户端的完成回调:
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;
}
}
}
我明白了。之所以出现此问题,是因为变量m_ClientSendArgs上的基础缓冲区一直在使用
SetBuffer
进行更改:
byte[] buffer = m_ClientSendQueue.Dequeue();
m_ClientSendArgs.SetBuffer(buffer, 0, buffer.Length);
当我为其分配静态缓冲区并使用buffer.BlockCopy时,问题消失了:
byte[] buffer = m_ClientSendQueue.Dequeue();
Buffer.BlockCopy(buffer, 0, m_ClientSendBuffer, 0, buffer.Length);
m_ClientSendArgs.SetBuffer(0, buffer.Length);
所以我一直在错误地实施它。奇怪的是,这不是.NET 3.5上的问题,也不是.NET 4.0/4.5上的TCP问题。请将相关片段发布到您的问题中。许多人没有时间下载、提取和运行示例项目。你能在示例代码中把地址设置在哪里吗?我明白。我在我的问题中发布了相关的代码片段。在GetRemoteIPEndPoint中,您有
catch{}
,为什么要丢弃所有异常?您可能发生了异常,但丢失了该异常,并且正在为地址返回null。删除那个try-catch,看看会出现什么错误。我从我创建的支持库中复制了那个片段,该库实际上处理了异常,但我没有删除try/catch,而是将空的catch留在了那里。我删除了它,但没有什么区别,因为当套接字绑定时会调用一次GetRemoteIPEndPoint。这个问题发生在发送数据“太快”之后,并且只能在.NET 4.0和4.5上重现。我注意到从UI调用它时,它似乎发生了。如果我单击“ClientSnd”按钮,然后按住enter键发送垃圾邮件,则会出现此问题。如果我创建了一个while循环,它发送缓冲区10000次,然后单击“ClientSnd”按钮一次(新while循环所在的位置),它就可以正常工作。如果我再次单击该按钮并再次发送10000次缓冲区,它将失败。