I need to write an app that accepts incoming sockect connections and sends some data to all off them every N ms. My code:
private readonly Timer _senderTimer;
private readonly Socket _serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
private readonly ConcurrentDictionary<EndPoint, Socket> _clientConnections =
new ConcurrentDictionary<EndPoint, Socket>();
public Start(SettingsProvider settingsProvider)
{
_senderTimer = new Timer(SendIntervalTick, null, settingsProvider.SendInterval, Timeout.Infinite);
_serverSocket.Bind(new IPEndPoint(selfIp, selfPort));
_serverSocket.Listen(MaxLengthOfPendingConnectionsQueue);
_serverSocket.BeginAccept(AcceptCallback, null);
}
private void AcceptCallback(IAsyncResult result)
{
EndPoint endPoint = null;
try
{
var acceptedSocket = _serverSocket.EndAccept(result);
endPoint = acceptedSocket.RemoteEndPoint;
_clientConnections.TryAdd(endPoint, acceptedSocket);
}
catch (Exception e)
{
CloseSocket(endPoint);
}
finally
{
_serverSocket.BeginAccept(AcceptCallback, null);
}
}
private void SendIntervalTick(object state)
{
SendPacket(new[] {(byte)0xFF});
_senderTimer.Change(Math.Max(0, _sendInterval - watch.ElapsedMilliseconds), Timeout.Infinite);
}
private void SendPacket(byte[] packet)
{
var connToRemove = new List<EndPoint>();
var connToSend = _clientConnections.ToDictionary(x => x.Key, x => x.Value);
// Send packet to all connected clients
foreach (var connection in connToSend)
{
try
{
connection.Value.BeginSend(packet, 0, packet.Length,
SocketFlags.None,
SendCallback,
new ClientConnection(connection.Key, connection.Value));
}
catch (Exception e)
{
// Get System.Net.Sockets.SocketException (0x80004005): An established connection was aborted by the software in your host machine here!!!
connToRemove.Add(connection.Key);
}
}
// Close broken connections
foreach (var endPoint in connToRemove)
{
CloseSocket(endPoint);
}
}
private void SendCallback(IAsyncResult result)
{
var connection = (ClientConnection)result.AsyncState;
try
{
connection.Sock.EndSend(result);
}
catch (Exception e)
{
CloseSocket(connection.RemoteEndPoint);
}
}
private void CloseSocket(EndPoint endPoint)
{
Socket socketToClose;
_clientConnections.TryGetValue(endPoint, out socketToClose)
try
{
socketToClose.Close();
socketToClose.Dispose();
}
catch (Exception e)
{
Logger.Error("Error of closing/disposing socket.", e);
}
finally
{
Socket removable;
if (!_clientConnections.TryRemove(endPoint, out removable))
{
Logger.Warn("Can not remove an endPoint ({0}) from the client-connections-dict ({1}).",
endPoint, string.Join(", ", _clientConnections.Keys));
}
}
}
Now I get an exception
System.Net.Sockets.SocketException (0x80004005): An established connection was aborted by the software in your host machine
when call BeginSend among 20-50 successful sent. What I've noticed is that exception always throws soon after new connection accepted:
TRACE 674 bytes successfully sent to client 212.193.xxx.xxx:21666
TRACE 674 bytes successfully sent to client 212.193.xxx.xxx:21666
DEBUG An endPoint (212.193.xxx.xxx:22014) successfully added to the client-connections-dict (212.193.xxx.xxx:21666, 212.193.xxx.xxx:22014)
TRACE 674 bytes successfully sent to client 212.193.xxx.xxx:21666
TRACE 674 bytes successfully sent to client 212.193.xxx.xxx:22014
WARN Closing socket 212.193.xxx.xxx:21666 because of exception while BeginSend: System.Net.Sockets.SocketException (0x80004005): An established connection was aborted by the software in your host machine
But I don't know why the error occurs and how to fix it. Please help!
EDIT: I've sniffed the traffic and found out that one of clients (who always was a source of problems) periodically sends tcp packet with a Reset flag. That's why I failed to send packet to it. According to this answer this may may occure because of server received packet for a closed socket. I wonder why this may happen and how server should behave in this situation.