2010-08-18 3 views
1

У меня проблема с методом Socket.SendAsync, не обнаруживающим мертвое TCP-соединение. В моем клиентском/серверном приложении сервер периодически посылает удаленные сообщения на подключенные клиенты.Socket.SendAsync не обнаруживает мертвое соединение TCP

Проблема, которую я испытываю, заключается в том, что даже если клиент может быть мертв, обратные вызовы метода SendAsync указывают «SocketError.Success», а свойство Socket.Connected истинно, даже если клиент больше не " в живых". Таким образом, на сервер выглядит так, что данные heartbeat были отправлены правильно, а клиент все еще жив.

Я вижу эту проблему каждый раз, клиентский ПК либо ставится в спящий режим/спящий режим, либо, например, когда клиент работает в экземпляре VMWare, и этот экземпляр приостанавливается. Я не вижу этот вопрос, когда клиент закрывает приложение, убивает его из диспетчера задач и т.д.

internal void InternalSendAsync(ByteDataChunk chunk) 
    { 
     asyncSendArgs.SetBuffer(chunk.Buffer, 0, chunk.Offset); 
     asyncSendArgs.UserToken = chunk; 
     Socket.SendAsync(asyncSendArgs); 
    } 

    private void SendCompleted(object sender, SocketAsyncEventArgs args) 
    { 
     if (args.SocketError != SocketError.Success || !Socket.Connected) 
     { 
      InternalDisconnect(args.SocketError); 
      return; 
     } 

     // all is good & do some other stuff 
    } 

Кто-нибудь имеет ни малейшего представления, что происходит здесь и почему метод SendCompleted не возвращает SocketError даже несмотря на то, клиент давно мертв (у меня был сервер, работавший несколько часов назад, и мертвый сокет никогда не обнаруживался)?

Спасибо,

Том

ответ

3

От MSDN:

Обратите внимание, что успешное завершение метод SendAsync не указывает , что данные были успешно доставлены .

IMO, одна из самых сложных частей в области сетевого взаимодействия, вы не можете быть уверены, что клиент когда-либо получал данные. Если вы внедряете систему сердечных сокращений, вы должны вернуть клиенту ответное мгновение, подтвердив, что он все еще жив.

Когда вы приостанавливаете процесс или спящий режим компьютера, я не думаю, что сокет будет закрыт так, как если бы вы остановили машину, на которой вы работаете.

+0

Право - это обычный подход. Если клиент не ответил на биение в секунд, предположите, что он мертв и отключите его. – caf

+0

Я согласен, что это был бы один из способов сделать это, однако я никогда не видел эту проблему с синхронными отправлениями, поскольку я всегда получал сообщение об ошибке после того, как даже если хост был поставлен в спящий режим/спящий режим – TJF

0

Действительно ли отправлены сердечные сокращения? Мое подозрение было бы Naggle algorithm. Вытяните wireshark и проверьте, что происходит на проводе. Вы можете отключить Nagle с SocketOptionName.NoDelay. От MSDN:

Успешное завершение метода BeginSend означает, что базовая система имеет место для буферизации данных для отправки по сети. Если для вашего приложения важно сразу отправить каждый байт на удаленный хост, вы можете использовать SetSocketOption для включения SocketOptionName.NoDelay. Для получения дополнительной информации о буферизации эффективности сети см. Алгоритм Nagle в MSDN.
+0

Nagle не должен иметь что-либо с этим, так как даже с включенным Nagle данные будут отправляться после фиксированного интервала времени (обычно 200-500 мс). – TJF

+0

Интересно, делает ли асинхронный слой .net асинхронным. –

0

Игнорировать имущество Socket.Connected; это почти бесполезно. В вашем примере кода вы предполагаете, что все нормально, если либоSocket.Connected верен, либо не было кода ошибки. Первое, что я сделал бы, это удалить часть Socket.Connected.

Я рекомендую хранить выдающееся асинхронное считывание в любое время вместе с периодическими посылами сердечных сокращений. Если сокет больше не подключен, то либо чтение, либо запись приведет к ошибке.

Пошлите время ожидания несколько раз с экспоненциальным отклонением. Таким образом, требуется некоторое время, чтобы обнаружить, когда другая сторона исчезнет (в случае выхода программы ОС немедленно ответит, что соединение больше не является жизнеспособным). Тем не менее, это должно быть не так близко к часам; всего несколько минут (с самого начала с медленным сетевым подключением). Мои сокеты регулярно обнаруживают сброшенные соединения в течение секунды или около того.

+0

Это именно то, что я делаю, и вопрос был сосредоточен вокруг того, почему запись не приводит к ошибке, если клиентская машина была усыпан. Я могу писать в этот сокет часами с помощью метода SendAsync, и он никогда не выдает ошибку, если клиент был приостановлен, но он вызывает ошибку, если, например, клиент был убит – TJF

+0

Ваш код предполагает, что если 'Socket.Connected' истинно, соединение все еще действует. Это не правильно. Удалите часть проверки (Socket.Connected) (оставляя только часть ошибки проверки) и посмотрите, работает ли это. –

+0

вы правы, я сделал ошибку при размещении кода здесь, так как мой производственный код выполняет некоторые другие вещи, и я ошибся, когда я сократил его и разместил здесь. Производственный код || ! Socket.Connected – TJF

0

Вы использовали Wireshark или подобное, чтобы узнать, что происходит в сети? Можно было бы подумать, что если подсистема TCP на клиенте не подтверждает пакеты, тогда должна быть ошибка сокета. Возможно, клиент поддерживает открытие порта и подтверждение пакета (ов). Если это так, то вы можете попытаться решить это на клиенте или сделать то, что сказал Николай.

+0

В захвате я вижу PSH, ACK, а затем 3 повторных передачи, ничего после этого. Imho, я должен получить исключение таймаута в сокете, потому что никакой ACK не получен, но я этого не делаю? – TJF

Смежные вопросы