C#-сокет сервер. потеря соообщений, при доставке клиенту.

Phoenix

Есть приложение на c#, написано с использованием SocketAsyncEventArgs(есть ощущение, что технология не так важна, хотя могу ошибаться). крутится в VBox'e. Хотя эффект я смог повторить на десктопной винде.
Приложение(сервер) завершает асинхронную операцию, но сообщение получателю(клиенту) не доставляется. (нагл отключён)
и "ничего не происходит" достаточно долго(минуту где-то, потом мне ждать надоедает).
Данные доставляются, если сервер снова посылает какие-то данные.
В принципе, это предусмотрено и стандартом TCP, и документацией. Мало ли, пакетик потерялся. Но, эффект был обнаружен на локальной винде. оба приложения на ней.
и freebsd- windows(vbox на той же freebsd). На последней появляется раз в 2-3 минуты. Трафик не сказать, чтобы какой-то огромный.
Раньше, когда рассылались ещё и дебажные сообщений(и трафика было больше) эффект не был замечен, т.к. следующий пакет проталкивал предыдущие.
Пока склоняюсь к тому, чтобы в протокол обмена ввести heartbeat сообщение, которое каждые 2-3 секунды будет посылаться.
Но хочется всё-таки разобраться, в чём проблема, буферы может какие-то недостаточно большие или ещё что-то.

FRider

Приложение(сервер) завершает асинхронную операцию, но сообщение получателю(клиенту) не доставляется.
завершение вызова send, неважно, синхронного или нет, говорит только о том, что данные отданы операционке на отправку, а не о том, что данные доставленны удаленному клиенту.
Если сообщения мелкие - глянь в сторону отключения алгоритма Нагля.

Phoenix

В принципе, это предусмотрено и стандартом TCP, и документацией.

:)
нагля нету.
Я пока склоняюсь к тому, что либо какие-то буферы переполняются( и часть данных просто откидываются) и данные просто отбрасываются на каком-то ethernet-уровне, либо это баг где-то.

FRider

так это, отбрасываются или все же доставляются, но потом, после отправки других сообщений? :)

Phoenix

я предполагаю, что на низком уровне отбрасывается (условно ethernet)
Но на уровне tcp, данные ещё в буфере есть, Помечены как отправленные, но не подтверждённые.
потом приходит от клиента сообщение, что пропущен сегмент и tcp перепосылает эти данные.

Phoenix

 

C:\Documents and Settings\Администратор>time /t
12:33

C:\Documents and Settings\Администратор>netstat -n -s -p tcp | grep "="
Активных открыто = 868
Пассивных открыто = 36
Сбоев при подключении = 0
Сброшено подключений = 579
Текущих подключений = 25
Получено сегментов = 923719
Отправлено сегментов = 745705
Повторно отправлено сегментов = 958

C:\Documents and Settings\Администратор>time /t
12:33

C:\Documents and Settings\Администратор>time /t
12:41

C:\Documents and Settings\Администратор>netstat -n -s -p tcp | grep "="
Активных открыто = 868
Пассивных открыто = 36
Сбоев при подключении = 0
Сброшено подключений = 579
Текущих подключений = 25
Получено сегментов = 970735
Отправлено сегментов = 783803
Повторно отправлено сегментов = 1003

C:\Documents and Settings\Администратор>time /t
12:41


за 8 минут 45 перепосылов.
хотя retransmitted/sent = 45/38098 < 0.1%
мда.. кажись ничего экстраординарного.

kokoc88

Приложение(сервер) завершает асинхронную операцию, но сообщение получателю(клиенту) не доставляется.
[telepat]
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
[/telepat]

Phoenix

да стоит это всё. да и потом от нагла 30секундной задержки не может быть.

Marinavo_0507

TCP так себя вести не должно, скорее где-то на более высоком уровне не сбрасывается буфер.

kokoc88

да стоит это всё.
Значит есть вероятность, что у тебя ошибка в коде.

Phoenix

