LINUX.ORG.RU

Ядро не отдаёт приложению содержимое TCP-пакета, пока не придёт следующий

 , , ,


1

8

Есть приложение, которое общается с сервером по TCP. Протокол представляет собой просто запрос-ответ, плюс ещё клиент и сервер отправляют друг другу пинги.

Клиент в бесконечном цикле дёргает read() из сокета, который переведёт в неблокирующий режим.

Проблема в том, что через некоторое время случается так, что клиент не вычитывает ответ, пока не придёт очередной пинг от сервера. То есть, read() всё это время возвращает -1, errno=EAGAIN.

В tcpdump видно, что ответ пришёл вовремя (сразу после запроса). А также видно, что в тот момент, когда клиент наконец-то вычитал, пришёл следующий пакет (пинг).

Проблемы в приложении исключили, запускали под strace, постоянно пытается вычитать новые данные, но ядро их не отдаёт.

В какую сторону копать?


Ответ на: комментарий от cudeta

Так я же ответил, что на клиенте

так я ж сверху-вниз читаю: когда это писал, ответа еще не видел, учитывай

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

pihter ★★★★★
()
Ответ на: комментарий от pihter

А откуда куда неткат? Я же говорю, через интернет всё нормально работает, по локальной сети — нет. А в локальной сети только этот сервер, на который у нас нет доступа.

cudeta
() автор топика
Ответ на: комментарий от firkax

Во-первых, он показывает пакеты на интерфейсе, ещё до того, как их обработает файрволл. А файрволл может пакет вообще отклонить. Штуки, настраивающиеся через tc, скорее всего тоже уже после, а там можно и задержки ставить.

Спасибо. Как отладить всё, что происходит после tcpdump’а?

Во-вторых, если даже отбросить фокусы с пакетным фильтром, может быть такая ситуация: сервер прислал ответ в двух (или больше) пакетах, первый пакет потерялся, второй нет, и в tcpdump-е ты видишь второй пакет и его позицию с принятыми байтами. Пока первый пакет тоже не придёт - приложение ничего не увидит, а то в зависимости от настроек может и много времени занять, но «несколько минут» - всё-таки не особо реалистично. В любом случае, стоит внимательнее посмотреть, какие именно пакеты пришли, что они покрыли все seq номера без дырок.

Ну, tcpdump показывает только один пакет (длина ответа 73 байта, пинга — 16). Дырок в seq нет. Куда ещё смотреть?

cudeta
() автор топика
Ответ на: комментарий от cudeta

Через обычный интернет не воспроизводится, только в этой конкретной локальной сети.

И это привело тебя к мысли что проблема в ядре? Чего она тогда через обычный интернет не воспроизводится?

На клиенте есть возможность поставить виртуалку с бздей?

Ты можешь собрать минимально-возможную версию клиента, оставив только работу с сетью, выкинув все остальное?

У нас пока что нет эмулятора сервера.

А зачем он нам? Мы же тут со стороны клиента можем экспериментировать с настоящим сервером, я прав?

pihter ★★★★★
()
Ответ на: комментарий от pihter

И это привело тебя к мысли что проблема в ядре?

То, что ядро не отдаёт данные приложению, пока не придёт следующий пакет.

На клиенте есть возможность поставить виртуалку с бздей?

Скорее нет, чем да.

Ты можешь собрать минимально-возможную версию клиента, оставив только работу с сетью, выкинув все остальное?

Да, но на это уйдёт довольно много времени.

А зачем он нам? Мы же тут со стороны клиента можем экспериментировать с настоящим сервером, я прав?

Да.

cudeta
() автор топика
Ответ на: комментарий от cudeta

А откуда куда неткат? Я же говорю, через интернет всё нормально работает, по локальной сети — нет. А в локальной сети только этот сервер, на который у нас нет доступа.

Ну так а я это откуда должен был знать? Про локальную сеть и интернет ты сказал только к концу первой страницы: ты так спрашиваешь с недоумением, будто твоя ситуация очевидна для всех. Нет. Мы нифига не знаем и имей уж терпение отвечать на уточняющие вопросы, раз пришел )

