Перейти к содержанию
    

Вопросы по TCP: delay ack и TCP Window Update

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

 

Есть прибор(arm7), отсылающий по tcp посылки удаленной системе(нетбук). Посылки достаточно объемны - 100 КБайт.

 

Т.к. софт для удаленной системе будет писаться на стороне заказчика, то проверяю скорость работы обычной терминалкой.

И что я вижу - каждый фрагмент tcp посылки передается раз в 200 мс.

 

Стал гуглить - оказалось в винде (и не только) есть такая штука, delay ack - т.е. винда отправляет ACK на посылку раз в 200 мс. Меня такое совсем не устраивает... Мои 100 Кбайт получается разбиваются на примерно 68 посылок и отправляются по 200 мс каждая - в сумме 14 секунд. Ужас.

 

Стал гуглить касаемо моего стека (взял кейловский). На кейловском форуме нашел решение - создать сокет с атрибутом TCP_TYPE_DELAY_ACK. Тогда каждая посылка будет разбиваться на две подряд и в этом случаи винда отвечает сразу.

 

После этих действий скорость заметно возросла, но... Но все равно не достаточно высоко - для меня это критично.

В WireShark во время передачи моей 100 кбайтной посылки иногда проскакивает после обычного TCP ACK от нетбука, еще какойто непонятный пакет - [TCP Window Update]. И это заметно отжирает время.

 

Собственно вопросы:

1) Можно ли настроить delay ack винды. Скажем 10 мсек вместо стандартных 200? И как?... :)

2) В програмирование для винды не силен, с сокетами не работал. Человек котоырй будет писать софт для моей системы, как то может сам отсылать ACK или же в винсокетах используется стандартный delay ack в 200 мсек? Он пишет на си шарпе.

3) Что такое [TCP Window Update] в сниффере и почему он происходит? Как от него избавиться?

 

Спасибо Всем кто откликнется!

Изменено пользователем Slowhan

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

а какая у Вас скорость должна быть?

выложите еще pcap file.

 

работу с сокетами в винде лучше делать на winapi. C# будет несщадно тормозить все это дело. тем более если Вам важна скорость, хотя смотря какой компьютер.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1) Можно ли настроить delay ack винды. Скажем 10 мсек вместо стандартных 200? И как?...

 

Но не нужно. Правильное решение - вменяемый стек, который правильно работает с Delayed Ack. Между прочим, решение по разбивке пакета пополам - оно паллиативно до жути и работает только в быстрой сети.

 

2) В програмирование для винды не силен, с сокетами не работал. Человек котоырй будет писать софт для моей системы, как то может сам отсылать ACK или же в винсокетах используется стандартный delay ack в 200 мсек? Он пишет на си шарпе.

 

Аллах до делов велик, что не позволил всем кому не попадя крутить подтверждения TCP (да и вообще всякие параметры стека). Трогать его не надо, это я уже говорил выше.

 

3) Что такое [TCP Window Update] в сниффере и почему он происходит? Как от него избавиться?

 

Ну я так понимаю, у Вас терминалка тормозит. Посмотрите внимательно, как меняется поле окна в пакетах подтверждения от большого брата. Видимо оно уменьшается до 0, потом, когда терминалка выводит все и выгребает следующую порцию данных, происходит уведомление передающей стороны о появлении места в окне пакетом TCP Window Update.

 

Эта проблема уйдет сама, когда у Вас будет вменяемый быстрый код на большом брате, а не консолька.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Скорость хотелось бы 2-3 Мбайта в секунду. Реально ли это на кейловском стеке? По тестам терминалки только около мегабайта...

 

Между прочим, решение по разбивке пакета пополам - оно паллиативно до жути и работает только в быстрой сети.

Там не пополам, там разбивается на 1514 байт и 60. Вроде так и делают умные люди?) просто по мне на отправку второго, пусть и маленького пакета уходит ценное время и проще исправить настройку стека, чтобы отвечал ACK на каждый пакет... Хотя может я заблуждаюсь...

 

Аллах до делов велик, что не позволил всем кому не попадя крутить подтверждения TCP (да и вообще всякие параметры стека). Трогать его не надо, это я уже говорил выше.

