Reputation: 3259
I'm using the following code to create an asynchronous TCP server:
private void SetupServerSocket()
{
var myEndpoint = new IPEndPoint(IPAddress.Any, _port);
_serverSocket = new Socket(myEndpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
_serverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
_serverSocket.Bind(myEndpoint);
_serverSocket.Listen((int)SocketOptionName.MaxConnections);
}
protected void Open()
{
SetupServerSocket();
Opened = true;
_serverSocket.BeginAccept(AcceptCallback, _serverSocket);
}
private void AcceptCallback(IAsyncResult result)
{
var connection = new ConnectionInfo();
try
{
// Finish Accept
var s = (Socket)result.AsyncState;
connection.Socket = s.EndAccept(result);
connection.Buffer = new byte[8192];
lock (_connections)
{
_connections.Add(connection);
}
// Start Receive and a new Accept
connection.Socket.BeginReceive(connection.Buffer, 0, connection.Buffer.Length, SocketFlags.None, ReceiveCallback, connection);
_serverSocket.BeginAccept(AcceptCallback, result.AsyncState);
}
catch (SocketException ex)
{
CloseConnection(connection);
}
catch (Exception ex)
{
CloseConnection(connection);
}
}
private void CloseConnection(ConnectionInfo ci)
{
if (ci.Socket != null)
{
if (OnDisconnect != null)
OnDisconnect.Invoke((IPEndPoint)ci.Socket.RemoteEndPoint);
ci.Socket.Shutdown(SocketShutdown.Both);
ci.Socket.Close();
}
lock (_connections)
{
_connections.Remove(ci);
}
}
There are some things that are hard to understand:
1 - Using _serverSocket.SetSocketOption
I enabled the use of KeepAlive in TCP. I discovered that Windows by default has the default KeepAlive Timer of 2 hours! and this behavior was confirmed using Wireshark.
After some Googling I found in http://support.microsoft.com/kb/120642/EN-US that you can change Windows KeepAliveTime by creating a key in registry. I did like this support page instructs but the KeepAlive Timer was not applied (even after rebooting), it's still 2 hours. Does anyone know how to change KeepAlive Timer in Windows?
2 - The code connection.Socket = s.EndAccept(result)
can thrown exceptions (usually SocketException). Why Socket.EndResult
throws SocketException
?
3 - Why do we have to set _serverSocket
in Accept state again in AcceptCallback()
if it was already in that state?
Upvotes: 2
Views: 976
Reputation: 3299
I had similar problems with the .net Keep Alive timer. I never found a way to change it so the solution we used was to have a background worker thread that sends a small amount of data, generally byte[1]
, after a given time period if other real data had not been sent.
Socket.EndAccept() can throw the expected exceptions as per the MSDN. These generally occur if something has happened to the socket elsewhere, such as the socket being closed etc. You need to correctly catch these exceptions and decide how you want your program to continue. In your case it looks like you are just closing the connection.
Calling _serverSocket.BeginAccept(AcceptCallback, result.AsyncState);
will trigger the callback only ONCE for whichever connection is accepted first. After this you have to decide whether you want to accept further future connections, if so by calling BeginAccept again within the callback.
Upvotes: 1