Конференция "Сети" » TServerSocket/TClientSocket SendText [D7]
 
  • Андрей (29.10.08 14:36) [0]
    Уважаемые, не подскажете, от чего может зависеть скорость передачи/приема пакетов? При тесте на локальной машине все летает, а как только подключаю в локальную сеть, так начинаются задержки отправки/приема (приложение работает по принципу: клиент отослал на сервер пакет, тот его разослал всем подключившимся, в том числе и тому, кто отослал). На сеть грешить сложно, т.к. обычные чаты в ней работают практически без задержек. Есть различие в скорости на чем делать клиента/сервер синхронный/асинхронный режимы?
  • Сергей М. © (29.10.08 14:49) [1]

    > начинаются задержки отправки/приема


    В чем и как это выражается ?


    > Есть различие в скорости на чем делать клиента/сервер синхронный/асинхронный
    > режимы?


    Принципиальных различий нет.
  • Сергей М. © (29.10.08 14:56) [2]

    > тот его разослал всем подключившимся


    Если сервер однопоточный, то при росте числа активных клиентов рост задержки будет неминуем.
  • tesseract © (29.10.08 20:04) [3]

    >  клиент отослал на сервер пакет, тот его разослал всем подключившимся,
    >  в том числе и тому, кто отослал)


    Так правильно, ты сеть по геометрической прогрессии загружаешь. Каждый подключённый клиент это увеличение трафика на КолКлиентов*2.  И Если ты работаешь не по UDP - начнуться проблемы.


    > Принципиальных различий нет.


    Огромные ! Асинхронный режим это чисто придумка MS  - там прослойка (не спорю написана хорошо) над чисто синхронными сокетами.
  • Сергей М. © (29.10.08 20:21) [4]

    > tesseract ©   (29.10.08 20:04) [3]


    > Огромные

    Да не трынди)


    > Асинхронный режим это чисто придумка MS


    Ты не любишь кошек ? Ну тогда и не советуй как их готовить !)
  • Сергей М. © (30.10.08 08:11) [5]

    > tesseract ©   (29.10.08 20:04) [3]


    Извини за резкость
  • Андрей (31.10.08 20:28) [6]

    >В чем и как это выражается?


    Подключены 2 клиента. Задержка между отправкой пакета и приходом его же отправляющему клиенту несколько секунд.
  • Сергей М. © (01.11.08 08:25) [7]

    > Андрей   (31.10.08 20:28) [6]


    Думаю что у тебя ошибка в программе.
  • Андрей (01.11.08 14:46) [8]
    Если не трудно, посмотрите, где может скрываться эта ошибка :)
    Вроде все просто и на скорость передачи здесь ничего влиять не должно?

    Const
     cDelitel : String = '#end#';


    procedure ClientDataProc(Socket:TCustomWinSocket; s:string);
    begin
    if Copy(s,1,2) = '#M' then begin
     Delete(s,1,2);
     ChatMemo.Lines.Add('['+TimeToStr(Time)+']  '+ Copy(s,1,Pos(';',s)-1)+'> '+
               Copy( s, Pos(';',s)+1, Length(s)-Pos(';',s) ) );
     Exit;
    end;
    end;

    procedure TfrmMy.sClientSocketRead(Sender: TObject; Socket: TCustomWinSocket);
    var
     s : string;
     i : integer;
    begin
     s:=Socket.ReceiveText; // присваиваем s полученную строку из Socket
     if ClientBuf<>''
       then ClientBuf := ClientBuf+s  // в буфере был кусок какой-то стороки
       else ClientBuf := s;
     i:=0;
     repeat // разделяем строку, если одновременно пришло несколько
       i:=Pos(cDelitel,ClientBuf);   // ищем раздлелитель строк в буфере
       if i<>0 then begin   // найден разделитель строк
         s:=Copy(ClientBuf,1,i-1); // копируем одну пришедшую строку в s
         Delete(ClientBuf,1,i+Length(cDelitel)-1); // удаляем из буфера уже скопированную строку
         ClientDataProc(sClientSocket.Socket,s) // обрабатываем выделенную строку
       end;
     until i=0; // повторять, пока будет не найден разделитель строк
    end;

    procedure TfrmMy.TextEditKeyDown(Sender: TObject; var Key: Word;
             Shift: TShiftState);
    var
     s:  string;
     i:  integer;
    begin
     if Key = VK_RETURN then begin  // нажата клавиша Enter
       s := '#M'+NickName+';'+TextEdit.Text; // Добавляем наше имя (от кого) и само сообщение
       If sServerSocket.Active=True then  // проверка, в каком режиме находится программа
         For i:=0 to sServerSocket.Socket.ActiveConnections-1 do  // отправляем сообщение с сервера всем пользователям
           sServerSocket.Socket.Connections[i].SendText(s)
       else  // отправляем сообщение с клиента
         sClientSocket.Socket.SendText(s);
       TextEdit.Clear;  // очищаем TextEdit
     end;
     ActiveControl := TextEdit;
    end;

    procedure TfrmMy.sServerSocketClientRead(Sender: TObject;
             Socket: TCustomWinSocket);
    var
     i     : Integer;
    begin
     if Copy(s,1,2) = '#M' then begin  // обработка сервером
       for i := 0 to sServerSocket.Socket.ActiveConnections-1 do
         sServerSocket.Socket.Connections[i].SendText(s+cDelitel);
       Exit;
     end;
    end;

  • Сергей М. © (01.11.08 15:41) [9]
    Явных несуразностей пока не видно, хотя код и требует некоторых довольно важных доработок/переработок.

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

    http://www.google.ru/search?hl=ru&newwindow=1&client=firefox-a&rls=org.mozilla%3Aru%3Aofficial&hs=6Gv&q=Nagle+SetSockOpt++TCP_NODELAY&btnG=%D0%9F%D0%BE%D0%B8%D1%81%D0%BA&lr=lang_ru&aq=f&oq=
  • Андрей (01.11.08 16:13) [10]
    Почитаю на досуге, попробую разобраться :)
    А что за
    >  довольно важных доработок/переработок

    требует код? Просветите, пожалуйста.
  • Сергей М. © (01.11.08 16:59) [11]

    > Просветите, пожалуйста.


    Вот тебе информация к размышлению на досуге :

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

    Ты этот результат игнорируешь, следовательно, в произвольных "боевых" условиях твой алгоритм рано или поздно даст сбой.
  • Андрей (01.11.08 23:06) [12]
    Вставил на сервере и клиенте в обработку приема и отсылки сообщений
    вывод на экран TimeToStr(Time):
    Задержка происходит на сервере между приемом строки и отправкой ее по клиентам.
    Вопрос - почему и как это победить? Если это алгоритм Нагеля, то как его отключить в Делфи?

    procedure TfrmMy.sServerSocketClientRead(Sender: TObject;
            Socket: TCustomWinSocket);
    var
    i     : Integer;
    begin
    s:=Socket.ReceiveText;
    Memo1.Lines.Add('['+TimeToStr(Time)+']  получил сообщение от клиента: '+s);
    if Copy(s,1,2) = '#M' then begin  // обработка сервером
      for i := 0 to sServerSocket.Socket.ActiveConnections-1 do
        sServerSocket.Socket.Connections[i].SendText(s+cDelitel);
      Memo1.Lines.Add('['+TimeToStr(Time)+']  рассылаю сообщения клиентам ');
      Exit;
    end;
    end;

  • Андрей (01.11.08 23:34) [13]
    Если опция TCP_NODELAY установлена с помощью setsockopt, то она действует в пределах только указанного сокета или это глобальное изменение настроек конкретного компа для работы с сетью?
  • Андрей (02.11.08 00:03) [14]
    Похоже глупость сморозил в предыдущем вопросе :) . Где можно почитать про все параметры setsockopt? Желательно на русском, но можно и на английском, если на русском нет :)
  • Андрей (02.11.08 14:54) [15]

    procedure Tfrm1.sServerSocketClientRead(Sender: TObject;
             Socket: TCustomWinSocket);
    var
     s, from_, to_, color_, time_, temp_ : string;
     game_   : string[8];
     i,j     : Integer;
    begin
     Memo1.Lines.Add('['+TimeToStr(Time)+'] handle='+IntToStr(Socket.SocketHandle));
     j:=sizeof(i);
     GetSockOpt(Socket.SocketHandle,SOL_SOCKET,SO_SNDBUF,@i,j);
     Memo1.Lines.Add('['+TimeToStr(Time)+'] SO_SNDBUF='+IntToStr(i));
     GetSockOpt(Socket.SocketHandle,IPPROTO_TCP,TCP_NODELAY,@i,j);
     Memo1.Lines.Add('['+TimeToStr(Time)+'] NO_DELAY установлен и ='+IntToStr(i));
     s := Socket.ReceiveText;
     Memo1.Lines.Add('['+TimeToStr(Time)+']  получил сообщение от клиента: '+s);

    if (Copy(s,1,2) = '#M')then begin
       Memo1.Lines.Add('['+TimeToStr(Time)+'] вошли в процедуру #M');
       for i := 0 to sServerSocket.Socket.ActiveConnections-1 do
         sServerSocket.Socket.Connections[i].SendText(s+cDelitel);
       Delete(s,1,2);
       Memo1.Lines.Add('['+TimeToStr(Time)+']  рассылаю сообщения клиентам '+ Copy(s,1,Pos(';',s)-1)+'> '+Copy( s, Pos(';',s)+1, Length(s)-Pos(';',s) ) );
       Exit;
     end;

    end;

    procedure Tfrm1.sServerSocketGetSocket(Sender: TObject; Socket: Integer;
     var ClientSocket: TServerClientWinSocket);
    var
     i, len : integer;
    begin
     len:=sizeof(i);
     i:=0;
     SetSockOpt(Socket,SOL_SOCKET,SO_SNDBUF,@i,len);
     i:=1;
     SetSockOpt(Socket,IPPROTO_TCP,TCP_NODELAY,@i,sizeof(i));
    end;



    в результате получаю
    на сервере:

    [14:33:11] handle=272
    [14:33:11] SO_SNDBUF=0
    [14:33:11] NO_DELAY установлен и =1
    здесь идет задержка и потом вываливаются сразу оставшиеся строки
    [14:33:11]  получил сообщение от клиента: #Mпервое сообщение      
    [14:33:15] вошли в процедуру #M
    [14:33:15]  рассылаю сообщения клиентам> первое сообщение


    у клиента:

    [14:33:11]  посылаю сообщение на сервер: первое сообщение
    [14:33:11]  отослал
    [14:33:15]  получил сообщение от сервера: #Mпервое сообщение#end#
    [14:33:15] сообщение с сервера после оработки>первое сообщение



    Т.е. буфер отсылки сокета обнулен, TCP_NODELAY отключена, а задержка на сервере между ReceiveText и отсылкой по клиентам составляет 4с
    У меня уже мозги скоро закипят :) помогите, плиз, укажите, где у меня ошибка.
  • Сергей М. © (02.11.08 18:05) [16]
    Отключать алгоритм Нагеля и на стороне клиента и на стороне сервера следует сразу после установки соединения.
  • Сергей М. © (02.11.08 18:10) [17]

    >  i:=0;
    >  SetSockOpt(Socket,SOL_SOCKET,SO_SNDBUF,@i,len);


    Это лишнее
  • Андрей (02.11.08 19:25) [18]

    > Отключать алгоритм Нагеля и на стороне клиента и на стороне
    > сервера следует сразу после установки соединения.

    Так со стороны клиента, вроде, никаких задержек нет. Сервер начинает обрабатыват событие OnClientRead в ту же секунду, в какую клиент отсылает сообщение. Задержка начинается при попытке прочесть данные с помощью ReceiveText. Как раз между первым и вторым выводом в Memo сообщения.

    Memo1.Lines.Add('['+TimeToStr(Time)+'] NO_DELAY установлен и ='+IntToStr(i));
    // здесь секунды клиента и сервера совпадают
    s := Socket.ReceiveText;
    // а здесь уже разница в 4-5с
    Memo1.Lines.Add('['+TimeToStr(Time)+']  получил сообщение от клиента: '+s);


    Вставил отключение Нагеля на событие ClientSocketConnect результат тот же :(

    И еще одна странность: вижу, что после начала выполнения оператора  ReceiveText идет фактическая задержка, однако строки

    [14:33:11]  получил сообщение от клиента: #Mпервое сообщение      
    [14:33:15] вошли в процедуру #M


    вываливаются одновременно в [14:33:15]


    > SetSockOpt(Socket,SOL_SOCKET,SO_SNDBUF,@i,len);

    это я вставил на всякий случай, т.к. везде (где я нашел описания параметров сокета) сказано, что, если буфер отправки будет = 0, то кэшироваться сообщения не будут, а будут отправляться сразу.
  • Сергей М. © (02.11.08 20:58) [19]
    Не верю.

    ReceiveText в контексте этого обработчика должна выполниться практически мгновенно.
  • Андрей (02.11.08 21:11) [20]
    Вот ... :(
    3 дня промучался. Сейчас ради эксперимента убрал на сервере
    s := Socket.ReceiveText;

    и вставил его в отображение Memo.
    Откликнулся мгновенно. Поставил эту строку назад и все летает.
    Что было так и не понял. Наверное, какой-то глюк.
    Завтра тестить буду. Спасибо за отклики.
  • Тын-Дын © (06.11.08 19:45) [21]
    Сомнения вызывает код чтения пришедших данных.
    Более того - в нём ошибка.
    Правильный алгоритм такой:

    var
     s: String;
    begin
     s := '';
     while Socket.ReceiveLength>0 do s := s+Socket.ReceiveText;
    ...

  • Сергей М. © (06.11.08 20:11) [22]

    > в нём ошибка


    Нет в нем в этом смысле ошибки.
    Данные не потеряются, так что цикл вовсе не обязателен.
  • Тын-Дын © (06.11.08 20:21) [23]

    > Сергей М. ©   (06.11.08 20:11) [22]
    >
    > > в нём ошибка
    >
    >
    > Нет в нем в этом смысле ошибки.
    > Данные не потеряются, так что цикл вовсе не обязателен.


    Судя по постам выше, у автора на это может быть логика завязана.
  • Сергей М. © (06.11.08 20:28) [24]
    Логика хоть и неважнецкая, но с этим все в порядке - получил шматок текста, дописал в конец стат.буфера, тут же обработал тек.содержимое буфера с должной его коррекцией (если таковая требуется) после обработки.
  • Тын-Дын © (06.11.08 20:35) [25]

    > Андрей   (29.10.08 14:36)  


    А на какой операционной системе проверяешь?
  • Сергей М. © (06.11.08 21:04) [26]

    > Тын-Дын ©   (06.11.08 20:35) [25]


    А по барабану на какой.

    WSA 1.01 работает везде одинаково
 
Конференция "Сети" » TServerSocket/TClientSocket SendText [D7]
Есть новые Нет новых   [134433   +21][b:0][p:0.005]