kb 03.10.2011 08:37 c8541125

Вот объясните мне, пожалуйста. Если я делаю с одной стороны send() какой-то строки, а с другой стороны делается recv(), сообщение может дойти не полностью, а в несколько recv()'ов? Или, всё же, можно надеяться, что оно либо дойдет, либо не дойдёт?

И второй вопрос: если я делаю асинхронный recv() (можно через select, можно через libevent/epoll), при этом с большой вероятностью зная, что может быть клиент уже ничего не пошлет, но сокет не закроет. Ничего ведь страшного? Это ведь нормально? (чтоб не городить контрольные суммы переговоров и их длинну и т.п.)

Спасибо.

Recommended by: @DZhon
1. magog 03.10.2011 08:38 Azoth

может не поместиться, поэтому recv должен работать в цикле, пока есть что считывать

2. magogmagog /1 03.10.2011 08:38 Azoth

почитай Стивенсона и его Разработка сетевых приложений под UNIX — там это очень хорошо расписано

3. kbmagog /2 03.10.2011 08:39 c8541125

да, я уже скачал, обязательно почитаю. но пока так хотел бы, вроде вопросо не очень много :-)

4. kbmagog /2 03.10.2011 08:43 c8541125

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

и еще вопрос: у меня в экспериментах, в recv() ты укахываешь макс. длинну сообщения, и если его послать с большей длинной — оно просто обрезается. то есть твоё "не поместится", — получается я сам должен заботиться о разбиении сообщения и т.п.?

5. magogkb /4 03.10.2011 08:45 Azoth

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

6. kbmagog /5 03.10.2011 08:47

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

7. magogkb /6 03.10.2011 08:47 Azoth

не работать с сокетами, а юзать готовые либы.

8. kbmagog /7 03.10.2011 08:49 c8541125

ага. и что, к примеру, для Си самое популярное?

9. DZhonkb /8 03.10.2011 09:04

zeromq, например (не самое популярное, но довольно удобное).

10. analizer 03.10.2011 09:20 mcabber

из того какие вопросы ты задаёшь, предполагаю что сокет у тебя блокирующий:
1. recv в этом случае работает так же как и fread, читай из него блоками. покуда сокет не закрыт, он пытается прочитать из него столько данных, сколько ты запросил на чтение. если вернёт количество прочитанных байт меньше запрошенного — значит удалённый хост послал всё что хотел и закрыл сокет.
2. зачем ты через select делаешь асинхронный recv, если можно просто сделать сокет неблокирующим? случае отсутствия данных выставит errno в EAGAIN и EWOULDBLOCK

11. DZhonanalizer /10 03.10.2011 09:29

Ага, а в той же винде еще лучше заюзать Overlapped I/O. И тут вспоминаем ненавистные кресты и boost::asio, где под каждой платформой выбрано оптимальное демультиплексирование.

12. kbanalizer /10 03.10.2011 09:58

Хмм. То есть мне прийдёт, с одной стороны, не больше, чем я запросил, а с другой, — не больше, чем размер пакета, но если я знаю, что сообщение вместится и туда и туда — значит оно _гарантировано_ отдастся мне за один read?

Ну, через select — чтоб сразу много было. Но лучше вообще libevent, потому что при select или ручной обработке, на сколько я понял, сложность (из-за того, что на каждый чих пробегаешься по списку клиентов) сильно растёт.

13. analizerkb /12 03.10.2011 10:02 mcabber

1. ЕМНИП, «не больше, чем размер пакета» — не имеет под собой оснований. можно запросить мегабайт прочесть и оно действительно прочтёт разом мегабайт, хотя пакеты восьмикилобайтные ходить будут. да, гарантированно за один read, если сокет неблокирующий.
2. не думаю что select настолько туп. насколько мне известно, все нормальные люди им пользуются для детекта активности в сокетах.

14. kbanalizer /13 03.10.2011 10:06 c8541125

нет, select используется потому, что ты не хуячишь в цикле по неблокирующему сокету, сжирая весь CPU, а он уснёт, пока активность не проявится хотя бы в одном из запрошеных сокетов. но когда она появится — ты бегом побежишь по всем массивам в поисках нужных/ответивших, чтоб из них уже читать. может я неправильно понял, но вроде именно об этом пишет intro по libevent http://www.wangafu.net/~nickm/libevent-b...

по поводу мегабайта --

15. kbanalizer /13 03.10.2011 10:07 c8541125

по поводу мегабайта — это именно то, о чем я спрашивал, спасибо. только вот magog в этом треде утверждает обратное, что надо делать recv() в цикле. какой из ваших ответов правильный?)

16. kbanalizer /13 03.10.2011 10:10

и еще к тебе оффтопик-вопрос: в вашей группе SePe (надежности и производительности поиска) работа/задачи интересные? или говно? или ты хз и не спрашивать об этом? спасибо.

17. analizerkb /14 03.10.2011 10:14 mcabber

