| Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
| Форум программистов > C/C++: Сети > баг asio? или баг TCP стека? |
| Автор: boostcoder 23.3.2011, 14:30 |
| всем доброго дня. проблема весьма специфическая. будет сложно сформулировать вопрос, и предоставить результаты тестов. рискну... код клиента выполняет следующее: 1. выделение памяти у ОС в boost::shared_array. 2. сериализация. 3. копирование в shared_array. 4. boost::asio::async_write(). 5. tcpdump показывает что данные ушли. 6. на приеме данные искажены. лог tcpdump`a для пояснения формирования непонятных "фреймов": строки сериализации содержащие подстроки servimpl - это то, что отправляется от клиента серверу. строки сериализации содержащие подстроки type1 - это то, что отправляется от сервера клиенту. ![]() что за непонятные данные присутствующие кроме архивов сериализации - мне не известно. в точке 1 показан пример когда архив разбивается. в точке 2 показана остальная часть архива. этот архив на принимающей стороне восстанавливается правильно. ошибки не возникает. далее... в точке 3 происходит разбиение очередного архива, остальная часть которого появляется только в точке 5. т.е. это именно та итерация(348), на которой происходит искажение архива, и как следствие - ошибка десериализации. ![]() далее... в точке 4 должен находится остаток архива разбитого в точке 3. ![]() вот та часть, в которой появляется остаток от архива разбитого в точке 3. ![]() какие мысли? |
| Автор: Daevaorn 23.3.2011, 14:46 |
| Где код? |
| Автор: boostcoder 23.3.2011, 15:13 |
| Daevaorn, не нужен вам код. в коде все ок. архивы отправляются целые. причину нужно искать в том коде, который добавляет к моим данным нечитаемые данные и разбивает мои данные на "фреймы". Добавлено через 1 минуту и 35 секунд я рассчитывал на то, что кто-то увидит знакомую ситуацию, и подскажет как решить. зы кстати, если между отправками данных поставить sleep на 1мс - то проблема решается полностью. |
| Автор: boostcoder 24.3.2011, 15:32 |
| прикладываю мегапростой пример воспроизводящий ошибку. |
| Автор: MAKCim 27.3.2011, 21:06 |
фрагментация осуществляется на сетевом уровне к данным "мусор" добавляться не может вы точно уверены, что в сокет все записывается правильно? вы сравнивали, что передается в async_write() и в send()? |
| Автор: marykone 27.3.2011, 21:17 |
| boostcoder, данные скорее всего искажены именно от отправителя. На первый взгляд, именно так. копать... Если дальше копать то послушайте трафик wireshark, и выложите сюда. ну может конечно кто то и tcp дамп почитать смоежт я как то не. |
| Автор: MAKCim 27.3.2011, 21:43 |
| по поводу задержки если постоянно записывать данные, а читать с задержкой 1 мс, то данные будут накапливаться в буфере сокета ядра...хм, может тут проблема версия ядра какая? |
| Автор: boostcoder 28.3.2011, 15:18 | ||||
тот мусор что на скринах, показывает tcpdump. в мой код этот мусор не поступает. насколько это возможно утверждать на приведенном коде - да. просто код реально мегапрост, чтоб где-то мог закрасться баг. если конечно это не баг asio. в чем сомневаюсь.. иначе данные портились бы не только в этом примере, но и в других приложениях. нет. а как это сделать? добавить в недры asio вывод? или есть какой-то еще способ? сейчас сделаю.
эм.. помогает задержка при записи, а не при чтении. я проверял не только на линукс... клиентский код запускаю на: 2.6.35-28-generic-pae код сервера на: 2.6.35-25-generic-pae |
| Автор: boostcoder 28.3.2011, 15:57 |
| логи. сделал wireshark`ом на локальной стороне. на сервере нет X`ов, я хз как там wireshark запускать. зы по логам, ничего не понял... |
| Автор: boostcoder 28.3.2011, 18:49 | ||
| заново протестил еще раз на winXP/Win7/Linux. баг воспроизводится всегда :( тестил так:
|
| Автор: MAKCim 29.3.2011, 14:28 | ||||||
в этом большая ошибка я много раз на это же полагался а в итоге как раз в мегапростом коде и был баг ;)
написать враперы над send/recv/... из Libc насколько я знаю, достаточно создать что-то типа wrappers.c, там реализовать обертки вот реальный код подмены fork из какого-то проекта...
Добавлено через 3 минуты и 2 секунды в таком случае я уверен на 99% баг где-то в коде думаю в вашем, а не asio |
| Автор: phprus 2.4.2011, 22:22 | ||
| Почитал я код и у меня возникло подозрение, что async_test() и asio в клиенте работают в разных потоках. А shared_array (как и shared_ptr) не может корректно передаваться между потоками (по крайней мере я пару месяцев назад не нашел такого способа). Те когда выполняется код:
в отмеченных местах возникает состояние гонки, а так-же в функции start_write в вызове boost::bind происходят изменения в счетчике ссылок shared_array и последний ломается. Если не повезет один из деструкторов shared_array может убить данные раньше чем обновятся данные счетчика ссылок. В свое время пару месяцев назад я напоролся на точно такой-же баг, но только с shared_ptr, через который между потоками передавался вектор. Весь процесс передачи был обернут мутексами, кроме участков, где возможно удаление временных объектов shared_ptr (а они возникали так как данные передавались через std::queue), но программа всеравно падала где-то в недрах asio. А иногда не падала, а что-то передавала. И что самое паршивое в GCC все работало. А в Intel C++ падало или не падало. Зависело это от фазы луны, загрузки процессора и т. д. В результате в этом месте я сделал полностью ручное управление памяти, а shared_ptr в области, где данные передаются между потоками я убрал полностью. Потокобезопасность shared_ptr описана здесь: http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/shared_ptr.htm#ThreadSafety (Example 3 описывает возникшую ситуацию). |
| Автор: boostcoder 2.4.2011, 22:32 |
| перевариваю... Добавлено через 41 секунду а по этой проблеме, которая у вас возникла, вы где-то на форуме тему создавали? хотелось бы почитать. |
| Автор: phprus 2.4.2011, 22:40 |
| Нет, не создавал. Что-бы создать тему нужна была хоть какая-либо детализация проблемы, а у меня даже segmentation fault в разных местах всплывали. Я неделю думал, что на какой-то хитрый баг либо в asio либо в intel С++ наткнулся. Потом начал искать все места, который потенциально перераспределяли память и где рядом был обмен данными между потоками. С 3-й попытки заменил нужный shared_ptr на обычный ptr и ручное управление памятью и проблема ушла полностью. |
| Автор: boostcoder 2.4.2011, 23:04 |
| вручную управлять памятью страшно |
| Автор: phprus 2.4.2011, 23:17 |
| boostcoder Везде да. У меня к каждому клиенту привязан объект класса Client, который привязан к одному io_service и который гарантированно работает в одном потоке. В качестве буферов (которые и передаются asio) используется два std::vector<char>, как поля класса Client, а данные получаемые из другого потока в виде обычного указателя используются очень локализовано и после чего удаляются, по этому я и согласился на ручное управление памятью, а не стал дальше думать, как обойти такое ограничение shared_ptr. |
| Автор: boostcoder 8.4.2011, 00:44 | ||||||
| убрал из кода все смарт-поинтеры. проблема осталась на стороне клиента, сохраняю отправляемые массивы в файл, чтоб убедится в том, что массивы не портятся. так и есть. массивы не искаженны. ![]() на стороне сервера, так же, сохраняю принятые массивы. но тут, массив приходит искаженным: ![]() вот снифф этого участка, tcpdump`ом, на стороне клиента: ![]() а это на стороне сервера: ![]() на стороне клиента, как раз в то место где данные искаженны, добавлены какие-то данные. не мои. видно, что на стороне сервера, массив, после прочтения при помощи asio, уже испорчен. но tcpdump показывает что массив не испорчен. я в замешательстве выложу код в тему, в надежде, что кто-то внесет ясность в ситуацию. клиент:
http://liveworkspace.org/code/15c1e78ea45184f18f1eebe7e08564a7 сервер:
http://liveworkspace.org/code/0a8e2cb728f85caa5d31f28c46ddf9e2 общий хидер:
http://liveworkspace.org/code/fa23c521eab14f727320dcff6290e656 всем спасибо. зы во вложении, архив с исходниками и Makefile`ом. проверено на gcc-linux и на mingw32. |
| Автор: phprus 8.4.2011, 10:02 |
| Из замеченного при помощи wireshark: Когда включен - TCP-сегменты ходят с данными ровно в 13 байт и у каждого стоит флаг PUSH (сразу отдавать на уровень выше, те приложению). Когда sleep выключен сегменты содержат множество склееных блоков по 13 байт (возможно не целое число блоков). А что говорят по этому поводу на https://svn.boost.org/trac/boost/ ? Может быть имеет смысл написать Bug report? |
| Автор: boostcoder 8.4.2011, 12:13 | ||||
при помощи strace обнаружил, что для ввода-вывода используются http://linux.die.net/man/2/sendmsg и http://linux.die.net/man/2/recvmsg, а не http://linux.die.net/man/2/send и http://linux.die.net/man/2/recv как предполагал.
это я тоже заметил. любопытно почему так происходит. но это сейчас не главное.
та я потому и не писал, ибо все же не был уверен что это не мой баг в коде. сейчас напишу.. |
| Автор: boostcoder 8.4.2011, 21:44 |
| вот что еще странно... в один прекрасный момент, сервер принимает это: ![]() в первой строке сверху - все хорошо. во второй и третьей строке, вместо завершающего нуля, вставлен символ c в четвертой строке, символа c вначале, попросту нет. |
| Автор: phprus 9.4.2011, 09:38 | ||
| boostcoder Если sleep сделать с временем ожидания, равным 0 ( boost::this_thread::sleep(boost::posix_time::microseconds(0)) ) то проблема тоже уходит, а sleep(0) в теории должен вызывать передачу управления другому процессу, те в коде похоже зарылась гонка. Только не могу понять в каком коде.
Сообщите пожалуйста ссылку на тикет. |
| Автор: boostcoder 9.4.2011, 14:28 | ||
по идее - да. не спорю... но где?! и как побороть?! |
| Автор: boostcoder 9.4.2011, 15:00 |
| phprus, так у тебя этот баг тоже воспроизводиться? скажи как тестируешь? версии ОС, компилятора, boost? локально, удаленно? |
| Автор: MAKCim 9.4.2011, 15:14 | ||
ну так это может быть пустой пакет с tcp флагами... хотя я не очень понимаю формат снифа еще проверь у себя версию в аттаче там логинг asio заменен а логинг sendmsg/recvmsg |
| Автор: phprus 9.4.2011, 15:25 | ||
Да, воспроизводится. Boost 1.46.1 openSUSE 11.3 (2.6.34.7-0.7-desktop) g++ (SUSE Linux) 4.5.0 20100604 [gcc-4_5-branch revision 160292] Тестирую локально, путем запуска сервера и клиента с двух соседних консолей в Konsole. |
| Автор: MAKCim 9.4.2011, 15:28 |
| и еще вы пробовали запускать io_service::run не в отдельном потоке? |
| Автор: phprus 9.4.2011, 15:39 |
| В реальной сети, с rtt min/avg/max/mdev = 5.226/5.250/5.281/0.023ms баг воспроизводится значительно быстрее. В среднем до падения проходит 0,1 - 0,3 секунды, а в случае с локальным запуском код работает до падения минимум несколько секунд. Но тут boost 1.46.0 и CentOS с родным GCC 4.1.2. |
| Автор: boostcoder 9.4.2011, 15:49 | ||||
сделал. на локальной машине. в аттаче. это 7z архив. расширение сменил потому что форум не позволяет приаттачивать 7z. скажите как сделать? а то я не очень разбираюсь в снифферах. Добавлено @ 15:52 в последних исходниках теста, сервер, я так и сделал. а клиенту, помимо того, что отправлять сообщения в цикле, нужно еще и принимать ответы от сервера. потому там я оставил один поток. Добавлено через 11 минут и 24 секунды
проверил. у меня ничего не изменилось. ubuntu-10.10 gcc-4.5.1 boost-1.46.1 |
| Автор: MAKCim 9.4.2011, 16:01 | ||
boostcoder,
эмм так а в чем проблема это все делать в одном потоке? делаешь async_read, потом async_write далее run и уже в обработчиках written/readed генерируешь новые операции... или я ошибаюсь? Добавлено через 1 минуту и 14 секунд в общем нужно, имхо, избавиться от более одного потока в клиенте и сервере и снова проверить Добавлено через 2 минуты и 4 секунды вот я не понимаю, как этот скрин был получен |
| Автор: boostcoder 9.4.2011, 16:11 | ||||||
перед async_write данные нужно приготовить. допустим, добавлю функцию prepare_data, из нее вызову async_write. но если prepare_data вызвать снова из written - то это будет уже не тот тест. т.к. written вызывается когда, согласно документации:
но не понятно, куда "written". в какой-то буфер ОС? на другой конец? кстати, сейчас обратил внимание на это:
т.е. я правильно понимаю, что пока не вызовется хэндлер async_write, нельзя запускать новую операцию записи? с тех пор как познакомился с этим багом, это примечание читал много раз, но обратил внимание только сейчас... Добавлено @ 16:11 просмотром в mc. Добавлено @ 16:14 MAKCim, в тех логах что выложил последними, проглядывается такое: ![]() и такое: ![]() это в файле server_in.log |
| Автор: MAKCim 9.4.2011, 16:17 | ||||
выходит так
думаю, когда возвратились из системного вызова sendmsg, это и есть written Добавлено через 26 секунд так а причем тут tcpdump тогда? |
| Автор: boostcoder 9.4.2011, 16:21 | ||
или же имеется ввиду, не вызывать эту функцию из нескольких потоков для одного сокета? ибо я всегда так это примечание и понимал. |
| Автор: MAKCim 9.4.2011, 16:25 | ||
если рассуждать логически, об этом (про потоки) было бы сказано, хотя не факт, конечно поэтому я и предлагаю как-нибудь исхитриться и не юзать таки более одного потока |
| Автор: boostcoder 9.4.2011, 16:27 | ||
нет. те скрины что сделаны tcpdump`ом, я комментировал как скрины сделанные tcpdump`ом. вот они: http://clip2net.com/clip/m47996/1300879093-1-262kb.png http://clip2net.com/clip/m47996/1300879448-2-27kb.png http://clip2net.com/clip/m47996/1300879665-3-21kb.png http://clip2net.com/clip/m47996/1300879710-4-22kb.png и последние два: http://clip2net.com/clip/m47996/1302213996-clipzb3346-11kb.png http://clip2net.com/clip/m47996/1302214240-clipsa3346-4kb.png т.е., на сколько я понял, для продолжения, нужно переписать клиент так, чтоб следующая запись производилась по окончанию предыдущей? ок. сейчас. Добавлено через 38 секунд
ок. сейчас делаю. |
| Автор: boostcoder 9.4.2011, 17:25 | ||||||
| переписал как договаривались, без единого потока, и с запуском следующей операции записи по завершению предыдущей. работает значительно дольше, но все равно валится(только при запуске клиента и сервера локально): исходники + логи, тут: http://rghost.ru/5141178 выложу в тему исходники. может я в коде что-то намудрил.. сам не замечаю.. клиент:
сервер:
общий хидер:
на клиент, в один прекрасный, приходит это: ![]() тут две ошибки: 1. склеились две части "sc". 2. клиент ожидает 3574549, а пришло 3574550. Добавлено @ 17:32 вот что еще странно. в server_out.log есть такое: ![]() т.е. одно и то же значение повторяется дважды. хотя, в server_in.log этого нет: ![]() странно.. |
| Автор: boostcoder 9.4.2011, 18:17 |
| запустил сервер на удаленной машине - вроде работает. уже минут 10ть. ... уже пол часа. |
| Автор: boostcoder 9.4.2011, 18:53 | ||
а возврат из sendmsg() когда происходит? что-то немогу в доках найти этого момента.. дело в том, что скорость очень сильно упала. сейчас, используется около 17% от пропускной способности. чем это может быть вызвано? |
| Автор: boostcoder 9.4.2011, 19:10 | ||
возможно это из-за того, что происходит почти симметричных обмен? т.е. кол-во входящих и исходящих данных почти равно. сейчас имею такую скорость обмена для одного клиента в секунду. что больше чем требуется.
вот только решить бы еще проблему порчи данных при запуске клиента и сервера локально.. |
| Автор: phprus 9.4.2011, 19:14 | ||
А в сервере это условие выполняется? |
| Автор: boostcoder 9.4.2011, 19:23 |
да. сервер реализован по принципу реакции. пришли данные от клиента - отсылается ответ. и только после этого запускается следующая операция чтения. Добавлено через 4 минуты и 41 секунду коды несколькими постами выше. |
| Автор: boostcoder 9.4.2011, 19:46 | ||
наверное тогда же, когда и send(). но в тестах, сокеты находятся в неблокирующем режиме.
получается что нет. т.к. изменил клиента и сервера так, что клиент только шлет, а сервер только принимает. никаких ответов обратно не шлется. скорость не увеличилась... ... увеличил размер массивов с 13 байт до 1024 байт. использование канала не увеличилось. получается так, что тормозом в данной ситуации является sendmsg(). |
| Автор: boostcoder 9.4.2011, 20:27 |
| запустил параллельно тесту торрент-клиент, и все стало еще интересней в client_out.log стало наблюдаться такое: ![]() и такое: ![]() и такое: ![]() и т.д... в server_in.log такое: ![]() и такое: ![]() бред какой-то... последняя версия исходников во вложении. |
| Автор: mes 9.4.2011, 20:37 | ||||
что то этот момент меня сильно смущает.. |
| Автор: boostcoder 9.4.2011, 20:49 |
| mes, что именно в этом моменте Вас смущает? |
| Автор: mes 9.4.2011, 20:55 |
| start_write из writen.. |
| Автор: boostcoder 9.4.2011, 20:58 |
| и? запуск следующей записи когда предыдущая завершилась. |
| Автор: mes 9.4.2011, 21:10 |
не знаю, но интуитивно глаз спотыкается на этом месте.. остальной код подозрений не вызывает.. |
| Автор: boostcoder 9.4.2011, 21:26 |
| mes, на то он и _completion_handler_ чтоб из него производить некоторые действия по окончанию асинхронной операции. |
| Автор: mes 9.4.2011, 21:30 |
| в бустовых примерах подобное применение имеется ? |
| Автор: boostcoder 9.4.2011, 21:41 |
конечно. почти в каждом примере, где одна асинхронная операция создает другую. зы да и чем вызов start_write() из хэндлера, отличается от вызова из какого-то другого места? |
| Автор: boostcoder 9.4.2011, 22:18 | ||
| странно все это.. при загрузке сети, иногда валится, иногда нет... любопытно, почему когда валится, в хэндлер не передается ошибка. хотя должна. т.е. из этого следует, что asio думает что все гуд Добавлено через 6 минут и 54 секунды опять свалилось :( клиент ожидал 8475039, а получил 84 ![]() обратите внимание. значение 8475038 повторяется дважды. и еще одна странность. клиент упал с таким сообщением:
а пришло 847 Добавлено через 12 минут и 27 секунд |
| Автор: mes 9.4.2011, 22:52 |
| попробуйте обнулять тот буффер, который создаете.. |
| Автор: mes 9.4.2011, 23:31 | ||
а записанное выглядит ровно, без сомнений ? Добавлено через 49 секунд да, тут зря я придрался.. |
| Автор: boostcoder 9.4.2011, 23:50 | ||
попробовал. на обоих сторонах. при загруженной сети, клиент свалился с таким сообщением:
во входящем буфере клиента было это: ![]() опять одно и то же значение повторяется дважды. сервер отправил это: ![]() и снова непонятка... клиент говорит что получено 92410. но такого сообщения в входящих нет! это ###.. Добавлено @ 23:51 на скринах, где я упоминаю server_*.log и client_*.log - это и есть то, что записано. т.е. сырые данные. Добавлено @ 23:55 кстати! клиент падает по необработанному исключению. а в этом случае, файловые буфера сброшены не будут. потому я в логах не увижу последних данных. сейчас повешу обработчик, и повторю тест. |
| Автор: mes 10.4.2011, 00:00 |
какой именно буффер ? а то что то я запутался.. Добавлено через 1 минуту и 37 секунд ну это как раз не удивительно, Вы не проверяете кол-во прочитанных байтов... |
| Автор: boostcoder 10.4.2011, 00:02 |
вы последний выложенный мной архив с исходниками качали? смотрели их? чтоб я знал с какой стороны объяснять.. |
| Автор: mes 10.4.2011, 00:06 |
| попробуйте убрать из readed весь анализ приходящих данных и оставить только запись в файл.. только буффер не забудьте обнулять.. да смотрел и как раз смотрю их.. |
| Автор: boostcoder 10.4.2011, 00:11 | ||
вот это?:
как же я тогда узнаю об ошибке? там есть файл wrappers.cpp, его Максим добавил. логи пишутся из оберток над sendmsg() и recvmsg(). т.е. по идее на 100% точно отражают происходящее. |
| Автор: mes 10.4.2011, 00:14 | ||
поставьте после каждой записи сырых данных еще запись какой нибудь последовательности.. Добавлено через 1 минуту и 24 секунды если вы о исключении, то запись дополнительной последовательности в файл должна показать причину.. |
| Автор: boostcoder 10.4.2011, 00:17 | ||
поясните. не понял. Добавлено через 3 минуты и 58 секунд еще, на сервере во входящих есть такое: ![]() в hex: ![]() т.е. три нуля. |
| Автор: mes 10.4.2011, 00:22 |
| после каждого сброса буффера в файл записывайте туда еще например "$$$" чтоб наглядно бросалась в глаза.. |
| Автор: boostcoder 10.4.2011, 00:25 | ||
но мне не известно когда буфера сбрасываются. все что я могу, это принудительно вызывать fflush(). т.е. предлагаете вставить вызовы fflush() в обертки над sendmsg() и recvmsg() ? Добавлено через 6 минут и 4 секунды mes, я обернул код клиента в try-catch блок. теперь буфера не потеряются. есть ли смысл сбрасывать буфера после каждой записи в файл? Добавлено через 11 минут и 5 секунд теперь свалился с таким сообщением:
в client_in.log такое: ![]() число 6690985 повторяется дважды. хотя второе должно было быть 6690986 и пропал символ c и : |
| Автор: mes 10.4.2011, 00:39 |
| чего то мы друг друга не понимаем.. я предлагаю Вам грубо говоря просто перенаправить получаемое с сокета в файл.. только добавить разделитель, после каждой операции чтения.. и без того исключения на получаемый индекс чтоб посмотреть, что будет в файле.. а окончание можно узнать по числу итераций, превышающее с запасом итерацию с возникновением ошибки.. |
| Автор: boostcoder 10.4.2011, 00:41 |
сервер вот что отправил:![]() последние две строки повторяются. |
| Автор: mes 10.4.2011, 00:42 |
| в общем у меня сложилось впечатление, что у Вас были две ошибки, которые создали эту ситуацию.. 1. чтение не полной записи с сокета, а следовательно невозможность получения ожидаемого индекса используемым способом 2. не обнуление буффера, которое приводило к появлению неожидаемых символов.. |
| Автор: boostcoder 10.4.2011, 00:46 | ||||||
еще раз свалилось.![]() тут уже повторение с предыдущими запусками. опять повтор. и опять пропали два символа. Добавлено @ 00:48
это от меня не зависит. согласно документации, хэндлер вызовется в случае:
с другой стороны, в последних трех скринах ситуация повторилась.
это я сделал. |
| Автор: mes 10.4.2011, 01:00 |
сорри.. наверно ночь сказывается, но я так и не понял откуда данные попадают в этот лог.. Добавлено через 1 минуту и 8 секунд не в сбрасывании цель, а в добавлении визуального разделителя.. Добавлено через 7 минут и 5 секунд просто Вы не в праве применять scanf до тех пор пока не наполните свой буффер.. |
| Автор: boostcoder 10.4.2011, 01:08 | ||||||
в client.cpp есть такой код:
в wrappers.cpp есть такое:
Добавлено через 1 минуту и 11 секунд
так scanf() вызывается в хэндлере. а хэндлер вызывается тогда, когда указанный объем прочитан. |
| Автор: mes 10.4.2011, 01:11 | ||
сделайте strlen перед этим для проверки и будет ясно там ли мы ищем причину.. Добавлено @ 01:15 тогда, если подозрения правильные, то ошибки должны быть в server_out и в client_in но не в двух других.. |
| Автор: boostcoder 10.4.2011, 05:38 | ||||||||||
сделал.
Вы правы. вот только я немогу понять закономерность... клиент свалился с таким сообщением:
где: "expected: 10661261" - правильно. это и должно было прийти. "get: 1066126" - тут, внезапно, пропал последний знак. "buf: 1066126" - тут его тоже нет. "1expected: 10661262" - а ни это ли наша пропавшая единичка?;) на ее месте должен быть символ перевода строки. "get: 661262" - тут нет первых двух знаков. т.е. "10" "buf: 0661262" - а тут нет только первой единички. сервер получил это: ![]() а отослал это: ![]() значение 10661262 повторяется дважды. а клиент получил это: ![]() ну что, кто-то раскусил головоломку? ;) последняя версия кодов. клиент:
сервер:
|
| Автор: boostcoder 10.4.2011, 06:05 |
| я сделал это!!! ща расскажу.. |
| Автор: boostcoder 10.4.2011, 06:28 | ||||||
обратите внимание на этот код из сервера:
последовательность такая: readed() вызывается когда данные прочитаны. из него я запускаю операцию записи и операцию чтения. и тут ошибка! операцию чтения нужно запускать из хэндлера операции записи. т.е. мало того что в доке намекается на то, что нельзя выполнять две операции записи и чтения одновременно, так оказывается что выполнять чтение пока не завершена запись(и наоборот) тоже нельзя! проверил на загруженной сети, и на локалке. все работает как надо. правильные исходники в аттаче. зы немного о впечатлениях. да, когда говорят что дока по бусту непонятная - правильно говорят. когда говорят что многое в бусте нелогично - правильно говорят. почему? да потому, что я за ночь прочел всю доку по asio от корки до корки, и не нашел ничего о том, что нельзя выполнять операцию на сокете до тех пор, пока предыдущая не завершилась! единственное что как-то на это намекает, это цитаты:
но не в одной из цитат не говорится о том, что нельзя выполнять запись пока не завершилось чтение, и на оборот! а должно быть выделено красным, и вставлено на каждой странице доки! эх... держите меня семеро MAKCim, mes, phprus, всем сильно благодарен за ваше терпение. всем спасибо. |
| Автор: boostcoder 10.4.2011, 06:57 |
| но мне все равно не понятно, каким образом операция чтения запущенная одновременно с операцией записи может портить данные которые записываются |
| Автор: boostcoder 10.4.2011, 07:31 | ||
удивительно... но использование канала наладилось. 94%
|
| Автор: MAKCim 10.4.2011, 10:43 | ||
предположу start_read->async_read->readed причем async_read->readed может быть синхронным (если данные уже получены) в этом случае имеем два последовательных async_write без вызова written |
| Автор: phprus 10.4.2011, 11:38 | ||
А если использовать read и write параллельно с разными, несвязанными данными, то баг тоже воспроизведется? Если да, то это будет по сути баг в ASIO. TCP полнодуплексный протокол и может работать одновременно в обе стороны. На каком канале? 100 Мбит/с? |
| Автор: mes 10.4.2011, 12:03 | ||||
у меня подозрения, что Вы не с причиной разобрались, а просто создали благоприятные условия, в которых _баг_ не/реже проявляется.. так же как и секундой слипа.. Добавлено через 10 минут и 47 секунд
Добавлено через 11 минут и 34 секунды рано эмоции выпустили |
| Автор: mes 10.4.2011, 12:19 | ||||||
данные не портятся, просто приходят не одним куском , а несколькими, на что Вам намекал strlen.. Добавлено через 1 минуту и 55 секунд а Вы их неправильно интерпретировали неполные блоки, что и служило поводом для исключения.. и еще сервер получив неправильный блок, переправлял его как полный клиенту.. поэтому ошибка только в логах server_out и client_in.. Добавлено через 3 минуты и 30 секунд т.е. неправильная интерпретация полученных данных сервером, создавала проблему с логами, а неправильная интерпретаця клиентом вызывало исключение.. Добавлено через 5 минут и 54 секунды в общем у Вас получился пример, ловко собравший в себе несколько похожих ошибок, и устранение одной, изменяло ситуацию, но не исправляло ее - именно это и привело к путанице.. Добавлено через 9 минут и 14 секунд хотя помимо названных причин, вроде еще одна должна быть для полной картины.. Добавлено через 13 минут и 25 секунд
не туда акцент.. проблема может быть не из за того, как говорится в цитате, что операция чтения запущена наряду с операцией записи, а в том что, например, при чтении подряд можно запустить две операции записи.. |
| Автор: boostcoder 10.4.2011, 12:34 | ||||||
подозреваю что да. тут не данные навязывают ограничения, а asio.
возможно это действительно не баг, а ограничение реализации asio. почитай: http://boost.2283326.n4.nabble.com/Multiple-async-operations-on-a-socket-td2579621.html да. завтра смогу проверить на 1гбит канале.
так же, прочтите по ссылке. Igor R. - это второй человек в поддержке asio. я полагаю весьма компетентный. да, можно. но не при помощи async_write(). нужно писать обертку над сокетом(сегодня этим и займемся ;) ), и в ней использовать socket::async_write_some() и socket::async_read_some(). на сколько я понял из того что по ссылке. Добавлено через 1 минуту тест работает почти 7 часов. |
| Автор: mes 10.4.2011, 12:36 | ||||||
| кстати комбинация с перемещением start_read во written поможет только для пинг понговского обмена, как у Вас в примере.. при нарушении симметрии на чтение запись, опять начнутся проблемы при такой архитектуре.. Добавлено через 2 минуты и 21 секунду будет работать, потому что Вы искусственно создали благоприятные условия.. шаг влево или вправо возродит этот баг в каком то из его проявлений.. Добавлено через 2 минуты и 40 секунд
сейчас.. Добавлено через 5 минут и 3 секунды
нет не поняли и уверен, что не это служило поводом для багов у Вас в примере.. Добавлено через 7 минут и 17 секунд
ага так и предполагалось, из за чего ограничение на совместную запись/чтение.. но это никак не может служить ограничением на совмещением записи с чтением.. |
| Автор: boostcoder 10.4.2011, 13:21 |
| mes, насколько я понимаю, одновременное чтение/запись нужно реализовать согласно этому принципу: http://www.boost.org/doc/libs/1_46_0/doc/html/boost_asio/overview/core/reactor.html Добавлено через 1 минуту и 37 секунд MAKCim, скажите, sendmsg() и recvmsg() можно использовать одновременно? т.е. с одним сокетом. |
| Автор: mes 10.4.2011, 13:24 | ||||
|
| Автор: MAKCim 10.4.2011, 13:30 | ||
да входной и выходной буферы сокета независимы я даже больше скажу, можно одновременно из разных потоков делать sendmsg() правда насчет атомарности записи данных (данные каждого sendmsg() запишутся непрерывно) не уверен |
| Автор: mes 10.4.2011, 13:30 | ||
нужно ? можно и как у Вас было, только порядок навести.. |
| Автор: boostcoder 10.4.2011, 13:31 |
| mes, я это видел. но я никогда такого не проделывал прежде. и тем более с sendmsg() и recvmsg(). я просто хочу понять, это ограничение asio, или OS-API. |
| Автор: mes 10.4.2011, 13:32 |
| мне кажется Вы причины проблемы до сих пор не поняли, из за этого с ужасом ищете обходные варианты.. Добавлено @ 13:32 что Вы подразумеваете под "это" ?.. |
| Автор: MAKCim 10.4.2011, 13:33 |
| еще раз убедился в том, как С++ на примере boost'а усложняет жизнь было бы что-то другое, можно было посмотреть реализацию и выяснить все вопросы лезть же в boost удовольствие сомнительное ;) |
| Автор: boostcoder 10.4.2011, 13:36 | ||||
понял. спасибо. значит нужно автора asio закидать письмами с четкими вопросами. что конкретно нужно изменить? я тоже понимаю что что-то не так. но согласно приведенной ссылке, во втором посте есть такое:
мне не кажется, что тут говорится о том, что можно читать и писать одновременно? |
| Автор: mes 10.4.2011, 13:37 | ||
так Вам все об этом говорят |
| Автор: boostcoder 10.4.2011, 13:39 | ||
но не писать же свою реализацию сокетов со всем вытекающим... я еще неделю назад подумывал использовать http://pocoproject.org/. но это не удобно, тянуть в проект еще что-то. у меня проект полностью написан с использованием буста. ну еще openssl... |
| Автор: mes 10.4.2011, 13:39 | ||||
пока вопросы возникшие в этой теме освещены документацией... не смотря на то, что она действительно скудная.. ;) Добавлено через 41 секунду asio Вас уже испугало ? Добавлено через 5 минут и 47 секунд при чтении не делать start_write, a добавлять в очередь, из которой забирать по writen.. Добавлено через 8 минут и 47 секунд
да, но палка о двух концах, усложняет при отсутствии хорошей и ясной литературы .. но облегчает пользование при понимании .. |
| Автор: boostcoder 10.4.2011, 13:49 | ||||
это не так. в последней версии исходников, я сделал проверку на сервере, на предмет порченных/неожидаемых данных. сервер ни разу не обнаружил несоответствие во входящих данных. Добавлено через 1 минуту и 50 секунд
переделаю. не вопрос. |
| Автор: mes 10.4.2011, 13:56 | ||
значит тогда клиент в порядке.. получается, что рубились только на сервере.. и должен стать порядок и на сервере |
| Автор: boostcoder 10.4.2011, 14:13 | ||||||
хм.. а это очень любопытно.. Добавлено @ 14:15 а если очередь пуста, то по какому событию делать проверку/перезапуск отправки из очереди? Добавлено @ 14:16 ужо 2.2 мильярда) total requests : 2212348580 total answers : 2212269496 Добавлено @ 14:17
хотя.. не вижу принципиальной разницы.. в чем она? Добавлено @ 14:22
а разве sendmsg() и recvmsg() не используют writev() и readv() ? хотя ни в одной из моих книжек по программированию для линукс, не говорится об атомарности записи/чтения... погуглю.. |
| Автор: mes 10.4.2011, 15:21 | ||
представьте ситуацию когда на два чтения должна происходить одна запись Добавлено через 1 минуту и 46 секунд
добавить флаг.. если очередь пуста и нет активной write, то после добавления стартовать ее, иначе просто добавлять.. |
| Автор: phprus 10.4.2011, 17:09 |
Напиши, пожалуйста, результаты (% использования канала) и RTT канала. |
| Автор: boostcoder 10.4.2011, 18:02 | ||
| phprus, ок. отпишусь. mes, вот что получилось. изменил только сервер. оно даже работает
|
| Автор: boostcoder 10.4.2011, 18:20 |
| зы нехочу постоянно помнить о познаных ограничениях. хочу обертку над сокетом. умную. удобную. |
| Автор: mes 10.4.2011, 18:55 | ||||||||
а разве asiо не предлагает ? ту которую Вы раньше применяли, в той длинной теме.. Добавлено @ 18:58 оглянувшись назад, можно оценить насколько пророчески были слова MAKCim
|
| Автор: boostcoder 10.4.2011, 18:59 | ||
что-то не помню. ладно, тему можно закрывать. а по поводу обертки создам новую тему. в общем разделе Добавлено через 52 секунды
это да. не в первый раз раз себя на таком ловлю... эх.. |
| Автор: MAKCim 10.4.2011, 19:04 | ||
нет Добавлено через 4 минуты и 45 секунд boostcoder, сейчас, кстати посмотрел код ядра и немного офигел http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/net/ipv4/tcp.c#L914 что-то я не вижу локов это наводит на мысль, что в linux'е лучше не пытаться писать в сокет из более одного потока без своих локов Добавлено через 7 минут и 56 секунд это нормально, психика у нас устроена таким образом, что мы априори не хотим признавать, что ошибка может быть у нас поэтому ищем оправдание себе в виде желания увидеть баг в другом просто когда много раз об это спотыкаешься, начинаешь относится к себе более критически дабы решить проблему быстрее |
| Автор: boostcoder 10.4.2011, 19:19 | ||
это правда |
| Автор: MAKCim 10.4.2011, 19:24 |
| короче я нагнал лок там есть в самом верху, я проглядел, паника отменяется |
| Автор: boostcoder 10.4.2011, 19:24 | ||||||
в структуре msghdr есть такой член:
эта же структура используется и для http://linux.die.net/man/2/writev. вот я и подумал, что в sendmsg() используется writev() дабы не плодить код.
ну вот, еще полезная информация. Добавлено через 52 секунды это он? :
|
| Автор: MAKCim 10.4.2011, 19:26 |
ага |
| Автор: boostcoder 11.4.2011, 11:04 |
| phprus, привет. RTT = 50-54ms в клиенте и сервере пришлось размер массивов увеличить до 10к, т.к. они съедали ядро из-за огромного кол-ва аллокаций и прочего. использование канала ~82% т.е. уверенно 800 мбит. |
| Автор: boostcoder 11.4.2011, 18:59 |
| что-то заблудился.. не понимаю, почему этот код бажил - а код с очередью нет? Добавлено через 2 минуты ааа.. у меня новая запись запускалась еще до того как предыдущая завершилась |