На уровне приложения, здесь никаких flush нету. по завершении операции вызывается callback.
а как должно себя вести? UPD: в случае потери пакета в смысле.

Phoenix

Где тут можно ошибиться-то?
вот это сообщение -> Log.LogLine("send-recv = {0} mks", Log.stop_from_start(token.start_time;
выводится, когда сервер посылает. а клиент при этом ничего не получает.
В доках ничего не говорится о том, что complited вызывается, когда только часть сообщений ушла, поэтому предполагаю, что оно вызывается только тогда, когда отправилось всё. (да и странно бы это было).

3493 private void OnIOCompleted(object sender, SocketAsyncEventArgs e)
3494 {
3495 // Determine which type of operation just completed and call the associated handler.
3496 switch (e.LastOperation)
3497 {
3498 case SocketAsyncOperation.Receive:
3499 //Console.WriteLine("Async recv done.");
3500 ProcessReceive(e);
3501 break;
3502 case SocketAsyncOperation.Send:
3503 //Console.WriteLine("Async send done.");
3504 ProcessSend(e);
3505 break;
3506 default:
3507 throw new ArgumentException("The last operation completed on the socket was not a receive or send");
3508 }
3509 }
3510

 
3552 private void ProcessSend(SocketAsyncEventArgs e)
3553 {
3554 if (e.SocketError == SocketError.Success)
3555 {
3556 UserToken token = (UserToken)e.UserToken;
3557
3558 Log.LogLine("send-recv = {0} mks", Log.stop_from_start(token.start_time;
3559
3560 //Log.LogLine("send ok.");
3561 argsPool.Push(e);
3562 }
3563 else
3564 {
3565 ProcessError(e);
3566 }
3567 }
3568

3649	        public void SendMessage(AppSocket appsock, Message msg)
3650 {
3651 SendLow(appsock.sock, msg.my_params_s, msg.msglen);
3652 }
3657
3658 private void SendLow(Socket sock, byte[] buf, int len)
3659 {
3660 SocketAsyncEventArgs e1 = argsPool.Pop;
3661 UserToken ut = new UserToken(sock, 0);
3662 e1.UserToken = ut;
3663 ut.set_time(Log.start;
3664
3665 if (e1 != null)
3666 {
3667 e1.SetBuffer(buf, 0, len);
3668 if (!(sock.SendAsync(e1
3669 {
3670 ProcessSend(e1);
3671 }
3672 }
3673 }
3678
3679 }

Phoenix

а это нормально ,что у двух разных соединений один SEQ ?

kokoc88

В доках ничего не говорится о том, что complited вызывается, когда только часть сообщений ушла, поэтому предполагаю, что оно вызывается только тогда, когда отправилось всё. (да и странно бы это было).
В доках сказано, что успешное завершение операции никак не связано с фактом доставки данных клиенту.
Где тут можно ошибиться-то?

Тут? Да где угодно:
3662	            e1.UserToken = ut;
3663 ut.set_time(Log.start;
3665 if (e1 != null)

Сейчас я всё ещё сомневаюсь в твоём коде. Попробуй написать полный/компилируемый пример, который воспроизводит твою проблему и запости его сюда. Меня очень волнует тема асинхронного ввода-вывода в .NET, так что я тоже поразбираюсь, если у меня воспроизведётся.

Phoenix

пишу вот сейчас. Думал, может это нормальное поведение, поэтому и спросил.
 На простых тестах это поймать автоматички не получалось. а руками очень долго нужно кнопок нажимать.
ПС: если до этого if дойдёт, то он всегда true будет. спасибо за багрепорт :)

Phoenix

Сейчас я всё ещё сомневаюсь в твоём коде

посмотрел снифером с двух сторон, всё код нормально отсылает и даже получает ACK.
Проблема в клиенте. Походу он там что-то кеширует при приёме.
спасибо всем! извиняюсь за беспокойство :o
Оставить комментарий
Имя или ник:
Комментарий: