我使用以下代码创建一个异步TCP服务器:
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);
}
}
有些事情很难理解:
1 -使用_serverSocket.SetSocketOption
启用了TCP中的KeepAlive。我发现Windows默认的KeepAlive Timer为2小时!并且使用Wireshark证实了该行为。
经过一些谷歌搜索,我发现在http://support.microsoft.com/kb/120642/EN-US,你可以改变Windows KeepAliveTime通过创建一个键在注册表中。我喜欢这个支持页面的指示,但KeepAlive计时器没有应用(即使重新启动后),它仍然是2小时。有人知道如何在Windows中更改KeepAlive定时器吗?
2 -代码connection.Socket = s.EndAccept(result)
可以抛出异常(通常是SocketException)。为什么Socket.EndResult
抛出SocketException
?
3 -如果AcceptCallback()
已经处于接受状态,为什么我们必须在AcceptCallback()
中再次将_serverSocket
设置为接受状态?
1条答案
按热度按时间6jjcrrmo1#
1.我在.net的Keep Alive计时器上也遇到了类似的问题。我从来没有找到一种方法来改变它,所以我们使用的解决方案是有一个后台工作线程,在给定的时间段后,如果其他真实的数据没有发送,它会发送少量数据,通常是
byte[1]
。1.调用
_serverSocket.BeginAccept(AcceptCallback, result.AsyncState);
只会对最先接受的连接触发一次回调。在此之后,您必须决定是否要接受未来的进一步连接,如果是,则在回调中再次调用BeginAccept。