Reputation: 31
I am working on writing a network library in C# and originally had used the .NET 3.5 Framework. I recently decided to switch to .NET 4.5 but started running into an issue with sending UDP packets. What I'm running into is if UDP packets are sent too fast, the Socket.SendToAsync
method completes with a SocketError
of AddressFamilyNotSupported
and the packets are never sent.
If I switch the project to .NET 3.5, I never run into the issue no matter how hard I try to repeat it. This also can be reproduced in .NET 4.0.
Here is a link to the project I put together to reproduce the issue. If you spam the "ClientSnd" or "ServerSnd" buttons you'll see the error occur. Switch the project to .NET 3.5 and spam all you want... no issues at all.
I haven't been able to find much useful information searching on this issue. Any ideas?
EDIT (added code from the sample project demoing the issue):
Here's where the binds are happening for both the client and server:
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();
The GetRemoteIPEndPoint
and GetLocalIPEndPoint
methods:
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;
}
Since this happens regardless of who sends the data (client or server), I'll focus on the client being the sender:
Clicking the ClientSnd
button:
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();
}
}
}
Sending methods for the client:
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);
}
}
Completion callback for the client:
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;
}
}
}
Upvotes: 1
Views: 1410
Reputation: 31
I figured this out. This issue is happening because the underlying buffer on the variable m_ClientSendArgs is constantly being changed using SetBuffer
:
byte[] buffer = m_ClientSendQueue.Dequeue();
m_ClientSendArgs.SetBuffer(buffer, 0, buffer.Length);
When I assigned a static buffer to it and used Buffer.BlockCopy, the issue went away:
byte[] buffer = m_ClientSendQueue.Dequeue();
Buffer.BlockCopy(buffer, 0, m_ClientSendBuffer, 0, buffer.Length);
m_ClientSendArgs.SetBuffer(0, buffer.Length);
So I've been implementing it wrong all along. It's strange that it wasn't an issue on .NET 3.5, or an issue for TCP on .NET 4.0/4.5.
Upvotes: 2