хм, ну да, только вот «бежать по всем массивам» мне кажется довольно шустрой операцией при помощи FD_ISSET

18. analizerkb /16 03.10.2011 10:15 mcabber

очень интересные задачи там. серьёзно

19. analizerkb /15 03.10.2011 10:17 mcabber

имхо, он сказал то же самое что и я

20. kbanalizer /17 03.10.2011 10:17 c8541125

при большом количестве сокетов эта операция не шустрая хотя бы чисто алгоритмически, собственно из-за этого libevent и был придуман, насколько я понял (точнее был придуман epoll с его коллбеками, а libevent — просто композиция всех этих kqueue). но я тут не специалист пока, только хочу им стать.

21. kbanalizer /19 03.10.2011 10:20

нет, есть очень важный момент/отличие, о котором я и спрашивал (хотя может я просто не понял): я могу при помощи send("bla-bla-bla") быть увереным, что при любой размерности "bla-bla-bla" оно придёт в один recv? Если да — я могу считать одну строку одним сообщением. Если нет — мне придётся городить свой маркер конца сообщения и делать recv пока я его не увижу (ну, еще вариант — заранее говорить длинну сообщения и т.п.).

и действительно, насколько я понял ZeroMQ именно так и работает, он предоставляет интерфейс, подобный всем этим send/recv, но там он гарантирует, что сообщение дойдёт и прочитается полностью.

22. DZhonkb /21 03.10.2011 10:40

zeromq вешает маркеры.

23. kbDZhon /22 03.10.2011 10:41

ну, это логично (если всё именно так работает, что ты не можешь быть уверенным, что тебе "прислали всё" за один recv)

24. DZhonkb /23 03.10.2011 10:48

В том, что мне доводилось пописывать, я отправлял сначала uint32_t с размером, а потом сообщение. Самый удобный вариант, на мой взгляд.

25. analizerkb /21 03.10.2011 10:49 mcabber

если в recv ты будешь передавать буфер размера большего "bla-bla-bla", то оно действительно пройдёт в один recv

26. kbDZhon /24 03.10.2011 10:49

какой-то борланд паскаль получается (длинна данных, потом данные). хотя если uint32_t хватит всем, почему бы и нет :-)

27. magogkb /26 03.10.2011 10:50 Azoth

а ты думаешь все по другому устроено?

28. kbmagog /27 03.10.2011 10:50 c8541125

думаю, что можно маркер окончания строки сделать

29. magogkb /28 03.10.2011 10:51 Azoth

как ты собираешь принимать данные на той стороне?

30. kbmagog /29 03.10.2011 10:52 c8541125

ну, возможно вот здесь мне и необходимо просветление. сейчас я бы делал recv(1024) пока не получу данные, в которых есть маркер. где я не прав?

31. kbmagog /29 03.10.2011 11:01

и еще. так всё-таки @analizer в /25 прав или нет? гарантируется, что мой .recv(100500) всегда примет чей-то .send("bla-bla-...-bla"), если этот bla-bla-bla будет меньше 100500?

32. kbkb /31 03.10.2011 11:02

(в один .recv(), имеется в виду, а не в цикл из .recv()'ов)

33. DZhonkb /31 03.10.2011 11:09

Маркер окончания строки не освободит тебя от костылей по проверке текущего принятого размера (ты же не хочешь переполнений?!)

34. analizerkb /31 03.10.2011 11:09 mcabber

гарантируется при выставлении флага MSG_WAITALL

35. kbDZhon /33 03.10.2011 11:11 c8541125

ну как, оно ведь гарантирует, что не даст мне больше, чем 100500 прочесть, оно ж обрежет просто большее сообщение? или не?

36. DZhonkb /35 03.10.2011 11:12

Насколько я понимаю, оно вполне может дочитаться в следующий раз.

37. DZhonkb /35 03.10.2011 11:13

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

38. kbanalizer /34 03.10.2011 11:14 c8541125

хмм, похоже на правду.
MSG_WAITALL (since Linux 2.2)
This flag requests that the operation block until the full request is satisfied. However, the call may still return less data than requested if a signal is caught,
an error or disconnect occurs, or the next data to be received is of a different type than that returned.

при этом, правда, всё равно доверять нельзя ему. говно, в общем.

39. kbDZhon /37 03.10.2011 11:15 c8541125

да, для бинарных данных — говно получается, согласен, проще (чтоб не лопатить) обернуть в свой header/размер.

40. analizerkb /38 03.10.2011 11:16 mcabber

доверять можно. вернул меньше чем ожидалось — значит всё, пиздец. сообщай об ошибке и переоткрывай соединение

41. kbanalizer /40 03.10.2011 11:18 c8541125

аа, точно. ну всё, кажется осознал как-то. осталось понять вот ту большую простыню с gunicorn и можно, видимо, идти и читать/практиковать стивенса не торопясь никуда. спасибо еще раз.

Do you really want to delete ?