Хорошо, проблемная сетка только клиент-сервер, но ведь клиент – это компьютер? Сетка – это изернет кабель? Можно воткнуть его в свитч и рядом поставить ноутбук, внедрив еще один девайс в сеть? Можно на клиент поставить виртуалку с бридж сетевым интерфейсов, внедрив еще одни виртуальный компьютер в сеть?

pihter ★★★★★
()
Ответ на: комментарий от cudeta

То, что ядро не отдаёт данные приложению, пока не придёт следующий пакет.

Только твоему приложению из этой сети или любому приложению из этой сети?

На клиенте есть возможность поставить виртуалку с бздей?

Скорее нет, чем да.

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

Ты можешь собрать минимально-возможную версию клиента, оставив только работу с сетью, выкинув все остальное?

Да, но на это уйдёт довольно много времени.

А как ты хотел? :)

pihter ★★★★★
()
Ответ на: комментарий от pihter

Хорошо, проблемная сетка только клиент-сервер, но ведь клиент – это компьютер?

Да.

Сетка – это изернет кабель?

Да.

Можно воткнуть его в свитч и рядом поставить ноутбук, внедрив еще один девайс в сеть? Можно на клиент поставить виртуалку с бридж сетевым интерфейсов, внедрив еще одни виртуальный компьютер в сеть?

Можно, но сложно. Мы ведь уже определили, что пакет доходит до физической машины вовремя, с помощью tcpdump.

cudeta
() автор топика
Ответ на: комментарий от pihter

Только твоему приложению из этой сети или любому приложению из этой сети?

Не понял смысла вопроса. Это единственное приложение, которое взаимодействует с сервером по этой сети.

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

Не уверен, можно ли это сделать. А зачем? Мы ведь уже определили, что пакет доходит до физической машины вовремя, с помощью tcpdump.

cudeta
() автор топика

Переписать на раст! :))

Так напиши на питоне скрипт за пару минут который будет просто слушать сокет и выводить его в stdout, и посмотри как там работать будет.

Ну или netcat ом как уже говорили.

skyman ★★★
()
Последнее исправление: skyman (всего исправлений: 1)
Ответ на: комментарий от cudeta

Обычно интернет отличается от локалки задержками и потерями...

Видимость пакета tcpdump-ом это только факт приёма пакета сетевухой. Дальше есть IP-стек который имеет кучу поводов отбросить пакет.

Но большинство дропов видно в статистике (netstat -s).

Есть и косвенный метод определить дроп.

Если tcp принимает валидный пакет, то в ответ идет ack.

Если у тебя пришел пакет, а назад не отправлен ack - значит его дропнули.

vel ★★★★★
()

Можно попробовать socat'ом читать сокет и выводить в STDOUT. Так можно определить проблема ли это с клиентом или у socat аналогичное поведение. Если нужно серверу отправить запрос перед тем как ловить ответ, то могу скинуть пример как это делается в одну команду(потратил сил на чтение доков по socat не мало для этого).

Loki13 ★★★★★
()
Ответ на: комментарий от cudeta

Сделать две вещи.

Во-первых, проверь так, как тебе сказал pihter Ядро не отдаёт приложению содержимое TCP-пакета, пока не придёт следующий (комментарий) . Если у тебя в сети вот прям вообще одна витая пара приходит от сервера, выдерни ее из устройства. Возьми свой рабочий компьютер и соедини его с устройством витой парой напрямую. Твой компьютер будет сервером. Настрой на компе такой же IP как на удаленном сервере. И тестируй себе на здоровье.

Во-вторых, напиши минимальный пример на сишечке, код ты сам уже показал. Просто в виде одного main.cpp, закомпиль его и проверь свою теорию, может быть в этом минимальном примере все будет по-другому. Тогда однозначно ищи проблему в своем коде.

Xintrea ★★★★★
()
Ответ на: комментарий от cudeta

Приложение постоянно пытается вычитать новые данные, семантика read() — вернуть, если есть хотя бы один байт в буфере.

попробуй - пусть оно читает даже если нет байт в буфере

Gennadevich
()
Ответ на: комментарий от cudeta

