C#-сокет сервер. потеря соообщений, при доставке клиенту.
Приложение(сервер) завершает асинхронную операцию, но сообщение получателю(клиенту) не доставляется.завершение вызова send, неважно, синхронного или нет, говорит только о том, что данные отданы операционке на отправку, а не о том, что данные доставленны удаленному клиенту.
Если сообщения мелкие - глянь в сторону отключения алгоритма Нагля.
В принципе, это предусмотрено и стандартом TCP, и документацией.
нагля нету.
Я пока склоняюсь к тому, что либо какие-то буферы переполняются( и часть данных просто откидываются) и данные просто отбрасываются на каком-то ethernet-уровне, либо это баг где-то.
так это, отбрасываются или все же доставляются, но потом, после отправки других сообщений?
Но на уровне tcp, данные ещё в буфере есть, Помечены как отправленные, но не подтверждённые.
потом приходит от клиента сообщение, что пропущен сегмент и tcp перепосылает эти данные.
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%
мда.. кажись ничего экстраординарного.
Приложение(сервер) завершает асинхронную операцию, но сообщение получателю(клиенту) не доставляется.[telepat]
socket.SetSocketOption(SocketOptionLevel.Tcp, SocketOptionName.NoDelay, true);
[/telepat]
да стоит это всё. да и потом от нагла 30секундной задержки не может быть.
TCP так себя вести не должно, скорее где-то на более высоком уровне не сбрасывается буфер.
да стоит это всё.Значит есть вероятность, что у тебя ошибка в коде.
а как должно себя вести? UPD: в случае потери пакета в смысле.
вот это сообщение -> 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 }
В доках ничего не говорится о том, что complited вызывается, когда только часть сообщений ушла, поэтому предполагаю, что оно вызывается только тогда, когда отправилось всё. (да и странно бы это было).В доках сказано, что успешное завершение операции никак не связано с фактом доставки данных клиенту.
Где тут можно ошибиться-то?
Тут? Да где угодно:
3662 e1.UserToken = ut;
3663 ut.set_time(Log.start;
3665 if (e1 != null)
Сейчас я всё ещё сомневаюсь в твоём коде. Попробуй написать полный/компилируемый пример, который воспроизводит твою проблему и запости его сюда. Меня очень волнует тема асинхронного ввода-вывода в .NET, так что я тоже поразбираюсь, если у меня воспроизведётся.
На простых тестах это поймать автоматички не получалось. а руками очень долго нужно кнопок нажимать.
ПС: если до этого if дойдёт, то он всегда true будет. спасибо за багрепорт
Сейчас я всё ещё сомневаюсь в твоём коде
посмотрел снифером с двух сторон, всё код нормально отсылает и даже получает ACK.
Проблема в клиенте. Походу он там что-то кеширует при приёме.
спасибо всем! извиняюсь за беспокойство
Оставить комментарий
Phoenix
Есть приложение на c#, написано с использованием SocketAsyncEventArgs(есть ощущение, что технология не так важна, хотя могу ошибаться). крутится в VBox'e. Хотя эффект я смог повторить на десктопной винде.Приложение(сервер) завершает асинхронную операцию, но сообщение получателю(клиенту) не доставляется. (нагл отключён)
и "ничего не происходит" достаточно долго(минуту где-то, потом мне ждать надоедает).
Данные доставляются, если сервер снова посылает какие-то данные.
В принципе, это предусмотрено и стандартом TCP, и документацией. Мало ли, пакетик потерялся. Но, эффект был обнаружен на локальной винде. оба приложения на ней.
и freebsd- windows(vbox на той же freebsd). На последней появляется раз в 2-3 минуты. Трафик не сказать, чтобы какой-то огромный.
Раньше, когда рассылались ещё и дебажные сообщений(и трафика было больше) эффект не был замечен, т.к. следующий пакет проталкивал предыдущие.
Пока склоняюсь к тому, чтобы в протокол обмена ввести heartbeat сообщение, которое каждые 2-3 секунды будет посылаться.
Но хочется всё-таки разобраться, в чём проблема, буферы может какие-то недостаточно большие или ещё что-то.