А как там в общем происходит работа? ВинСокет как-то понимает что пришел самый последний кусочек большой TCP посылки или это делается по таймауту у "большого брата"?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ВинСокет как-то понимает что пришел самый последний кусочек большой TCP посылки

 

Подтверждается каждый второй пакет, а если новые пакеты не приходят, то через 200мс. Вменяемый стек, имея данные для передачи, должен начинать эту передачу не ожидая подтверждения.

 

Реально ли это на кейловском стеке? По тестам терминалки только около мегабайта...

 

Реально. Замените терминалку на самонаписанное ПО.

 

Ну хотя-бы вот так (пример на C++ Builder):

//---------------------------------------------------------------------------

#include <vcl.h>
#include <winsock2.h>
#pragma hdrstop

#include "main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;



//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
       : TForm(Owner)
{
WSADATA		WSAData;
if(WSAStartup(MAKEWORD(2,0), &WSAData) != 0)
{
               ShowMessage("Error: WSAStartup failed!");
}
}
//---------------------------------------------------------------------------
#define SAMPLE_RATE (130208UL)
static const unsigned char wav_hdr[]={
	0x52,0x49,0x46,0x46,0x00,0x00,0x00,0x00,0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20,
	0x10,0x00,0x00,0x00,0x01,0x00,0x02,0x00,(SAMPLE_RATE>>0)&0xFF,(SAMPLE_RATE>>8)&0xFF,(SAMPLE_RATE>>16)&0xFF,0x00,0xC0,0x27,0x09,0x00,
	0x08,0x00,0x20,0x00,0x64,0x61,0x74,0x61,0x00,0x00,0x00,0x00
};

HANDLE fout;
DWORD total_size;
SOCKET ADCsock;

void __fastcall TForm1::StartBTClick(TObject *Sender)
{
       struct hostent * he;
       struct sockaddr_in addr;
       if ((he=gethostbyname("usm.rov"))==NULL)
       {
               ShowMessage("Error: gethostbyname failed!");
               return;
       }
       if (he->h_addr==NULL)
       {
               ShowMessage("Error: Host not found!");
               return;
       }
       memset(&addr, 0, sizeof(addr));
       memcpy(&(addr.sin_addr.s_addr), he->h_addr, sizeof(addr.sin_addr.s_addr));
       addr.sin_port=htons(2000);
       addr.sin_family=PF_INET;
       if ((ADCsock=socket(PF_INET, SOCK_STREAM, 0))==INVALID_SOCKET)
       {
               ShowMessage("Error: Can't create socket!");
               return;
       }
       {
               DWORD v_ctl=1;
               ioctlsocket(ADCsock,FIONBIO,&v_ctl); //Неблокирующийся режим
       }
       if (connect(ADCsock,(const struct sockaddr *)&addr, sizeof(addr)))
       {
               int err=WSAGetLastError();
               if (err!=WSAEWOULDBLOCK)
               {
                       closesocket(ADCsock);
                       ShowMessage("Error: Connect failed!");
                       return;
               }
               fd_set rfds; //Ожидающие данных (или ответ, или закрытие)
               fd_set wfds; //Ожидающие подключения (готовность писать)
               fd_set efds; //Ожидающие подключения влетают сюда при ошибке
               struct timeval tv;
               tv.tv_sec=3; //Дольше 3х секунд не ждем, нефиг
               tv.tv_usec=0;
               FD_ZERO(&rfds);
               FD_ZERO(&wfds);
               FD_ZERO(&efds);
               FD_SET(ADCsock,&wfds);
               FD_SET(ADCsock,&efds);
               int i=select(0,NULL,&wfds,&efds,&tv);
               if (i!=0)
               {
                       if (FD_ISSET(ADCsock,&wfds))
                       {
                               //соединились
                               goto L1;


                       }
               }
               //Ахтунг, короче
               closesocket(ADCsock);
               ShowMessage("Error: Can't connect!");
               return;
       }
L1:
       DWORD br;
fout = CreateFile("adc_data.wav",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL, 0);
WriteFile(fout,wav_hdr,sizeof(wav_hdr),&br,NULL);
       total_size=0;
       Timer1->Enabled=true;
       LblReceivedBytes->Caption="";
}
//---------------------------------------------------------------------------

