Я пишу TCP-сервер на C # и столкнулся со странной и потенциально опасной проблемой.
Моя основная арка для принятия новых подключений выглядит следующим образом:
- Сокет C #, прослушивающий порт, используя метод
AcceptAsync
для приема входящих подключений. - Отключение принятых соединений с использованием
ThreadPool
для завершения принятия.
Все работает достаточно хорошо, однако все тормозит, если кто-то подключится к порту.
Симптомы:
Если я подключусь к своему серверу через telnet и не отправлю никаких данных (т.е. не нажму никакие клавиши), сервер никогда не завершит прием соединения.
Мой обратный вызов
SocketAsyncEventArgs.Completed
никогда не выполняется для соединения telnet.Хуже того, все дальнейшие соединения блокируются / ставятся в очередь и никогда не принимаются моим кодом. Они переведены в состояние
CLOSE_WAIT
:TCP 127.0.0.1:8221 chance:53960 CLOSE_WAIT
TCP 127.0.0.1:8221 chance:53962 CLOSE_WAIT
TCP 127.0.0.1:8221 chance:53964 CLOSE_WAIT
Любой совет будет принят во внимание.
StartAccept:
private void StartAccept(SocketAsyncEventArgs AcceptArgs)
{
CurrentAcceptArgs = AcceptArgs;
AcceptArgs.AcceptSocket = null;
if (AcceptArgs.Buffer == null ||
AcceptArgs.Buffer.Length < 1024)
{
AcceptArgs.SetBuffer(new byte[1024], 0, 1024);
}
if (MainSocket != null)
{
lock (MainSocket)
{
// If this is false, we have an accept waiting right now, otherwise it will complete aynsc
if (MainSocket.AcceptAsync(AcceptArgs) == false)
{
ThreadPool.QueueUserWorkItem(FinishAccept, AcceptArgs);
StartAccept(GetConnection());
}
}
}
}
Завершенный обратный вызов для приема соединений:
protected override void OnIOCompleted(object sender, SocketAsyncEventArgs e)
{
PWClientRemote RemoteClient = e.UserToken as PWClientRemote;
// Determine which type of operation just completed and call the associated handler.
switch (e.LastOperation)
{
case SocketAsyncOperation.Accept:
StartAccept(GetConnection());
ThreadPool.QueueUserWorkItem(FinishAccept, e);
break;
default:
base.OnIOCompleted(sender, e);
break;
}
}
Завершить Принять:
private void FinishAccept(object StateObject)
{
SocketAsyncEventArgs args = (SocketAsyncEventArgs)StateObject;
FinishAcceptInternal(args);
}
Вот wirehark от подключения telnet, но до отправки данных:
No. Time Source Destination Protocol Length Info
1 0.000000 192.168.1.146 192.168.1.109 TCP 66 59766 > 8221 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
2 0.000076 192.168.1.109 192.168.1.146 TCP 66 8221 > 59766 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
3 0.000389 192.168.1.146 192.168.1.109 TCP 60 59766 > 8221 [ACK] Seq=1 Ack=1 Win=65536 Len=0
Это должно быть полное рукопожатие для установления моего соединения, но событие Completed
никогда не возникает.