flush на TCP-сокет
типа man tcp
TCP_NODELAY и TCP_CORK
TCP_NODELAY и TCP_CORK
Если я тебя правильно понял, ты имеешь в виду опции SO_RCVLOWAT и SO_SNDLOWAT.
Под Линуксом всегда стоит единичка (1 байт т. е. ты можешь быть уверен что данные пойдут немедленно на интерфейс.
ЗЫ: это моё личное мнение после чтения man 7 socket. я могу пиздеть, но вроде тут всё очевидно.
ЗЗЫ: может тебе хочется получать информацию когда пакет точно ушел? думаю придется писать модуль ядра
Под Линуксом всегда стоит единичка (1 байт т. е. ты можешь быть уверен что данные пойдут немедленно на интерфейс.
ЗЫ: это моё личное мнение после чтения man 7 socket. я могу пиздеть, но вроде тут всё очевидно.
ЗЗЫ: может тебе хочется получать информацию когда пакет точно ушел? думаю придется писать модуль ядра

TCP_NODELAY стоит, но не помогает (?).
Что говорит strace:
А вот что показывает tcpdump:
Сначала посылается нормально (37, 45, 45, 49). Затем ожидаем ~38 мсек (? и потом посылаем сразу пачкой: 340 = 125 + 45 + 125 + 45. Почему так?
Сдается мне, какой-то у меня ламеризм. Надо читать "TCP/IP для самых маленьких". Сильно не бейте, плз.
Что говорит strace:
1156451686.802517 accept(8, 0, NULL) = 10
1156451686.802842 fcntl64(10, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
1156451686.802916 setsockopt(10, SOL_TCP, TCP_NODELAY, [1], 4) = 0
1156451686.802981 getsockopt(10, SOL_SOCKET, SO_SNDBUF, [16384], [4]) = 0
1156451686.803044 setsockopt(10, SOL_SOCKET, SO_SNDBUF, [65560], 4) = 0
1156451686.803110 getsockopt(10, SOL_SOCKET, SO_RCVBUF, [87380], [4]) = 0
....
1156451686.811931 writev(10, [{"\r\0\0"..., 24}, {"t\354\261"..., 13}], 2) = 37
1156451686.813345 writev(10, [{"\25\0\0"..., 24}, {"1\213T"..., 21}], 2) = 45
1156451686.814442 writev(10, [{"\25\0\0"..., 24}, {"1\213T"..., 21}], 2) = 45
1156451686.815047 writev(10, [{"\31\0\0"..., 24}, {"\335\304"..., 25}], 2) = 49
1156451686.815536 writev(10, [{"e\0\0\0"..., 24}, {"N\256\327"..., 101}], 2) = 125
1156451686.815906 writev(10, [{"\25\0\0"..., 24}, {"1\213T"..., 21}], 2) = 45
1156451686.816346 writev(10, [{"e\0\0\0"..., 24}, {"N\256\327"..., 101}], 2) = 125
1156451686.816742 writev(10, [{"\25\0\0"..., 24}, {"1\213T"..., 21}], 2) = 45
1156451687.318626 writev(10, [{"\25\0\0"..., 24}, {"1\213T"..., 21}], 2) = 45
А вот что показывает tcpdump:
00:34:46.812002 IP X > Y: P 163:200(37) ack 297 win 429 <nop,nop,timestamp 786145347 1230635927>
00:34:46.813431 IP X > Y: P 200:245(45) ack 342 win 429 <nop,nop,timestamp 786145347 1230635929>
00:34:46.814534 IP X > Y: P 245:290(45) ack 342 win 429 <nop,nop,timestamp 786145347 1230635929>
00:34:46.815155 IP X > Y: P 290:339(49) ack 342 win 429 <nop,nop,timestamp 786145348 1230635929>
00:34:46.853104 IP X > Y: P 339:679(340) ack 342 win 429 <nop,nop,timestamp 786145357 1230635970>
00:34:46.853879 IP X > Y: P 679:724(45) ack 387 win 429 <nop,nop,timestamp 786145357 1230635970>
00:34:46.892052 IP X > Y: . ack 432 win 429 <nop,nop,timestamp 786145367 1230635971>
Сначала посылается нормально (37, 45, 45, 49). Затем ожидаем ~38 мсек (? и потом посылаем сразу пачкой: 340 = 125 + 45 + 125 + 45. Почему так?
Сдается мне, какой-то у меня ламеризм. Надо читать "TCP/IP для самых маленьких". Сильно не бейте, плз.
Более точно, это место выглядит так:
Отправка пакета в 340 байт происходит сразу после получения "Y > X: . ack 339 win 1460". То есть оно ждет подтверждения? Как указать объем или количество пакетов, которые можно отсылать без подтверждения? Вроде SNDBUF и так 64 кб...
00:34:46.812002 IP X > Y: P 163:200(37) ack 297 win 429 <nop,nop,timestamp 786145347 1230635927>
00:34:46.812207 IP Y > X: P 297:342(45) ack 200 win 1460 <nop,nop,timestamp 1230635929 786145346>
00:34:46.813431 IP X > Y: P 200:245(45) ack 342 win 429 <nop,nop,timestamp 786145347 1230635929>
00:34:46.814534 IP X > Y: P 245:290(45) ack 342 win 429 <nop,nop,timestamp 786145347 1230635929>
00:34:46.815155 IP X > Y: P 290:339(49) ack 342 win 429 <nop,nop,timestamp 786145348 1230635929>
00:34:46.853045 IP Y > X: . ack 339 win 1460 <nop,nop,timestamp 1230635970 786145347>
00:34:46.853104 IP X > Y: P 339:679(340) ack 342 win 429 <nop,nop,timestamp 786145357 1230635970>
00:34:46.853226 IP Y > X: . ack 679 win 1728 <nop,nop,timestamp 1230635970 786145357>
Отправка пакета в 340 байт происходит сразу после получения "Y > X: . ack 339 win 1460". То есть оно ждет подтверждения? Как указать объем или количество пакетов, которые можно отсылать без подтверждения? Вроде SNDBUF и так 64 кб...
Всё правильно, TCP_NODELAY работает. TCP стек перестал слать, потому что было послано несколько пакетов и не поступило подтверждения с той стороны. Если бы подтверждения поступали без задержки, то все пакеты совпадали бы. Возможно, когда сессия разгонится, у неё устаканится окно и rtt, то этот эффект пропадёт. Если же сеть работает неравномерно, то не пропадёт.
afaik, TCP_ПРОБКА делает обратное и вообще это фантазия линукса, больше нигде этого нет.
> TCP стек перестал слать, потому что было послано несколько пакетов и не поступило подтверждения с той стороны.
Я считал, что пакеты посылаются пока не заполнится окно. В данном случае окно 1460 и послано значительно меньше, порядка пары сотен байт. Почему тогда не посылаются данные? Есть еще какой-то фактор, кроме размера окна (в байтах)? Как его можно регулировать?
Я считал, что пакеты посылаются пока не заполнится окно. В данном случае окно 1460 и послано значительно меньше, порядка пары сотен байт. Почему тогда не посылаются данные? Есть еще какой-то фактор, кроме размера окна (в байтах)? Как его можно регулировать?
Чёрт его знает... TCP стек линукса 2.6 может делать много того, про что в книжках не пишут. Возможно, для получения однозначного ответа на вопрос пришлось бы включать дебаг и разбираться.
Это соединение давно образовалось или это самое его начало?
Это соединение давно образовалось или это самое его начало?
> Это соединение давно образовалось или это самое его начало?
Самое начало. В ... всего порядка 10 пакетов (в сумме в обе стороны).
Самое начало. В ... всего порядка 10 пакетов (в сумме в обе стороны).
Надо спрашивать у отцов из
Если им дамп показать, они обычно быстро отвечают.
Во время ожидания ответа попытаться почитать исходник, может там есть комментарии.
Если им дамп показать, они обычно быстро отвечают.
Во время ожидания ответа попытаться почитать исходник, может там есть комментарии.
TCP_CORK можно включить, а потом выключить - получится flush
> Самое начало. В ... всего порядка 10 пакетов (в сумме в обе стороны).
Тогда оно может быть еще в состоянии slow start?
Тогда оно может быть еще в состоянии slow start?
up! До сих пор очень актуально.
Написали программу, которая это воспроизводит. Посылает в цикле несколько маленьких пакетов, затем спит 0.1 с. При корректной работе программы время должно быть порядка 10.0 с. На практике - порядка 14.0 с. С очень большой точностью воспроизводится задержка порядка 40 мс. Самое интересное, что даже не обязательно посылать по Ethernet, поведение такое же при работе через loopback. Если посылать ответы чаще (поменять константу DELIM с 10 на 1 или 2 то эффект исчезает.
Запуск сервера:
Запуск клиента:
Я посмотрел список linux-netdev, но там обсуждают более сложные вопросы, связанные с разработкой драйверов в ядре. Уместно ли послать туда такую программу? Может быть есть какие-то другие списки рассылки?
Написали программу, которая это воспроизводит. Посылает в цикле несколько маленьких пакетов, затем спит 0.1 с. При корректной работе программы время должно быть порядка 10.0 с. На практике - порядка 14.0 с. С очень большой точностью воспроизводится задержка порядка 40 мс. Самое интересное, что даже не обязательно посылать по Ethernet, поведение такое же при работе через loopback. Если посылать ответы чаще (поменять константу DELIM с 10 на 1 или 2 то эффект исчезает.
Запуск сервера:
$ ./a.out 1 5000
Server: begin send_all
Server: total time 14.216441
Запуск клиента:
$ ./a.out 2 5000 localhost
Client: connected to localhost:5000
Client: begin receive_all
Client: total time 14.223265
Я посмотрел список linux-netdev, но там обсуждают более сложные вопросы, связанные с разработкой драйверов в ядре. Уместно ли послать туда такую программу? Может быть есть какие-то другие списки рассылки?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <time.h>
int TOTAL_SENDS = 1000;
int DELIM = 10;
int sock = -1;
int init_server(int port)
{
struct sockaddr_in sin;
struct sockaddr_in new_sin;
int new_sock;
int val;
int sockaddrlen;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock == -1) return -1;
val = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val < 0) return -1;
memset(&sin,0,sizeof(struct sockaddr_in;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if (-1 == bind(sockstruct sockaddr*)&sin,sizeof(sin return -2;
if (-1 == listen(sock,1 return -3;
sockaddrlen = sizeof(struct sockaddr_in);
new_sock = accept(sockstruct sockaddr*)&new_sinsocklen_t*)&sockaddrlen);
if (new_sock == -1) return -4;
sock = new_sock;
val = 1;
if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val != 0) return -5;
return 0;
}
int init_client(char* hostname, int port)
{
int val;
int res;
struct sockaddr_in sin;
sock = socket(PF_INET, SOCK_STREAM, 0);
memset(&sin, 0, sizeof(sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
memcpy(&sin.sin_addr, gethostbyname(hostname)->h_addr, 4);
val = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val;
res = connect(sock, (struct sockaddr*)&sin, sizeof(sin;
printf("Client: connected to %s:%d\n", hostname, port);
if (res == -1) return -1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val;
return 0;
}
void send_all(unsigned long delay)
{
int i;
char buf[1024];
printf("Server: begin send_all\n");
for (i = 1; i < TOTAL_SENDS; ++i) {
write(sock, buf, 1);
if (i % DELIM == 0) read(sock, buf, 1);
if (i % 10 == 0) usleep(delay);
}
}
void receive_all(unsigned long delay)
{
int i;
char buf[1024];
printf("Client: begin receive_all\n");
for (i = 1; i < TOTAL_SENDS; ++i) {
read(sock, buf, 1);
if (i % DELIM == 0) write(sock, buf, 1);
if (i % 10 == 0) usleep(delay);
}
}
int main(int argc, char* argv[])
{
int port;
char* host;
int me;
struct timeval tv1, tv2;
double tt;
assert(argc > 2);
me = atoi(argv[1]);
switch (me) {
case 1:
port = atoi(argv[2]);
if (init_server(port {
printf("Server initialization failed!\n");
return 1;
}
gettimeofday(&tv1, 0);
send_all(100000);
gettimeofday(&tv2, 0);
tt = tv2.tv_sec - tv1.tv_sec + (tv2.tv_usec - tv1.tv_usec) * 0.000001;
printf("Server: total time %f\n", tt);
break;
case 2:
assert(argc == 4);
port = atoi(argv[2]);
host = argv[3];
if (init_client(host, port {
printf("Client initialization failed!\n");
return 1;
}
gettimeofday(&tv1, 0);
receive_all(100000);
gettimeofday(&tv2, 0);
tt = tv2.tv_sec - tv1.tv_sec + (tv2.tv_usec - tv1.tv_usec) * 0.000001;
printf("Client: total time %f\n", tt);
break;
default:
printf("Wrong parameter\n");
return 1;
}
shutdown(sock, SHUT_RDWR);
close(sock);
return 0;
}
Я посмотрел список linux-netdev, но там обсуждают более сложные вопросы, связанные с разработкой драйверов в ядре. Уместно ли послать туда такую программу?Типа твой вопрос простой что ли? Конечно, уместно.
Конечно уместно! Это же open source! Люди могут задавать вопросы даже ламерские, хотя твой таковым не является, а кому не лень будут на них отвечать.
Кстати тебе респект, ты написал портабельную программу, которая собралась на другой операционной системе с -Wall -pedantic без единого warning! Блин, впервые столкнулся с тем, что человек написал программу под линукс, и она у меня собралась без правок. Респект!
Кстати, вот результат работы на FreeBSD:
Похоже, ты действительно прав, стек ведёт себя не так, как надо (не так, как хотелось бы).
Кстати тебе респект, ты написал портабельную программу, которая собралась на другой операционной системе с -Wall -pedantic без единого warning! Блин, впервые столкнулся с тем, что человек написал программу под линукс, и она у меня собралась без правок. Респект!
Кстати, вот результат работы на FreeBSD:
Client: connected to localhost:5000
Client: begin receive_all
Client: total time 9.997108
Похоже, ты действительно прав, стек ведёт себя не так, как надо (не так, как хотелось бы).
Все работает под Linux 2.6.А у тебя какое 2.6? У меня на генту с 2.6.11.11 работает правильно.
nix ~ $ ./a.out 2 5000 localhost
Client: connected to localhost:5000
Client: begin receive_all
Client: total time 10.376747
nix ~ $
Проверял на 2.6.17.
там отцы вроде разобрались с проблемой
надо отключить tcp_abc
надо отключить tcp_abc
А что такое tcp_abc?
какая-то фича
почитай лучше сам тред, там отцы спорят про неё
почитай лучше сам тред, там отцы спорят про неё
Алекс, я думаю, тебе для твоей фигни нужен протокол на базе UDP с собственными (простыми и понятными) механизмами потверждения и flow control.
Потому как тебе важны границы сообщений, но не важен их порядок, в то время как TCP даёт совершенно противоположные гарантии.
В этом поможет измерялка RTT и PL, так что и этот пункт будет закрыт
Потому как тебе важны границы сообщений, но не важен их порядок, в то время как TCP даёт совершенно противоположные гарантии.
В этом поможет измерялка RTT и PL, так что и этот пункт будет закрыт

А в Линуксе же уже есть SCTP?
SCTP есть, но я в него не верю.
Ещё есть DCCP, вроде занятная штука, но потребовать, чтобы везде, где надо, его включили в ядро, может оказаться затруднительно.
Ещё есть DCCP, вроде занятная штука, но потребовать, чтобы везде, где надо, его включили в ядро, может оказаться затруднительно.
> Алекс, я думаю, тебе для твоей фигни нужен протокол на базе UDP с собственными (простыми и понятными) механизмами потверждения и flow control.
Полностью согласен с тобой. Тут такая проблема --- TCP используется не в самой программе, а в библиотеке. Причем это библиотека используется множеством людьми. Переписывать заново всю библиотеку очень не хочется (она большая). Авторы библиотеки тоже наверное ничего менять не станут --- скажут, что мой случай нетипичный
Полностью согласен с тобой. Тут такая проблема --- TCP используется не в самой программе, а в библиотеке. Причем это библиотека используется множеством людьми. Переписывать заново всю библиотеку очень не хочется (она большая). Авторы библиотеки тоже наверное ничего менять не станут --- скажут, что мой случай нетипичный

Да, с отключенным tcp_abc заметно лучше (задержки вместо 40 мс стали порядка 2,3 мс)
Да, с отключенным tcp_abc заметно лучше (задержки вместо 40 мс стали порядка 2,3 мс)Теперь всё понятно, нормальный slow start работает.
Видно, что с каждой итерацией cwnd растёт на один пакет (в линуксе оно считается в пакетах начиная с двух. Соответственно, через короткое время сессия разгоняется, и пакеты уходят без задержек.
В BSD, как сказано в том треде, cwnd считается в байтах, и потому можно сразу, без задержки, несколько тысяч пакетов, по одному байту в каждом, отправить.
Оставить комментарий
Landstreicher
У меня есть открытый TCP-сокет.1) Допустим я записал в него некоторые данные (небольшие по размеру) с помощью write(2). Я хочу, чтобы весь текущий накопленный буфер немедленно ушел по сети получателю. Или хотя бы какая-то его порция послалась по сети. Что мне надо сделать?
2) Могу ли я сделать какой-нибудь setsockopt, чтобы подобное поведение делалось на сокете автоматически при всех посылках.
Знаю, что это неоптимально в плане пропускной способности, но так надо в силу специфики задачи.
Все работает под Linux 2.6.