void __fastcall TForm1::StopBTClick(TObject *Sender)
{
       if (!Timer1->Enabled) return;
       Timer1->Enabled=false;
       shutdown(ADCsock, SD_BOTH);
       closesocket(ADCsock);
       DWORD br=0;
       if (total_size&4)
       {
       	WriteFile(fout,&br,4,&br,NULL);
       }
//WriteFile(fout,RE_ARR,total_bytes,&br,NULL);
SetFilePointer(fout,0x28,NULL,FILE_BEGIN);
WriteFile(fout,&total_size,4,&br,NULL);
SetFilePointer(fout,0x04,NULL,FILE_BEGIN);
total_size+=0x24;
WriteFile(fout,&total_size,4,&br,NULL);
CloseHandle(fout);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::ADCsockError(TObject *Sender,
     TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
{
       ShowMessage("Errorcode: "+ErrorCode);

}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
       int l;
       String s;
       do
       {
               char p[8192];
               l=recv(ADCsock,p,8192,0);
               if (l<0)
               {
                       int err=WSAGetLastError();
                       if (err!=WSAEWOULDBLOCK)
                       {
                               s="Error: recv() return error ";
                               s+=err;
                               s+="!";
                               ShowMessage(s);
                       }
               }
               else
                       if (l>0)
                       {
                               DWORD br;
                               WriteFile(fout,p,l,&br,NULL);
                               total_size+=l;
                       }
       }
       while(l>0);
       s="Received ";
       s+=total_size;
       s+="bytes...";
       LblReceivedBytes->Caption=s;
}
//---------------------------------------------------------------------------

 

Тут чтение выполняется по таймеру, дабы не морочиться с ожиданиями и прочим. Можно просто вызывать Timer1Timer в вечном цикле.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вменяемый стек, имея данные для передачи, должен начинать эту передачу не ожидая подтверждения.

Вот тут не понял, что Вы имеете ввиду. Какой же это вменяемый стек, если он без подтверждения работает. Тогда UDP какое-то получается.

 

За пример кода большое спасибо, буду разбираться!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вот тут не понял, что Вы имеете ввиду.

 

Подтверждение не надо ждать принципиально, потом придет. А новые данные надо сразу передавать. Именно за счет этого будет скорость.

 

А если вдруг пакет в потоке потеряется, то есть два метода быстрого восстановления - Fast Retransmit (попроще) и Selective ACK (посложнее). Рекомендую накормить гугль этими словами и плотно раскурить что и как.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подтверждение не надо ждать принципиально, потом придет. А новые данные надо сразу передавать. Именно за счет этого будет скорость.

 

А если вдруг пакет в потоке потеряется, то есть два метода быстрого восстановления - Fast Retransmit (попроще) и Selective ACK (посложнее). Рекомендую накормить гугль этими словами и плотно раскурить что и как.

Гугл, конечно, накормлю, но у кейловского TCP такая реализация функции send_data, что она не будет работать, если не пришел ACK после предыдущей посылки. Можно при создании сокета выставить упомянутый мной флаг и тогда внутри функции send_data кеил делит пакет на 2 - 1510 и 60 байт. Т.е. скорость таки теряется в этой реализации... Собственно вот подтверждение моих слов - http://www.keil.com/forum/19008/

 

Если не секрет какие стеки Вы используете? Правда особо ковырять и портировать время не позволяет сейчас - но на будущее буду иметь ввиду.

Изменено пользователем Slowhan

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

но у кейловского TCP такая реализация

 

Ну так а кто Вас заставлял брать этот стек? Теперь все, ойц.

 

Если не секрет какие стеки Вы используете?

 

Я использую свой собственный. Вам посоветую смотреть в сторону lwip - он хоть и здоровый, но куда более адекватен. Уважаемый zltigo рекомендует смотреть в сторону TCP-стека от TN-kernel - там, вроде бы, на основе классического кода от bsd-sockets сделано все.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Ну так а кто Вас заставлял брать этот стек? Теперь все, ойц.

Соблазнителен больно - и документирован, и сразу поддержка компилятора. А тот же lwip прикручивать и прикручивать, адаптировать и адаптировать ибо готового проекта для моего lpc2368 неть. Хотя полезно, конечно, при наличии времени. =)

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

Изменено пользователем Slowhan

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Соблазнителен больно - и документирован, и сразу поддержка компилятора.

 

Только вот геморрой в комплекте забыли прорекламировать ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...