А seqnum у пришедшего пакета точно следует за ранее полученным? Может какой-нибудь (втч пустой keepalive) пакет перед этим где-то теряется, и ядро просто ждёт ретрансмита прошлого сегмента?

Например, разницу между интернетом и локалкой можно объяснить дропами по кривому MTU.

snizovtsev ★★★★★
()
Последнее исправление: snizovtsev (всего исправлений: 1)
Ответ на: комментарий от snizovtsev

Да, точно следуетза ранее полученным. Seq num у пакетов 0x78b36864 (ответ 1), 0x78b36898 (ответ 2), 0x78b368cc (пинг). Разница между ними 52, это размер ответа в байтах.

Как отладить возможные проблемы с MTU?

cudeta
() автор топика
Ответ на: комментарий от vel

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

Но это же злостное 4.2. Это было бы жутко неэффективно. Ack’и отправляются с задержкой миллисекунд так в 300 (в предположении что больше посылать было нечего).

bugfixer ★★★★★
()
Ответ на: комментарий от bugfixer

Запусти хоть раз tcpdump, теоретик...

Задержка отправки ack в нормальной ситуации базируется на rtt (про rto - это описка).

Для локалки это единицы милисекунд. Посмотри на rtt в «ss -i»

vel ★★★★★
()
Ответ на: комментарий от cudeta

Это хорошо. А то были подозрения на запуск каких-нибудь старинных бинариков.

На клиенте не пробовали отключать на сетевом интерфейсе все виды offload ?

ethtool -K ethX sg off tso off gso off gro off lro off

Хорошо бы увидеть трафик собранный tcpdump/wireshark на стороне клиента во время этой проблемы.

vel ★★★★★
()
Ответ на: комментарий от vel

На клиенте не пробовали отключать на сетевом интерфейсе все виды offload ?

Нет, ещё не пробовали.

Хорошо бы увидеть трафик собранный tcpdump/wireshark на стороне клиента во время этой проблемы.

А что именно интересует в трафике? Как я уже говорил, согласно дампу, пакет приходит вовремя, ACK ядро отправляет на него.

cudeta
() автор топика

Собери своё ядро (с тем же конфигом, что и дистрибутивное), воспроизведи проблему, потом понатыкай логов на пути обработки пакета и отлаживай, в какой момент обработка пакета стопорится. Может и баг в ядре, кто его знает, он же у тебя не всегда воспроизводится.

vbr ★★★★
()

Читал,читал. Попробуй valgrind-ом потестить клиент. По некоторому опыту скажу, что довольно часто странное поведение программы, особенно нерегулярное, объясняется грязной работой с памятью.

praseodim ★★★★★
()
Ответ на: комментарий от vel

Запусти хоть раз tcpdump, теоретик…

Улыбнулся.

Для локалки это единицы милисекунд.

Я не знаю что в Вашем понимании является «локалкой». Loopback? Там делайте что хотите, а на real wire такие низкие задержки ack’ов ни к чему хорошему не приведут.

bugfixer ★★★★★
()
Ответ на: комментарий от cudeta

если «ACK ядро отправляет на него», то уже не нужно.

И offload тоже отключать не нужно.

IMHO баг в клиенте. Делать read на сокет не самый лучший вариант. Там же не напрямую syscall read используется, там обёртка в виде glibc у которой свои тараканы.

Есть утиль ltrace - это вариант strace, только она еще показывает вызовы функций. Попробуй её натравить.

Интересно, а через какой-нибудь tcp-proxy (redirect в xinetd) этот баг будет проявлятся?

А какая версия ядра и glibc сейчас на клиенте. даунгрейд ядра не пробовали? Это достаточно просто. А вот даунгрейд glibc может оказаться сложнее.

vel ★★★★★
()
Последнее исправление: vel (всего исправлений: 1)
Ответ на: комментарий от bugfixer

Речь идет о локалке от 100Мбит/с и хотя бы одном коммутаторе между хостами.

А на loopback все вообще неинтересно. Там и mtu 64k и контрольные суммы заголовков не считают и не проверяют.

vel ★★★★★
()