Подскажите пожалуйста хоть в какую сторону рыть, сутки проб и поисков не дали результата...
Пишу простенький веб-сервер сугубо для своих нужно, нужно отправлять и получать JSON.
Тестил в Firefox, все отлично, запустил на Webkit-подобном движке и...
Обнаружил что tcp пакет с хедером и контент с json идут в разных tcp пакетах! (суммарный размер прмиерно 300 байт и оно почему -то разбивает на 2 пакета!)
В Firefox, опять таки, такого нет. Один пакет.
Мой код с epoll просыпается когда есть данные на сокете, но считывается только хедер без данных...
Код считывания с сокета, на котором есть данные:
нижеприведенный код создает буффер определенного размера, если считаны данный длинной с буффер — расширяем его еще и так пока считается меньше чем шаг буффера, потом ресайзим буффер
Здравствуйте, lnkuser, Вы писали:
L>Тестил в Firefox, все отлично, запустил на Webkit-подобном движке и... L>Обнаружил что tcp пакет с хедером и контент с json идут в разных tcp пакетах! (суммарный размер прмиерно 300 байт и оно почему -то разбивает на 2 пакета!) L>В Firefox, опять таки, такого нет. Один пакет.
Любая программа, работающая с TCP, должна быть готова к тому, что данные могут прийти произвольно нарезанными порциями.
Соответственно, если пришел только хидер, то надо быть готовым дочитать данные, которые могут прийти позже. Кстати, и хидер может приехать частями, и данные тоже.
Здравствуйте, Pzz, Вы писали:
Pzz>Здравствуйте, lnkuser, Вы писали:
L>>Тестил в Firefox, все отлично, запустил на Webkit-подобном движке и... L>>Обнаружил что tcp пакет с хедером и контент с json идут в разных tcp пакетах! (суммарный размер прмиерно 300 байт и оно почему -то разбивает на 2 пакета!) L>>В Firefox, опять таки, такого нет. Один пакет.
Pzz>Любая программа, работающая с TCP, должна быть готова к тому, что данные могут прийти произвольно нарезанными порциями.
Pzz>Соответственно, если пришел только хидер, то надо быть готовым дочитать данные, которые могут прийти позже. Кстати, и хидер может приехать частями, и данные тоже.
Прикол в том, что после считывания первого пакета на сокете пусто, повторный read будет блокирующим.
Можно ли как-то принудительно ждать, пока сегменты соберуться? Ничего путного в поиске я не нашел...
Здравствуйте, lnkuser, Вы писали:
L>Прикол в том, что после считывания первого пакета на сокете пусто, повторный read будет блокирующим.
Ну да. Если два куска шли физически отдельными пакетами, между их появлением будет некоторая пауза.
L>Можно ли как-то принудительно ждать, пока сегменты соберуться? Ничего путного в поиске я не нашел...
Нет, нельзя.
А откуда TCP вообще может знать, что "сегменты собрались". Ему на стороне отправителя сказали два раза send(). Откуда он знает, что это связанные данные?
Здравствуйте, lnkuser, Вы писали:
L>Подскажите пожалуйста хоть в какую сторону рыть, сутки проб и поисков не дали результата...
рыть в сторону boost::asio, а то и вообще какого встраиваемого http сервера. Использовать сырые сокеты для такой задачи просто не следует.
L>Мой код с epoll просыпается когда есть данные на сокете, но считывается только хедер без данных...
Данные могут приезжать кусками какой угодно длины, и код должен быть к этому готов.
$ man read
...
ERRORS
EAGAIN The file descriptor fd refers to a file other than a socket and has been marked nonblocking (O_NONBLOCK), and the read
would block.
EAGAIN or EWOULDBLOCK
The file descriptor fd refers to a socket and has been marked nonblocking (O_NONBLOCK), and the read would block.
POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same
value, so a portable application should check for both possibilities.
> Обнаружил что tcp пакет с хедером и контент с json идут в разных tcp пакетах!
Данные пришедшие по tcp складываются в буфер, из которого вы читаете. OS не делает разделения по границам пришедших пакетов. Т.е. данные туда могли прийти хоть по одному байту, OS отдаст вам сколько вы запросили (все пришедшие пакеты) или меньше (по одному байту, хотя пакет был на 1К), если по какой-то причине не успеет отдать все (прерывание сигналом, например). Уверен, что если вы почитаете документацию, то самостоятельно сможете написать программу, которая будет передавать данные в пакетах с полезной нагрузкой по одному байту...
Я это все к тому, что независимо от размера передаваемых данных они не факт, что придут в одном пакете и даже если в одном, то не факт, что вы их все получите за одни read, более того, размер read может быть как больше, так и меньше размера передаваемого по сети пакета.
Здравствуйте, Pzz, Вы писали:
Pzz>А откуда TCP вообще может знать, что "сегменты собрались". Ему на стороне отправителя сказали два раза send(). Откуда он знает, что это связанные данные?
Ну по спецификации TCP такое возможно (флаг PSH):
[quote]
Sometimes users need to be sure that all the data they have
submitted to the TCP has been transmitted. For this purpose a push
function is defined. To assure that data submitted to a TCP is
actually transmitted the sending user indicates that it should be
pushed through to the receiving user. A push causes the TCPs to
promptly forward and deliver data up to that point to the receiver.
The exact push point might not be visible to the receiving user and
the push function does not supply a record boundary marker.
[/quote]
то есть присылка без PSH может не вызывать немедленной готовности данных, а вызовет или по пакету с PSH, или по таймауту, или по существенному заполнению буфера.
Здравствуйте, Слава, Вы писали:
С>Здравствуйте, lnkuser, Вы писали:
L>>Подскажите пожалуйста хоть в какую сторону рыть, сутки проб и поисков не дали результата...
С>Пакет от отправителя в 300 байт размером, вам может придти в виде 300 пактов по 1 байту. Собирайте у себя.
Это как раз вряд ли. С минимальным IP4 MTU, равным 68 байт, минимум порции данных TCP на реальных стеках — 16 байт. Так что 300 байт нарежется на 19 порций. Но в живом мире на Ethernet MTU менее 1300 не встречается.
Здравствуйте, netch80, Вы писали:
N>Это как раз вряд ли. С минимальным IP4 MTU, равным 68 байт, минимум порции данных TCP на реальных стеках — 16 байт. Так что 300 байт нарежется на 19 порций.
Я про теорию. Сказано же в писании, что tcp есть поток, стало быть не следует от него ожидать каких-то границ пакетов. Это вообще надо в FAQ раздела вывесить.
N>Но в живом мире на Ethernet MTU менее 1300 не встречается.
Существует еще связь по GPRS и тому подобным каналам.
Здравствуйте, Слава, Вы писали:
С>Здравствуйте, netch80, Вы писали:
N>>Это как раз вряд ли. С минимальным IP4 MTU, равным 68 байт, минимум порции данных TCP на реальных стеках — 16 байт. Так что 300 байт нарежется на 19 порций.
С>Я про теорию. Сказано же в писании, что tcp есть поток, стало быть не следует от него ожидать каких-то границ пакетов. Это вообще надо в FAQ раздела вывесить.
С этим полностью согласен. Но примеры для объяснения должны быть реальными. А особенно — должно быть сказано, что это как раз тот случай, когда банально пройти через сито собственных функциональных тестов, но нарваться на проблему в реальном мире.
А ещё я бы в тот же FAQ запихнул жёсткое "нужен собственный протокол — начинайте с SCTP и переходите на TCP только если первый недоступен".
N>>Но в живом мире на Ethernet MTU менее 1300 не встречается.
С>Существует еще связь по GPRS и тому подобным каналам.
Здравствуйте, netch80, Вы писали:
N>А ещё я бы в тот же FAQ запихнул жёсткое "нужен собственный протокол — начинайте с SCTP и переходите на TCP только если первый недоступен".
А он разве где-то за пределами UNIX-ов поддерживается? Насколько знаю, ни в OS X ни в Windows нет поддержки. Да и не без своих проблем, вроде как. Но в целом, идея безусловно здравая, не надо придумывать новый протокол, если можно обойтись существующими.
Здравствуйте, kaa.python, Вы писали:
KP>Здравствуйте, netch80, Вы писали:
N>>А ещё я бы в тот же FAQ запихнул жёсткое "нужен собственный протокол — начинайте с SCTP и переходите на TCP только если первый недоступен".
KP>А он разве где-то за пределами UNIX-ов поддерживается? Насколько знаю, ни в OS X ни в Windows нет поддержки.
OS X это Unix, конкретно, в основном BSD.
Для Windows есть минимум 2 доступные бесплатные реализации и несколько платных.
L>Мой код с epoll просыпается когда есть данные на сокете, но считывается только хедер без данных
Считывайте с сокета по мере прихода пакетов. Сделать сокеты неблокирующими, буфер для чтения фиксированным.
Количество данных для чтения в recv/read/recvmsg назначаем равным размеру фиксированного
буфера. Если чтение вернуло -1, и error выставился в EAGAIN, ждём пакетов на сокете, которые, когда придут,
просигнализируют о себе появлением в возврате из epoll_wait. Если прочитали размер фиксированного буфера, значит
на сокете, возможно, есть непрочитанные данные, после обработки прочитанных данных нужно будет вызвать read/recv/recvmsg
снова. Если чтение вернуло количество, меньшее запрошенного, то на сокете данных больше нет, ждём дальше, если в прочитанных
данных только хедер без тела, например. Разные буфера-куски фиксированного размера, прочитаные в разное время, можно собирать в
распределёный буфер-список буферов фиксированного размера, представляющий собой абстракцию непрерывного буфера. Хотя в Вашем
resize, может, именно это. Такими являются буфера приёмных и отправных пакетов в ядрах ОС.
Здравствуйте, netch80, Вы писали:
N>OS X это Unix, конкретно, в основном BSD.
ПОловина BSD, половина Mach. Сеть – старая BSD с сильными изменениями.
N>Для Windows есть минимум 2 доступные бесплатные реализации и несколько платных.
Которые нужно еще отдельно установить. Однозначно не вариант, если приложение не для гиков, конечно.
Здравствуйте, netch80, Вы писали:
N>А ещё я бы в тот же FAQ запихнул жёсткое "нужен собственный протокол — начинайте с SCTP и переходите на TCP только если первый недоступен".
Я бы этого точно этого не рекомендовал. Это противоречит KISS. TCP элементарен в использовании. Чтобы использовать расширенные возможности SCTP нужно разобраться с кучей доков, а в конце выясняется, что всех удовольствий — это возможность указать системе границы пакетов которые нужно отдавать пользователю. Вся остальная дребедень типа мультихома нужна только в специфических случаях и требует ещё кучу сложностей.
Так что начинать стоит с TCP, и обращаться к другим вариантам только если через TCP ну никак не выходит.
В данном же случае, HTTP вообще не предполагает пакетных границ. Он уже заточен под использование TCP. Так что ожидание данных в рамках одного пакета — это классические грабли всех начинающих сетевых программистов. Нужно изначально проектировать решение как систему обработчиков событий. Нашёл в буфере ASCII 10, передай управление обработчику строк. Обработчик обнаружил хидер дающий длину тела, пусть запустит ожидание нужного количество байт тела и так далее. Наличие epoll вообще не играет особой роли. Лучше вообще от него абстрагироваться, использовав какой-нибудь libevent или собственную библиотеку. Есть даже люди умудряющиеся читать-писать-обрабатывать в разных потоках. Только и в этом случае всё подчиняется тому же принципу обработки событий.
Здравствуйте, netch80, Вы писали:
N>А ещё я бы в тот же FAQ запихнул жёсткое "нужен собственный протокол — начинайте с SCTP и переходите на TCP только если первый недоступен".
Хм, а что изменит SCTP? Ну, будет информация ходить через другой транспорт, и чего? Каких-то кардинальных упрощений в прикладном смысле это человеку не даст. Лучше тогда уж в самом деле всякий boost::asio, Protobuf и всё такое прочее.
Здравствуйте, Mr.Delphist, Вы писали:
MD>Здравствуйте, netch80, Вы писали:
N>>А ещё я бы в тот же FAQ запихнул жёсткое "нужен собственный протокол — начинайте с SCTP и переходите на TCP только если первый недоступен".
MD>Хм, а что изменит SCTP?
Границы сообщений, гарантированные транспортом.
MD> Ну, будет информация ходить через другой транспорт, и чего? Каких-то кардинальных упрощений в прикладном смысле это человеку не даст. Лучше тогда уж в самом деле всякий boost::asio, Protobuf и всё такое прочее.
Условие данного совета — собственный протокол. Что там внутри у этого протокола — вопрос следующий.