Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > 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 - это то, что отправляется от сервера клиенту.
user posted image
что за непонятные данные присутствующие кроме архивов сериализации - мне не известно.
в точке 1 показан пример когда архив разбивается. в точке 2 показана остальная часть архива. этот архив на принимающей стороне восстанавливается правильно. ошибки не возникает.

далее...
в точке 3 происходит разбиение очередного архива, остальная часть которого появляется только в точке 5.
т.е. это именно та итерация(348), на которой происходит искажение архива, и как следствие - ошибка десериализации.
user posted image

далее...
в точке 4 должен находится остаток архива разбитого в точке 3.
user posted image

вот та часть, в которой появляется остаток от архива разбитого в точке 3.
user posted image

какие мысли?

Автор: Daevaorn 23.3.2011, 14:46
Где код?

Автор: boostcoder 23.3.2011, 15:13
Daevaorn, не нужен вам код. в коде все ок. архивы отправляются целые.
причину нужно искать в том коде, который добавляет к моим данным нечитаемые данные и разбивает мои данные на "фреймы".

Добавлено через 1 минуту и 35 секунд
я рассчитывал на то, что кто-то увидит знакомую ситуацию, и подскажет как решить.

зы
кстати, если между отправками данных поставить sleep на 1мс - то проблема решается полностью.

Автор: boostcoder 23.3.2011, 15:58
Цитата(boostcoder @  23.3.2011,  15:13 Найти цитируемый пост)
причину нужно искать в том коде, который добавляет к моим данным нечитаемые данные и разбивает мои данные на "фреймы".

кто-то знает кто может добавлять к моим данным нечитаемые данные и разбивать их на фреймы? это выполняет TCP стек? сетевая подсистема? просто не знаю как это называется...

Автор: boostcoder 24.3.2011, 15:32
прикладываю мегапростой пример воспроизводящий ошибку.

Автор: MAKCim 27.3.2011, 21:06
Цитата(boostcoder @  23.3.2011,  15:58 Найти цитируемый пост)
это выполняет TCP стек?

фрагментация осуществляется на сетевом уровне
к данным "мусор" добавляться не может

вы точно уверены, что в сокет все записывается правильно?
вы сравнивали, что передается в 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
Цитата(MAKCim @  27.3.2011,  21:06 Найти цитируемый пост)
фрагментация осуществляется на сетевом уровне
к данным "мусор" добавляться не может

тот мусор что на скринах, показывает tcpdump. в мой код этот мусор не поступает.

Цитата(MAKCim @  27.3.2011,  21:06 Найти цитируемый пост)
вы точно уверены, что в сокет все записывается правильно?

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

Цитата(MAKCim @  27.3.2011,  21:06 Найти цитируемый пост)
вы сравнивали, что передается в async_write() и в send()? 

нет. а как это сделать? добавить в недры asio вывод?
или есть какой-то еще способ?

Цитата(marykone @  27.3.2011,  21:17 Найти цитируемый пост)
послушайте трафик wireshark, и выложите сюда.

сейчас сделаю.

Цитата(MAKCim @  27.3.2011,  21:43 Найти цитируемый пост)
если постоянно записывать данные, а читать с задержкой 1 мс, то данные будут накапливаться в буфере сокета ядра...хм, может тут проблема

эм.. помогает задержка при записи, а не при чтении.

Цитата(MAKCim @  27.3.2011,  21:43 Найти цитируемый пост)
версия ядра какая?

я проверял не только на линукс...

клиентский код запускаю на: 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.
баг воспроизводится всегда :(
тестил так:
Цитата

клиент | сервер | локально/удаленно
-------------------------------------
WinXP    WinXP    локально
WinXP    Linux    удаленно
Win7     Win7     локально
Win7     Linux    удаленно
Linux    Linux    локально
Linux    Linux    удаленно

Автор: MAKCim 29.3.2011, 14:28
Цитата(boostcoder @  28.3.2011,  15:18 Найти цитируемый пост)
насколько это возможно утверждать на приведенном коде - да. просто код реально мегапрост, чтоб где-то мог закрасться баг. если конечно это не баг asio. в чем сомневаюсь.. 

в этом большая ошибка
я много раз на это же полагался
а в итоге как раз в мегапростом коде и был баг ;)


Цитата(boostcoder @  28.3.2011,  15:18 Найти цитируемый пост)
нет. а как это сделать? добавить в недры asio вывод?
или есть какой-то еще способ?

написать враперы над send/recv/... из Libc
насколько я знаю, достаточно создать что-то типа wrappers.c, там реализовать обертки
вот реальный код подмены fork из какого-то проекта...
Код

#ifndef NO_PROCESS_LOCK
static pid_t (*_fork)();

HOOK_FUNCTION pid_t fork()
{
    pid_t pid;
#ifdef DEBUG
    fprintf(stdout, "fork()\n");
#endif
    start_fork();
    pid = _fork();
    if (pid == 0)
    {
    }
    else if (pid > 0)
    {
        end_fork();
    }
    return pid;
}
#endif

int PLogInit()
{
#ifndef NO_PROCESS_LOCK
    void *ptr;
    if ((ptr = dlsym(RTLD_NEXT, "fork")) == NULL)
    {
        fprintf(stderr, "dlsym() failed\n");
        return -1;
    }
    _fork = ptr;
#endif
    return 0;
}


Добавлено через 3 минуты и 2 секунды
Цитата(boostcoder @  28.3.2011,  18:49 Найти цитируемый пост)
баг воспроизводится всегда :(

в таком случае я уверен на 99% баг где-то в коде
думаю в вашем, а не asio

Автор: phprus 2.4.2011, 22:22
Почитал я код и у меня возникло подозрение, что async_test() и asio в клиенте работают в разных потоках. А shared_array (как и shared_ptr) не может корректно передаваться между потоками (по крайней мере я пару месяцев назад не нашел такого способа).
Те когда выполняется код:
Код

boost::shared_array<char> buf(new char[send_buffer_size]);
sprintf(buf.get(), "cs:%8d\n", idx);
start_write(_sock, buf); // <----
if ( _ws ) {
     boost::this_thread::sleep(boost::posix_time::microseconds(1000));
}
 // <----

в отмеченных местах возникает состояние гонки, а так-же в функции 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
вручную управлять памятью страшно smile 

Автор: phprus 2.4.2011, 23:17
boostcoder
Везде да.
У меня к каждому клиенту привязан объект класса Client, который привязан к одному io_service и который гарантированно работает в одном потоке. В качестве буферов (которые и передаются asio) используется два std::vector<char>, как поля класса Client, а данные получаемые из другого потока в виде обычного указателя используются очень локализовано и после чего удаляются, по этому я и согласился на ручное управление памятью, а не стал дальше думать, как обойти такое ограничение shared_ptr.

Автор: boostcoder 8.4.2011, 00:44
убрал из кода все смарт-поинтеры.
проблема осталась smile 

на стороне клиента, сохраняю отправляемые массивы в файл, чтоб убедится в том, что массивы не портятся.
так и есть. массивы не искаженны.
user posted image

на стороне сервера, так же, сохраняю принятые массивы.
но тут, массив приходит искаженным:
user posted image

вот снифф этого участка, tcpdump`ом, на стороне клиента:
user posted image

а это на стороне сервера:
user posted image



на стороне клиента, как раз в то место где данные искаженны, добавлены какие-то данные. не мои.
видно, что на стороне сервера, массив, после прочтения при помощи asio, уже испорчен. но tcpdump показывает что массив не испорчен.

я в замешательстве smile 


выложу код в тему, в надежде, что кто-то внесет ясность в ситуацию.

клиент:
Код


#include "header.hpp"

/***************************************************************************/

int main(int argc, char** argv) {
   if ( argc != 4 ) {
      std::cout << "client ip port 0/1 - sleed disabled/enabled" << std::endl;
      return 0;
   }
   std::string ip = argv[1];
   boost::uint16_t port = boost::lexical_cast<boost::uint16_t>(argv[2]);
   bool wsleep = (argv[3][0] == '1');
   std::cout << "sleep " << (wsleep?"enabled":"disabled") << std::endl;
   
   FILE* in = fopen("client_in.log", "wb");
   FILE* out= fopen("client_out.log", "wb");
   if ( !out || !in ) {
      std::cout << "can`t open file!" << std::endl;
      return 1;
   }

   boost::asio::ip::tcp::endpoint endpoint(
      boost::asio::ip::address::from_string(ip), port
   );

   boost::asio::io_service ios;
   boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(ios));

   boost::thread thread(boost::bind(&boost::asio::io_service::run, &ios));
   
   boost::asio::ip::tcp::socket socket(ios);
   socket.connect(endpoint);

   boost::asio::socket_base::non_blocking_io non_blocking_io(true);
   socket.io_control(non_blocking_io);

   client_read(socket, in);

   for ( size_t idx = 0; idx < 100000000; ++idx ) {
      char* buf = new char[send_buffer_size];
      sprintf(buf, "cs:%8d\n", idx);
      start_write(socket, buf, out);
      if ( wsleep ) {
         boost::this_thread::sleep(boost::posix_time::microseconds(1000));
      }
   }

   std::cout
   << "send data to server finished!" << std::endl
   << "waiting for all ask`s from server..." << std::endl;

   work.reset();

   while ( counter ) {
      boost::this_thread::sleep(boost::posix_time::microseconds(1000));
      std::cout << "." << std::flush;
   }

   std::cout << std::endl << std::endl
   << "all ask`s received." << std::endl
   << "terminate client..." << std::endl;

   socket.cancel();
   socket.close();

   thread.join();
   fclose(in);
   fclose(out);
}

/***************************************************************************/

http://liveworkspace.org/code/15c1e78ea45184f18f1eebe7e08564a7

сервер:
Код


#include "header.hpp"

/***************************************************************************/

int main(int argc, char** argv) {
   if ( argc != 3 ) {
      std::cout << "server ip port" << std::endl;
      return 0;
   }
   std::string ip = argv[1];
   boost::uint16_t port = boost::lexical_cast<boost::uint16_t>(argv[2]);

   FILE* in = fopen("server_in.log", "wb");
   FILE* out= fopen("server_out.log", "wb");
   if ( !out || !in ) {
      std::cout << "can`t open file!" << std::endl;
      return 1;
   }

   boost::asio::ip::tcp::endpoint endpoint(
      boost::asio::ip::address::from_string(ip), port
   );
   
   boost::asio::io_service ios;
   boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
   boost::asio::ip::tcp::socket socket(ios);

   acceptor.accept(socket);
   std::cout << "new connection from: " << socket.remote_endpoint().address().to_string() << std::endl;
   
   boost::asio::socket_base::non_blocking_io non_blocking_io(true);
   socket.io_control(non_blocking_io);

   server_read(socket, in, out);

   ios.run();

   fclose(in);
   fclose(out);
}

/***************************************************************************/

http://liveworkspace.org/code/0a8e2cb728f85caa5d31f28c46ddf9e2

общий хидер:
Код


#ifndef _header_hpp_included_
#define _header_hpp_included_

#include <iostream>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/shared_array.hpp>
#include <boost/lexical_cast.hpp>

enum { recv_buffer_size = 13 };
enum { send_buffer_size = 13 };

volatile size_t counter = 0;

/***************************************************************************/

void client_readed(
   boost::asio::ip::tcp::socket&,
   char*,
   FILE*,
   const boost::system::error_code&
);

void client_read(
   boost::asio::ip::tcp::socket& sock,
   FILE* out
) {
   char* buf = new char[recv_buffer_size];
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &client_readed,
         boost::ref(sock),
         buf,
         out,
         boost::asio::placeholders::error
      )
   );
}

void client_readed(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out,
   const boost::system::error_code& e
) {
   if ( e ) {
      if ( !counter ) return;
      std::cout << "read handler: " << e.message() << std::endl;
      return;
   }

   fwrite(buf, recv_buffer_size, 1, out);

   counter--;

#ifdef _my_debug_
   printf("client_readed(): %s", buf);
   fflush(stdout);
#endif

   static size_t idx = 0;
   size_t tmp = 0;
   char* p = strchr(buf, ':');
   if ( p ) {
      p++;
      sscanf(p, "%8d", &tmp);
   } else {
      throw std::runtime_error("input data error!");
   }
   
   delete[] buf;
   
   if ( idx != tmp ) {
      std::ostringstream os;
      os << "read error. expected " << idx << " get " << tmp;
      throw std::runtime_error(os.str());
   }
   idx++;
   client_read(sock, out);
}

/***************************************************************************/

void writen(
   char*,
   FILE*,
   const boost::system::error_code&
);

void start_write(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out
) {
   counter++;
   boost::asio::async_write(
      sock,
      boost::asio::buffer(buf, send_buffer_size),
      boost::bind(
         &writen,
         buf,
         out,
         boost::asio::placeholders::error
      )
   );
}

void writen(
   char* buf,
   FILE* out,
   const boost::system::error_code& e
) {
   fwrite(buf, send_buffer_size, 1, out);
   delete[] buf;
   if ( e ) {
      std::cout << "writen(): " << e.message() << std::endl;
   }
}

/***************************************************************************/

void server_readed(
   boost::asio::ip::tcp::socket&,
   char*,
   FILE*,
   FILE*,
   const boost::system::error_code&
);
   
void server_read(
   boost::asio::ip::tcp::socket& sock,
   FILE* in,
   FILE* out
) {
   char* buf = new char[recv_buffer_size];
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &server_readed,
         boost::ref(sock),
         buf,
         in,
         out,
         boost::asio::placeholders::error
      )
   );
}

void server_readed(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* in,
   FILE* out,
   const boost::system::error_code& e
) {
   if ( e ) {
      std::cout << "read handler: " << e.message() << std::endl;
      return;
   }

#ifdef _my_debug_
   printf("server_readed(): %s", buf);
#endif

   fwrite(buf, recv_buffer_size, 1, in);
   
   static const char* ptr = "sc:";
   memcpy(buf, ptr, strlen(ptr));
   start_write(sock, buf, out);
   server_read(sock, in, out);
}

/***************************************************************************/

#endif // _header_hpp_included_


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 как предполагал.

Цитата(phprus @  8.4.2011,  10:02 Найти цитируемый пост)
Когда включен - TCP-сегменты ходят с данными ровно в 13 байт и у каждого стоит флаг PUSH (сразу отдавать на уровень выше, те приложению).
Когда sleep выключен сегменты содержат множество склееных блоков по 13 байт (возможно не целое число блоков).

это я тоже заметил. любопытно почему так происходит. но это сейчас не главное.

Цитата(phprus @  8.4.2011,  10:02 Найти цитируемый пост)
А что говорят по этому поводу на https://svn.boost.org/trac/boost/ ? Может быть имеет смысл написать Bug report? 

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

Автор: boostcoder 8.4.2011, 21:44
вот что еще странно...
в один прекрасный момент, сервер принимает это:
user posted image

в первой строке сверху - все хорошо.
во второй и третьей строке, вместо завершающего нуля, вставлен символ c
в четвертой строке, символа c вначале, попросту нет.

Автор: phprus 9.4.2011, 09:38
boostcoder
Если sleep сделать с временем ожидания, равным 0 ( boost::this_thread::sleep(boost::posix_time::microseconds(0)) ) то  проблема тоже уходит, а sleep(0) в теории должен вызывать передачу управления другому процессу, те в коде похоже зарылась гонка. Только не могу понять в каком коде.

Цитата(boostcoder @  8.4.2011,  15:13 Найти цитируемый пост)
та я потому и не писал, ибо все же не был уверен что это не мой баг в коде.
сейчас напишу..

Сообщите пожалуйста ссылку на тикет.

Автор: boostcoder 9.4.2011, 14:28
Цитата(phprus @  9.4.2011,  09:38 Найти цитируемый пост)
sleep(0) в теории должен вызывать передачу управления другому процессу

по идее - да.

Цитата(phprus @  9.4.2011,  09:38 Найти цитируемый пост)
те в коде похоже зарылась гонка

не спорю... но где?! и как побороть?! smile 

Автор: boostcoder 9.4.2011, 15:00
phprus, так у тебя этот баг тоже воспроизводиться?
скажи как тестируешь? версии ОС, компилятора, boost? локально, удаленно?

Автор: MAKCim 9.4.2011, 15:14
Цитата(boostcoder @  8.4.2011,  00:44 Найти цитируемый пост)
на стороне клиента, как раз в то место где данные искаженны, добавлены какие-то данные. не мои.

ну так это может быть пустой пакет с tcp флагами...
хотя я не очень понимаю формат снифа

еще проверь у себя версию в аттаче
там логинг asio заменен а логинг sendmsg/recvmsg

Автор: phprus 9.4.2011, 15:25
Цитата(boostcoder @  9.4.2011,  18:00 Найти цитируемый пост)
phprus, так у тебя этот баг тоже воспроизводиться?
скажи как тестируешь? версии ОС, компилятора, boost? локально, удаленно? 


Да, воспроизводится.
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
Цитата(MAKCim @  9.4.2011,  15:14 Найти цитируемый пост)
еще проверь у себя версию в аттаче
там логинг asio заменен а логинг sendmsg/recvmsg

сделал. на локальной машине.
в аттаче. это 7z архив. расширение сменил потому что форум не позволяет приаттачивать 7z.

Цитата(MAKCim @  9.4.2011,  15:14 Найти цитируемый пост)
хотя я не очень понимаю формат снифа

скажите как сделать? а то я не очень разбираюсь в снифферах.

Добавлено @ 15:52
Цитата(MAKCim @  9.4.2011,  15:28 Найти цитируемый пост)
вы пробовали запускать io_service::run не в отдельном потоке?

в последних исходниках теста, сервер, я так и сделал.
а клиенту, помимо того, что отправлять сообщения в цикле, нужно еще и принимать ответы от сервера. потому там я оставил один поток.

Добавлено через 11 минут и 24 секунды
Цитата(phprus @  9.4.2011,  09:38 Найти цитируемый пост)
Если sleep сделать с временем ожидания, равным 0 ( boost::this_thread::sleep(boost::posix_time::microseconds(0)) ) то  проблема тоже уходит

проверил. у меня ничего не изменилось.
ubuntu-10.10
gcc-4.5.1
boost-1.46.1

Автор: MAKCim 9.4.2011, 16:01
boostcoder, 
Цитата(boostcoder @  9.4.2011,  15:49 Найти цитируемый пост)
а клиенту, помимо того, что отправлять сообщения в цикле, нужно еще и принимать ответы от сервера. потому там я оставил один поток.

эмм
так а в чем проблема это все делать в одном потоке?
делаешь async_read, потом async_write
далее run и уже в обработчиках written/readed генерируешь новые операции...

или я ошибаюсь?

Добавлено через 1 минуту и 14 секунд
в общем нужно, имхо, избавиться от более одного потока в клиенте и сервере
и снова проверить

Добавлено через 2 минуты и 4 секунды
Цитата(boostcoder @  8.4.2011,  00:44 Найти цитируемый пост)
вот снифф этого участка, tcpdump`ом, на стороне клиента:

вот я не понимаю, как этот скрин был получен

Автор: boostcoder 9.4.2011, 16:11
Цитата(MAKCim @  9.4.2011,  16:01 Найти цитируемый пост)
делаешь async_read, потом async_write
далее run и уже в обработчиках written/readed генерируешь новые операции...

перед async_write данные нужно приготовить. допустим, добавлю функцию prepare_data, из нее вызову async_write. но если prepare_data вызвать снова из written - то это будет уже не тот тест. т.к. written вызывается когда, согласно документации:
Цитата

All of the data in the supplied buffers has been written. That is, the bytes transferred is equal to the sum of the buffer sizes.

но не понятно, куда "written". в какой-то буфер ОС? на другой конец?

кстати, сейчас обратил внимание на это:
Цитата

The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.

т.е. я правильно понимаю, что пока не вызовется хэндлер async_write, нельзя запускать новую операцию записи?
с тех пор как познакомился с этим багом, это примечание читал много раз, но обратил внимание только сейчас...

Добавлено @ 16:11
Цитата(MAKCim @  9.4.2011,  16:01 Найти цитируемый пост)
я не понимаю, как этот скрин был получен

просмотром в mc.

Добавлено @ 16:14
MAKCim, в тех логах что выложил последними, проглядывается такое:
user posted image

и такое:
user posted image

это в файле server_in.log

Автор: MAKCim 9.4.2011, 16:17
Цитата(boostcoder @  9.4.2011,  16:11 Найти цитируемый пост)
т.е. я правильно понимаю, что пока не вызовется хэндлер async_write, нельзя запускать новую операцию записи?

выходит так


Цитата(boostcoder @  9.4.2011,  16:11 Найти цитируемый пост)
но не понятно, куда "written". в какой-то буфер ОС? на другой конец?

думаю, когда возвратились из системного вызова sendmsg, это и есть written

Добавлено через 26 секунд
Цитата(boostcoder @  9.4.2011,  16:11 Найти цитируемый пост)
просмотром в mc. 

так а причем тут tcpdump тогда?

Автор: boostcoder 9.4.2011, 16:21
Цитата(boostcoder @  9.4.2011,  16:11 Найти цитируемый пост)
т.е. я правильно понимаю, что пока не вызовется хэндлер async_write, нельзя запускать новую операцию записи?

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

Автор: MAKCim 9.4.2011, 16:25
Цитата(boostcoder @  9.4.2011,  16:21 Найти цитируемый пост)
ли же имеется ввиду, не вызывать эту функцию из нескольких потоков для одного сокета? ибо я всегда так это примечание и понимал. 

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

Автор: boostcoder 9.4.2011, 16:27
Цитата(MAKCim @  9.4.2011,  16:17 Найти цитируемый пост)
так а причем тут tcpdump тогда? 

нет. те скрины что сделаны 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 секунд
Цитата(MAKCim @  9.4.2011,  16:25 Найти цитируемый пост)
предлагаю как-нибудь исхитриться и не юзать таки более одного потока

ок.
сейчас делаю.

Автор: boostcoder 9.4.2011, 17:25
переписал как договаривались, без единого потока, и с запуском следующей операции записи по завершению предыдущей.
работает значительно дольше, но все равно валится(только при запуске клиента и сервера локально):
исходники + логи, тут: http://rghost.ru/5141178

выложу в тему исходники. может я в коде что-то намудрил.. сам не замечаю..

клиент:
Код


#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>

#include "header.hpp"

/***************************************************************************/

void readed(
   boost::asio::ip::tcp::socket&,
   char*,
   FILE*,
   const boost::system::error_code&
);

void start_read(
   boost::asio::ip::tcp::socket& sock,
   FILE* out
) {
   char* buf = new char[recv_buffer_size];
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &readed,
         boost::ref(sock),
         buf,
         out,
         boost::asio::placeholders::error
      )
   );
}

void readed(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out,
   const boost::system::error_code& e
) {
   if ( e ) {
      std::cout << "read handler: " << e.message() << std::endl;
      return;
   }
   
   //   fwrite(buf, recv_buffer_size, 1, out);
   
   static size_t idx = 0;
   size_t tmp = 0;
   char* p = strchr(buf, ':');
   if ( p ) {
      sscanf(p+1, "%8d", &tmp);
   } else {
      throw std::runtime_error("input data error!");
   }
   
   delete[] buf;
   
   if ( idx != tmp ) {
      std::ostringstream os;
      os << "read error. expected " << idx << " get " << tmp;
      throw std::runtime_error(os.str());
   }
   idx++;
   start_read(sock, out);
}

/***************************************************************************/

void writen(
   boost::asio::ip::tcp::socket&,
   char*,
   FILE*,
   const boost::system::error_code&
);

void start_write(
   boost::asio::ip::tcp::socket& sock,
   FILE* out
) {
   static size_t idx = 0;
   
   char* buf = new char[send_buffer_size];
   sprintf(buf, "cs:%8d\n", idx);
   idx++;

   boost::asio::async_write(
      sock,
      boost::asio::buffer(buf, send_buffer_size),
      boost::bind(
         &writen,
         boost::ref(sock),
         buf,
         out,
         boost::asio::placeholders::error
      )
   );
}

void writen(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out,
   const boost::system::error_code& e
) {
   //   fwrite(buf, send_buffer_size, 1, out);
   delete[] buf;
   if ( e ) {
      std::cout << "writen(): " << e.message() << std::endl;
   }
   
   start_write(sock, out);
}

/***************************************************************************/

int main(int argc, char** argv) {
   if ( argc != 4 ) {
      std::cout << "client ip port 0/1 - sleed disabled/enabled" << std::endl;
      return 0;
   }
   if (init_wrappers())
   {
      return -1;
   }
   std::string ip = argv[1];
   boost::uint16_t port = boost::lexical_cast<boost::uint16_t>(argv[2]);
   bool wsleep = (argv[3][0] == '1');
   std::cout << "sleep " << (wsleep?"enabled":"disabled") << std::endl;
   
   FILE* in = fopen("client_in.log", "wb");
   FILE* out= fopen("client_out.log", "wb");
   dbg::in = in;
   dbg::out = out;
   if ( !out || !in ) {
      std::cout << "can`t open file!" << std::endl;
      return 1;
   }

   boost::asio::ip::tcp::endpoint endpoint(
      boost::asio::ip::address::from_string(ip), port
   );

   boost::asio::io_service ios;
   boost::asio::ip::tcp::socket socket(ios);
   socket.connect(endpoint);

   boost::asio::socket_base::non_blocking_io non_blocking_io(true);
   socket.io_control(non_blocking_io);

   start_write(socket, out);
   start_read(socket, in);

   ios.run();

   fclose(in);
   fclose(out);
}

/***************************************************************************/



сервер:
Код


#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>

#include "header.hpp"

/***************************************************************************/

void writen(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out,
   const boost::system::error_code& e
);

void start_write(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out
) {
   boost::asio::async_write(
      sock,
      boost::asio::buffer(buf, send_buffer_size),
      boost::bind(
         &writen,
         boost::ref(sock),
         buf,
         out,
         boost::asio::placeholders::error
      )
   );
}

void writen(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* out,
   const boost::system::error_code& e
) {
   //   fwrite(buf, send_buffer_size, 1, out);
   delete[] buf;
   if ( e ) {
      std::cout << "writen(): " << e.message() << std::endl;
   }
}

void readed(
   boost::asio::ip::tcp::socket&,
   char*,
   FILE*,
   FILE*,
   const boost::system::error_code&
);

void start_read(
   boost::asio::ip::tcp::socket& sock,
   FILE* in,
   FILE* out
) {
   char* buf = new char[recv_buffer_size];
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &readed,
         boost::ref(sock),
         buf,
         in,
         out,
         boost::asio::placeholders::error
      )
   );
}

void readed(
   boost::asio::ip::tcp::socket& sock,
   char* buf,
   FILE* in,
   FILE* out,
   const boost::system::error_code& e
) {
   if ( e ) {
      std::cout << "read handler: " << e.message() << std::endl;
      return;
   }
   
   #ifdef _my_debug_
   printf("server_readed(): %s", buf);
   #endif
   
   //   fwrite(buf, recv_buffer_size, 1, in);
   
   static const char* ptr = "sc:";
   memcpy(buf, ptr, strlen(ptr));
   start_write(sock, buf, out);
   start_read(sock, in, out);
}

/***************************************************************************/

int main(int argc, char** argv) {
   if ( argc != 3 ) {
      std::cout << "server ip port" << std::endl;
      return 0;
   }
   if (init_wrappers())
   {
      return -1;
   }
   std::string ip = argv[1];
   boost::uint16_t port = boost::lexical_cast<boost::uint16_t>(argv[2]);

   FILE* in = fopen("server_in.log", "wb");
   FILE* out= fopen("server_out.log", "wb");
   dbg::in = in;
   dbg::out = out;
   if ( !out || !in ) {
      std::cout << "can`t open file!" << std::endl;
      return 1;
   }

   boost::asio::ip::tcp::endpoint endpoint(
      boost::asio::ip::address::from_string(ip), port
   );
   
   boost::asio::io_service ios;
   boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
   boost::asio::ip::tcp::socket socket(ios);

   acceptor.accept(socket);
   std::cout << "new connection from: " << socket.remote_endpoint().address().to_string() << std::endl;
   
   boost::asio::socket_base::non_blocking_io non_blocking_io(true);
   socket.io_control(non_blocking_io);

   start_read(socket, in, out);

   ios.run();

   fclose(in);
   fclose(out);
}

/***************************************************************************/



общий хидер:
Код


#ifndef _header_hpp_included_
#define _header_hpp_included_

#include <cstdio>

enum { recv_buffer_size = 13 };
enum { send_buffer_size = 13 };

namespace dbg  {
   extern FILE *in;
   extern FILE *out;
};

extern int init_wrappers();

#endif // _header_hpp_included_



на клиент, в один прекрасный, приходит это:
user posted image

тут две ошибки:
1. склеились две части "sc".
2. клиент ожидает 3574549, а пришло 3574550.

Добавлено @ 17:32
вот что еще странно.
в server_out.log есть такое:
user posted image
т.е. одно и то же значение повторяется дважды.

хотя, в server_in.log этого нет:
user posted image

странно..

Автор: boostcoder 9.4.2011, 18:17
запустил сервер на удаленной машине - вроде работает. уже минут 10ть.

...
уже пол часа.

Автор: boostcoder 9.4.2011, 18:53
Цитата(MAKCim @  9.4.2011,  16:17 Найти цитируемый пост)
думаю, когда возвратились из системного вызова sendmsg, это и есть written

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

Автор: boostcoder 9.4.2011, 19:10
Цитата(boostcoder @  9.4.2011,  18:53 Найти цитируемый пост)
сейчас, используется около 17% от пропускной способности.

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

requests: 30074
answers : 29336

requests: 35086
answers : 31368

requests: 15037
answers : 19062

requests: 21615
answers : 22223

requests: 23491
answers : 22525

requests: 25062
answers : 23057

requests: 15037
answers : 16040

requests: 15037
answers : 15593

requests: 20049
answers : 20272

requests: 30074
answers : 25723

requests: 25061
answers : 27965

requests: 25558
answers : 28553

requests: 29574
answers : 22346

requests: 20049
answers : 25841




вот только решить бы еще проблему порчи данных при запуске клиента и сервера локально..

Автор: phprus 9.4.2011, 19:14
Цитата(boostcoder @  9.4.2011,  20:25 Найти цитируемый пост)
переписал как договаривались, без единого потока, и с запуском следующей операции записи по завершению предыдущей.
работает значительно дольше, но все равно валится:

А в сервере это условие выполняется?

Автор: boostcoder 9.4.2011, 19:23
Цитата(phprus @  9.4.2011,  19:14 Найти цитируемый пост)
А в сервере это условие выполняется?

да.
сервер реализован по принципу реакции. пришли данные от клиента - отсылается ответ. и только после этого запускается следующая операция чтения.

Добавлено через 4 минуты и 41 секунду
коды несколькими постами выше.

Автор: boostcoder 9.4.2011, 19:46
Цитата(boostcoder @  9.4.2011,  18:53 Найти цитируемый пост)
а возврат из sendmsg() когда происходит?

наверное тогда же, когда и send(). но в тестах, сокеты находятся в неблокирующем режиме.

Цитата(boostcoder @  9.4.2011,  19:10 Найти цитируемый пост)
возможно это из-за того, что происходит почти симметричных обмен? т.е. кол-во входящих и исходящих данных почти равно.

получается что нет. т.к. изменил клиента и сервера так, что клиент только шлет, а сервер только принимает. никаких ответов обратно не шлется. скорость не увеличилась...

...

увеличил размер массивов с 13 байт до 1024 байт. использование канала не увеличилось.
получается так, что тормозом в данной ситуации является sendmsg().

Автор: boostcoder 9.4.2011, 20:27
запустил параллельно тесту торрент-клиент, и все стало еще интересней smile 

в client_out.log стало наблюдаться такое:
user posted image
и такое:
user posted image
и такое:
user posted image
и т.д...


в server_in.log такое:
user posted image
и такое:
user posted image

бред какой-то...

последняя версия исходников во вложении.

Автор: mes 9.4.2011, 20:37
Цитата

Код

void writen(
   ...
   start_write(sock, out);
}



что то этот момент меня сильно смущает..

Автор: 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,  19:58 Найти цитируемый пост)
и?

не знаю, но интуитивно глаз спотыкается на этом месте.. 
остальной код подозрений не вызывает.. 

Автор: boostcoder 9.4.2011, 21:26
mes, на то он и _completion_handler_ чтоб из него производить некоторые действия по окончанию асинхронной операции.

Автор: mes 9.4.2011, 21:30
в бустовых примерах подобное применение имеется ?

Автор: boostcoder 9.4.2011, 21:41
Цитата(mes @  9.4.2011,  21:30 Найти цитируемый пост)
в бустовых примерах подобное применение имеется ?

конечно. почти в каждом примере, где одна асинхронная операция создает другую.

зы
да и чем вызов start_write() из хэндлера, отличается от вызова из какого-то другого места?

Автор: boostcoder 9.4.2011, 22:18
странно все это..
при загрузке сети, иногда валится, иногда нет...
любопытно, почему когда валится, в хэндлер не передается ошибка. хотя должна. т.е. из этого следует, что asio думает что все гуд smile

Добавлено через 6 минут и 54 секунды
опять свалилось :(

клиент ожидал 8475039, а получил 84
user posted image

обратите внимание. значение 8475038 повторяется дважды.

и еще одна странность. клиент упал с таким сообщением:
Цитата

terminate called after throwing an instance of 'std::runtime_error'
  what():  read error. expected 8475039 get 84

а пришло 847  smile

Добавлено через 12 минут и 27 секунд
 smile 

Автор: mes 9.4.2011, 22:52
попробуйте обнулять тот буффер, который создаете..

Автор: mes 9.4.2011, 23:31
Цитата

//  fwrite(buf, recv_buffer_size, 1, out);

а записанное выглядит ровно, без сомнений ?

Добавлено через 49 секунд
Цитата(boostcoder @  9.4.2011,  20:26 Найти цитируемый пост)
 на то он и _completion_handler_ 

да, тут зря я придрался.. 

Автор: boostcoder 9.4.2011, 23:50
Цитата(mes @  9.4.2011,  22:52 Найти цитируемый пост)
попробуйте обнулять тот буффер, который создаете..

попробовал. на обоих сторонах.
при загруженной сети, клиент свалился с таким сообщением:
Цитата

terminate called after throwing an instance of 'std::runtime_error'
  what():  read error. expected 92410192 get 92410


во входящем буфере клиента было это:
user posted image
опять одно и то же значение повторяется дважды.

сервер отправил это:
user posted image


и снова непонятка... клиент говорит что получено 92410. но такого сообщения в входящих нет! smile 

это ###..

Добавлено @ 23:51
Цитата(mes @  9.4.2011,  23:31 Найти цитируемый пост)
а записанное выглядит ровно, без сомнений ?

на скринах, где я упоминаю server_*.log и client_*.log - это и есть то, что записано. т.е. сырые данные.

Добавлено @ 23:55
Цитата(boostcoder @  9.4.2011,  23:50 Найти цитируемый пост)
но такого сообщения в входящих нет!

кстати!
клиент падает по необработанному исключению. а в этом случае, файловые буфера сброшены не будут. потому я в логах не увижу последних данных.

сейчас повешу обработчик, и повторю тест.

Автор: mes 10.4.2011, 00:00
Цитата(boostcoder @  9.4.2011,  22:50 Найти цитируемый пост)
во входящем буфере клиента было это:

какой именно буффер ? а то что то я запутался..

Добавлено через 1 минуту и 37 секунд
Цитата(boostcoder @  9.4.2011,  22:50 Найти цитируемый пост)
, клиент свалился с таким сообщением:

ну это как раз не удивительно, Вы не проверяете кол-во прочитанных байтов... 

Автор: boostcoder 10.4.2011, 00:02
Цитата(mes @  10.4.2011,  00:00 Найти цитируемый пост)
какой именно буффер ?

вы последний выложенный мной архив с исходниками качали? смотрели их? чтоб я знал с какой стороны объяснять..

Автор: mes 10.4.2011, 00:06
попробуйте убрать из readed весь анализ приходящих данных и оставить только запись в файл.. 
только буффер не забудьте обнулять..

Цитата(boostcoder @  9.4.2011,  23:02 Найти цитируемый пост)
архив с исходниками качали? смотрели их?

да смотрел и как раз смотрю их.. 

Автор: boostcoder 10.4.2011, 00:11
Цитата(mes @  10.4.2011,  00:06 Найти цитируемый пост)
весь анализ приходящих данных

вот это?:
Код

   char* p = strchr(buf, ':');
   if ( p ) {
      sscanf(p+1, "%8d", &tmp);
   } else {
      throw std::runtime_error("input data error!");
   }
   
   if ( idx != tmp ) {
      std::ostringstream os;
      os << "read error. expected " << idx << " get " << tmp;
      throw std::runtime_error(os.str());
   }


как же я тогда узнаю об ошибке?


Цитата(mes @  10.4.2011,  00:06 Найти цитируемый пост)
да смотрел и как раз смотрю их..

там есть файл wrappers.cpp, его Максим добавил. логи пишутся из оберток над sendmsg() и recvmsg(). т.е. по идее на 100% точно отражают происходящее.

Автор: mes 10.4.2011, 00:14
Цитата(boostcoder @  9.4.2011,  22:50 Найти цитируемый пост)
 server_*.log и client_*.log - это и есть то, что записано. т.е. сырые данные.

поставьте после каждой записи сырых данных еще запись какой нибудь последовательности..

Добавлено через 1 минуту и 24 секунды
Цитата(boostcoder @  9.4.2011,  23:11 Найти цитируемый пост)
как же я тогда узнаю об ошибке?

если вы о исключении, то запись дополнительной последовательности в файл должна показать причину.. 

Автор: boostcoder 10.4.2011, 00:17
Цитата(mes @  10.4.2011,  00:14 Найти цитируемый пост)
запись какой нибудь последовательности..

Цитата(mes @  10.4.2011,  00:14 Найти цитируемый пост)
запись дополнительной последовательности в файл должна показать причину..

поясните. не понял.

Добавлено через 3 минуты и 58 секунд
еще, на сервере во входящих есть такое:
user posted image

в hex:
user posted image
т.е. три нуля.

Автор: mes 10.4.2011, 00:22
после каждого сброса буффера в файл записывайте туда еще например "$$$"
чтоб наглядно бросалась в глаза.. 

Автор: boostcoder 10.4.2011, 00:25
Цитата(mes @  10.4.2011,  00:22 Найти цитируемый пост)
после каждого сброса буффера в файл

но мне не известно когда буфера сбрасываются.
все что я могу, это принудительно вызывать fflush(). т.е. предлагаете вставить вызовы fflush() в обертки над sendmsg() и recvmsg() ?

Добавлено через 6 минут и 4 секунды
mes, я обернул код клиента в try-catch блок. теперь буфера не потеряются.
есть ли смысл сбрасывать буфера после каждой записи в файл?

Добавлено через 11 минут и 5 секунд
теперь свалился с таким сообщением:
Цитата

(exception): read error. expected 6690986 get 6690987

в client_in.log такое:
user posted image

число 6690985 повторяется дважды. хотя второе должно было быть 6690986
и пропал символ c и : 

Автор: mes 10.4.2011, 00:39
чего то мы друг друга не понимаем.. 
я предлагаю Вам грубо говоря просто перенаправить получаемое с сокета в файл.. 
только добавить разделитель, после каждой операции чтения.. 
и без того исключения на получаемый индекс чтоб посмотреть, что будет в файле.. 
а окончание можно узнать по числу итераций, превышающее с запасом итерацию с возникновением ошибки.. 

Автор: boostcoder 10.4.2011, 00:41
сервер вот что отправил:
user posted image
последние две строки повторяются.

Автор: mes 10.4.2011, 00:42
в общем  у меня сложилось впечатление, что у Вас были две ошибки, которые создали эту ситуацию..
1. чтение не полной записи с сокета, а следовательно невозможность получения ожидаемого индекса используемым способом
2. не обнуление буффера, которое  приводило к появлению неожидаемых символов.. 

Автор: boostcoder 10.4.2011, 00:46
еще раз свалилось.
user posted image
тут уже повторение с предыдущими запусками. опять повтор. и опять пропали два символа.

Добавлено @ 00:48
Цитата(mes @  10.4.2011,  00:42 Найти цитируемый пост)
1. чтение не полной записи с сокета, а следовательно невозможность получения ожидаемого индекса используемым способом

это от меня не зависит. согласно документации, хэндлер вызовется в случае:
Цитата

The asynchronous operation will continue until one of the following conditions is true:
1. The supplied buffers are full. That is, the bytes transferred is equal to the sum of the buffer sizes.
2. An error occurred.


с другой стороны, в последних трех скринах ситуация повторилась.

Цитата(mes @  10.4.2011,  00:42 Найти цитируемый пост)
2. не обнуление буффера, которое  приводило к появлению неожидаемых символов..

это я сделал.

Автор: mes 10.4.2011, 01:00
Цитата(boostcoder @  9.4.2011,  23:25 Найти цитируемый пост)
в client_in.log такое:

сорри.. наверно ночь сказывается, но я  так и не понял откуда данные попадают в этот лог..

Добавлено через 1 минуту и 8 секунд
Цитата(boostcoder @  9.4.2011,  23:25 Найти цитируемый пост)
есть ли смысл сбрасывать буфера после каждой записи в файл?

не в сбрасывании цель, а в добавлении визуального разделителя..

Добавлено через 7 минут и 5 секунд
Цитата(boostcoder @  9.4.2011,  23:46 Найти цитируемый пост)
это от меня не зависит. 

просто Вы не в праве применять scanf до тех пор пока не наполните свой буффер.. 

Автор: boostcoder 10.4.2011, 01:08
Цитата(mes @  10.4.2011,  01:00 Найти цитируемый пост)
я  так и не понял откуда данные попадают в этот лог

в client.cpp есть такой код:
Код

   FILE* in = fopen("server_in.log", "wb");
   FILE* out= fopen("server_out.log", "wb");
   dbg::in = in; // dbg::in и dbg::out используются в wrappers.cpp
   dbg::out = out;

в wrappers.cpp есть такое:
Код

static void dump(const struct msghdr *msg, FILE *fp)
{
    size_t i;
    for (i = 0; i < msg->msg_iovlen; ++i)
    {
        struct iovec *iov = &msg->msg_iov[i];
        fwrite(iov->iov_base, iov->iov_len, 1, fp);
    }
}

extern "C"
{

ssize_t (*_sendmsg)(int sockfd, const struct msghdr *msg, int flags);
ssize_t (*_recvmsg)(int sockfd, struct msghdr *msg, int flags);

ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
{
    if (dbg::out)
    {
        dump(msg, dbg::out); // запись отсылаемого
    }
    return _sendmsg(sockfd, msg, flags);
}

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)
{
    ssize_t error;
    error = _recvmsg(sockfd, msg, flags);
    if (dbg::in && error > 0)
    {
        dump(msg, dbg::in); // запись принятого
    }
    return error;
}

}



Добавлено через 1 минуту и 11 секунд
Цитата(mes @  10.4.2011,  01:00 Найти цитируемый пост)
Вы не в праве применять scanf до тех пор пока не наполните свой буффер

так scanf() вызывается в хэндлере. а хэндлер вызывается тогда, когда указанный объем прочитан.

Автор: mes 10.4.2011, 01:11
Цитата(boostcoder @  10.4.2011,  00:08 Найти цитируемый пост)
так scanf() вызывается в хэндлере. а хэндлер вызывается тогда, когда указанный объем прочитан. 

сделайте strlen перед этим для проверки и будет ясно там ли мы ищем причину..

Добавлено @ 01:15
Цитата(boostcoder @  10.4.2011,  00:08 Найти цитируемый пост)
в wrappers.cpp есть такое:

тогда, если подозрения правильные, то ошибки должны быть в server_out и в client_in
но не в двух других..

Автор: boostcoder 10.4.2011, 05:38
Цитата(mes @  10.4.2011,  01:11 Найти цитируемый пост)
сделайте strlen перед этим для проверки и будет ясно там ли мы ищем причину..

сделал.

Цитата(mes @  10.4.2011,  01:11 Найти цитируемый пост)
тогда, если подозрения правильные, то ошибки должны быть в server_out и в client_in
но не в двух других..

Вы правы.
вот только я немогу понять закономерность...

клиент свалился с таким сообщением:
Цитата

expected: 10661261, get: 1066126, buf:  1066126 1expected: 10661262, get: 661262, buf: 0661262
 1expected: 10661263, get: 661263, buf: 0661263
...

где:
"expected: 10661261" - правильно. это и должно было прийти.
"get: 1066126" - тут, внезапно, пропал последний знак.
"buf:  1066126" - тут его тоже нет.
"1expected: 10661262" - а ни это ли наша пропавшая единичка?;) на ее месте должен быть символ перевода строки.
"get: 661262" - тут нет первых двух знаков. т.е. "10"
"buf: 0661262" - а тут нет только первой единички.

сервер получил это:
user posted image
а отослал это:
user posted image
значение 10661262 повторяется дважды.

а клиент получил это:
user posted image


ну что, кто-то раскусил головоломку? ;)

последняя версия кодов.
клиент:
Код


#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>

#include "header.hpp"

size_t req_cnt = 0;
size_t ask_cnt = 0;
size_t requests = 0;
size_t answers = 0;
bool wsleep = false;

void timer_func() {
   while ( true ) {
      std::cout
      << "per sec requests: " << requests << std::endl
      << "per sec answers : " << answers << std::endl
      << "total requests  : " << req_cnt << std::endl
      << "total answers   : " << ask_cnt << std::endl << std::endl;
      requests = answers = 0;
      boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
   }
}

/***************************************************************************/

void readed(boost::asio::ip::tcp::socket&,
            char*,
            const boost::system::error_code&,
            size_t);

void start_read(boost::asio::ip::tcp::socket& sock) {
   char* buf = new char[recv_buffer_size];
   memset(buf, 0, recv_buffer_size);
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &readed,
         boost::ref(sock),
         buf,
         boost::asio::placeholders::error,
         boost::asio::placeholders::bytes_transferred
      )
   );
}

void readed(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t rd)
{
   if ( e ) {
      throw std::runtime_error("readed(): " + e.message());
   }

   if ( rd != recv_buffer_size ) {
      std::cout
      << "readed(): " << buf << ", len: " << rd << std::endl;
   }

   static size_t idx = 0;
   size_t tmp = atoi(buf);
   
   if ( idx != tmp ) {
      std::cout
      << "expected: " << idx << ", get: " << tmp << ", buf: " << buf;
   }

   delete[] buf;

   answers++;
   ask_cnt++;

   ++idx;

   start_read(sock);
}

/***************************************************************************/

void writen(boost::asio::ip::tcp::socket&,
            char*,
            const boost::system::error_code&,
            size_t);

void start_write(boost::asio::ip::tcp::socket& sock) {
   static size_t idx = 0;
   requests++;
   req_cnt++;
   
   char* buf = new char[send_buffer_size];
   memset(buf, 0, send_buffer_size);
   sprintf(buf, "%9d\n", idx++);

   boost::asio::async_write(
      sock,
      boost::asio::buffer(buf, send_buffer_size),
      boost::bind(
         &writen,
         boost::ref(sock),
         buf,
         boost::asio::placeholders::error,
         boost::asio::placeholders::bytes_transferred
      )
   );
}

void writen(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t wr)
{
   if ( e ) {
      throw std::runtime_error("writen(): " + e.message());
   }

   if ( wr != send_buffer_size ) {
      std::cout
      << "writen(): " << buf << ", len: " << wr << std::endl;
   }

   delete[] buf;

   if ( wsleep ) {
      boost::this_thread::sleep(boost::posix_time::microseconds(1000));
   }
   
   start_write(sock);
}

/***************************************************************************/

int main(int argc, char** argv) {
   try {
      if ( argc != 4 ) {
         std::cout << "client ip port 0/1 - sleed disabled/enabled" << std::endl;
         return 0;
      }

      if (init_wrappers()) {
         return -1;
      }
      std::string ip = argv[1];
      boost::uint16_t port = atoi(argv[2]);
      wsleep = (argv[3][0] == '1');
      std::cout << "sleep " << (wsleep?"enabled":"disabled") << std::endl;

      boost::thread thread(&timer_func);

      dbg::in = fopen("client_in.log", "wb");
      dbg::out= fopen("client_out.log", "wb");
      if ( !dbg::out || !dbg::in ) {
         std::cout << "can`t open file!" << std::endl;
         return 1;
      }

      boost::asio::ip::tcp::endpoint endpoint(
         boost::asio::ip::address::from_string(ip), port
      );

      boost::asio::io_service ios;
      boost::asio::ip::tcp::socket socket(ios);
      socket.connect(endpoint);

//      boost::asio::socket_base::non_blocking_io non_blocking_io(true);
//      socket.io_control(non_blocking_io);

      start_write(socket);
      start_read(socket);

      ios.run();

   } catch ( const std::exception& e ) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
   fclose(dbg::in);
   fclose(dbg::out);
}

/***************************************************************************/


сервер:
Код


#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/lexical_cast.hpp>

#include "header.hpp"

/***************************************************************************/

void writen(boost::asio::ip::tcp::socket&,
            char*,
            const boost::system::error_code&,
            size_t);

void start_write(boost::asio::ip::tcp::socket& sock, char* buf) {
   boost::asio::async_write(
      sock,
      boost::asio::buffer(buf, send_buffer_size),
      boost::bind(
         &writen,
         boost::ref(sock),
         buf,
         boost::asio::placeholders::error,
         boost::asio::placeholders::bytes_transferred
      )
   );
}

void writen(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t wr)
{
   if ( e ) {
      throw std::runtime_error("writen(): " + e.message());
   }

   if ( wr != send_buffer_size ) {
      std::cout
      << "writen(): " << buf << ", len: " << wr << std::endl;
   }

   delete[] buf;
}

void readed(boost::asio::ip::tcp::socket&,
            char*,
            const boost::system::error_code&,
            size_t);

void start_read(boost::asio::ip::tcp::socket& sock) {
   char* buf = new char[recv_buffer_size];
   memset(buf, 0, recv_buffer_size);
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &readed,
         boost::ref(sock),
         buf,
         boost::asio::placeholders::error,
         boost::asio::placeholders::bytes_transferred
      )
   );
}

void readed(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t rd)
{
   if ( e ) {
      throw std::runtime_error("read handler: " + e.message());
   }

   if ( rd != recv_buffer_size ) {
      std::cout
      << "readed(): " << buf << ", len: " << rd << std::endl;
   }

   static size_t idx = 0;
   size_t tmp = atoi(buf);
   if ( idx != tmp ) {
      fprintf(stdout, "expected: %9d, get: %9d, buf: %s", idx, tmp, buf);
      fflush(stdout);
   }

   ++idx;

   start_write(sock, buf);
   start_read(sock);
}

/***************************************************************************/

int main(int argc, char** argv) {
   if ( argc != 3 ) {
      std::cout << "server ip port" << std::endl;
      return 0;
   }

   if ( init_wrappers() ) {
      return -1;
   }
   try {
      std::string ip = argv[1];
      boost::uint16_t port = atoi(argv[2]);

      dbg::in = fopen("server_in.log", "wb");
      dbg::out= fopen("server_out.log", "wb");
      if ( !dbg::out || !dbg::in ) {
         std::cout << "can`t open file!" << std::endl;
         return 1;
      }

      boost::asio::ip::tcp::endpoint endpoint(
         boost::asio::ip::address::from_string(ip), port
      );

      boost::asio::io_service ios;
      boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
      boost::asio::ip::tcp::socket socket(ios);

      acceptor.accept(socket);
      std::cout << "new connection from: " << socket.remote_endpoint().address().to_string() << std::endl;

   //   boost::asio::socket_base::non_blocking_io non_blocking_io(true);
   //   socket.io_control(non_blocking_io);

      start_read(socket);

      ios.run();
   } catch ( const std::exception& e ) {
      std::cout << "(exception): " << e.what() << std::endl;
   }

   fclose(dbg::in);
   fclose(dbg::out);
}

/***************************************************************************/



Автор: boostcoder 10.4.2011, 06:05
я сделал это!!! smile 

ща расскажу..

Автор: boostcoder 10.4.2011, 06:28
обратите внимание на этот код из сервера:
Код

void readed(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t rd)
{
   ...

   start_write(sock, buf);
   start_read(sock); <<<<<<<<<<<<<<<<<<<<<<
}


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

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

зы
немного о впечатлениях.
да, когда говорят что дока по бусту непонятная - правильно говорят. когда говорят что многое в бусте нелогично - правильно говорят. почему? да потому, что я за ночь прочел всю доку по asio от корки до корки, и не нашел ничего о том, что нельзя выполнять операцию на сокете до тех пор, пока предыдущая не завершилась!
единственное что как-то на это намекает, это цитаты:
Цитата

The program must ensure that the stream performs no other read operations (such as async_read, the stream's async_read_some function, or any other composed operations that perform reads) until this operation completes.

Цитата

The program must ensure that the stream performs no other write operations (such as async_write, the stream's async_write_some function, or any other composed operations that perform writes) until this operation completes.

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

эх... держите меня семеро smile 

MAKCim, mes, phprus, всем сильно благодарен за ваше терпение. всем спасибо. smile 

Автор: boostcoder 10.4.2011, 06:57
но мне все равно не понятно, каким образом операция чтения запущенная одновременно с операцией записи может портить данные которые записываются smile 

Автор: boostcoder 10.4.2011, 07:31
удивительно... но использование канала наладилось. 94% smile 
Цитата

per sec requests: 205184
per sec answers : 201890
total requests  : 169479197
total answers   : 169425734

per sec requests: 250922
per sec answers : 235622
total requests  : 169730133
total answers   : 169661370

per sec requests: 181205
per sec answers : 194040
total requests  : 169911338
total answers   : 169855439

per sec requests: 209731
per sec answers : 196663
total requests  : 170121093
total answers   : 170052102


Автор: MAKCim 10.4.2011, 10:43
Цитата(boostcoder @  10.4.2011,  06:57 Найти цитируемый пост)
но мне все равно не понятно, каким образом операция чтения запущенная одновременно с операцией записи может портить данные которые записываются 

предположу
start_read->async_read->readed
причем async_read->readed может быть синхронным (если данные уже получены)
в этом случае имеем два последовательных async_write без вызова written

Автор: phprus 10.4.2011, 11:38
Цитата(boostcoder @  10.4.2011,  09:28 Найти цитируемый пост)
readed() вызывается когда данные прочитаны. из него я запускаю операцию записи и операцию чтения. и тут ошибка! операцию чтения нужно запускать из хэндлера операции записи. т.е. мало того что в доке намекается на то, что нельзя выполнять две операции записи и чтения одновременно, так оказывается что выполнять чтение пока не завершена запись(и наоборот) тоже нельзя!

А если использовать read и write параллельно с разными, несвязанными данными, то баг тоже воспроизведется? Если да, то это будет по сути баг в ASIO. TCP полнодуплексный протокол и может работать одновременно в обе стороны.

Цитата(boostcoder @  10.4.2011,  10:31 Найти цитируемый пост)
удивительно... но использование канала наладилось. 94%

На каком канале? 100 Мбит/с?

Автор: mes 10.4.2011, 12:03
Цитата(boostcoder @  10.4.2011,  05:28 Найти цитируемый пост)
start_write(sock, buf);
   start_read(sock); <<<<<<<<<<<<<<<<<<<<<<

у меня подозрения, что Вы не с причиной разобрались, а просто создали благоприятные условия, в которых _баг_  не/реже проявляется.. 
так же как и секундой слипа..

Добавлено через 10 минут и 47 секунд
Цитата(boostcoder @  10.4.2011,  05:28 Найти цитируемый пост)
но не в одной из цитат не говорится о том, что нельзя выполнять запись пока не завершилось чтение, и на оборот!
уверен, что можно  smile

Добавлено через 11 минут и 34 секунды
Цитата(boostcoder @  10.4.2011,  05:28 Найти цитируемый пост)
эх... держите меня семеро 

рано эмоции выпустили smile

Автор: mes 10.4.2011, 12:19
Цитата(boostcoder @  10.4.2011,  05:57 Найти цитируемый пост)
но мне все равно не понятно, каким образом операция чтения запущенная одновременно с операцией записи может портить данные которые записываются

данные не портятся, просто приходят не одним куском , а несколькими, на что Вам намекал strlen..

Добавлено через 1 минуту и 55 секунд
а Вы их неправильно интерпретировали неполные блоки, что и служило поводом для исключения..
и еще сервер получив неправильный блок, переправлял его как полный клиенту.. поэтому ошибка только в логах server_out и client_in..

Добавлено через 3 минуты и 30 секунд
т.е. неправильная интерпретация полученных данных сервером,  создавала проблему с логами,
а неправильная интерпретаця клиентом вызывало исключение..

Добавлено через 5 минут и 54 секунды
в общем у Вас получился пример, ловко собравший в себе несколько похожих ошибок, и устранение одной, изменяло ситуацию, но не исправляло ее - именно это и привело к путанице..

Добавлено через 9 минут и 14 секунд
хотя помимо названных причин, вроде еще одна должна быть для полной картины..

Добавлено через 13 минут и 25 секунд
Цитата(boostcoder @  10.4.2011,  05:28 Найти цитируемый пост)
 start_write(sock, buf);
   start_read(sock); <<<<<<<<<<<<<<<<<<<<<<

Цитата(boostcoder @  10.4.2011,  05:28 Найти цитируемый пост)
 так оказывается что выполнять чтение пока не завершена запись(и наоборот) тоже нельзя!

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


Автор: boostcoder 10.4.2011, 12:34
Цитата(phprus @  10.4.2011,  11:38 Найти цитируемый пост)
А если использовать read и write параллельно с разными, несвязанными данными, то баг тоже воспроизведется?

подозреваю что да. тут не данные навязывают ограничения, а asio.

Цитата(phprus @  10.4.2011,  11:38 Найти цитируемый пост)
Если да, то это будет по сути баг в ASIO. TCP полнодуплексный протокол и может работать одновременно в обе стороны.

возможно это действительно не баг, а ограничение реализации asio.
почитай: http://boost.2283326.n4.nabble.com/Multiple-async-operations-on-a-socket-td2579621.html

Цитата(phprus @  10.4.2011,  11:38 Найти цитируемый пост)
100 Мбит/с?

да.
завтра смогу проверить на 1гбит канале.

Цитата(mes @  10.4.2011,  12:03 Найти цитируемый пост)
у меня подозрения, что Вы не с причиной разобрались, а просто создали благоприятные условия, в которых _баг_  не/реже проявляется.. 
так же как и секундой слипа..

так же, прочтите по ссылке.

Igor R. - это второй человек в поддержке asio. я полагаю весьма компетентный.


Цитата(mes @  10.4.2011,  12:03 Найти цитируемый пост)
уверен, что можно

да, можно. но не при помощи async_write(). нужно писать обертку над сокетом(сегодня этим и займемся ;) ), и в ней использовать socket::async_write_some() и socket::async_read_some(). на сколько я понял из того что по ссылке.

Добавлено через 1 минуту
тест работает почти 7 часов.

Автор: mes 10.4.2011, 12:36
кстати комбинация с перемещением start_read во written поможет только для пинг понговского обмена, как у Вас в примере.. 
при нарушении симметрии на чтение запись, опять начнутся проблемы при такой архитектуре..

Добавлено через 2 минуты и 21 секунду
Цитата(boostcoder @  10.4.2011,  11:34 Найти цитируемый пост)
тест работает почти 7 часов. 

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

Добавлено через 2 минуты и 40 секунд
Цитата(boostcoder @  10.4.2011,  11:34 Найти цитируемый пост)
почитай: http://boost.2283326.n4.nabble.com/Multipl...-td2579621.html

сейчас..

Добавлено через 5 минут и 3 секунды
Цитата(boostcoder @  10.4.2011,  11:34 Найти цитируемый пост)
да, можно. но не при помощи async_write(). нужно писать обертку над сокетом(сегодня этим и займемся ;) ), и в ней использовать socket::async_write_some() и socket::async_read_some(). на сколько я понял из того что по ссылке.

нет не поняли smile я говорил что уверен, что нет ограничения, на одновременное использование async_write и async_read .. 
и уверен, что не это служило поводом для багов у Вас в примере..

Добавлено через 7 минут и 17 секунд
Цитата

hand, for async_write, since it is divided in many async_write_some, 

async_read() also performs multiple socket::async_read_some() 

ага так и предполагалось, из за чего ограничение на совместную запись/чтение.. 
но это никак не может служить ограничением на совмещением записи с чтением.. 

Автор: 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
Цитата(boostcoder @  10.4.2011,  12:21 Найти цитируемый пост)
sendmsg() и recvmsg() можно использовать одновременно? т.е. с одним сокетом. 

Цитата(phprus @  10.4.2011,  10:38 Найти цитируемый пост)
TCP полнодуплексный протокол и может работать одновременно в обе стороны.



Автор: MAKCim 10.4.2011, 13:30
Цитата(boostcoder @  10.4.2011,  13:21 Найти цитируемый пост)
скажите, sendmsg() и recvmsg() можно использовать одновременно? т.е. с одним сокетом. 

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

Автор: mes 10.4.2011, 13:30
Цитата(boostcoder @  10.4.2011,  12:21 Найти цитируемый пост)
насколько я понимаю, одновременное чтение/запись нужно реализовать согласно этому принципу:

нужно ? можно и как у Вас было, только порядок навести.. 

Автор: boostcoder 10.4.2011, 13:31
mes, я это видел.
но я никогда такого не проделывал прежде. и тем более с sendmsg() и recvmsg().
я просто хочу понять, это ограничение asio, или OS-API.

Автор: mes 10.4.2011, 13:32
мне кажется Вы причины проблемы до сих пор не поняли, из за этого с ужасом ищете обходные варианты..

Добавлено @ 13:32
Цитата(boostcoder @  10.4.2011,  12:31 Найти цитируемый пост)
я просто хочу понять, это ограничение asio, или OS-API. 

что Вы подразумеваете под "это" ?.. 

Автор: MAKCim 10.4.2011, 13:33
 smile 
еще раз убедился в том, как С++ на примере boost'а усложняет жизнь
было бы что-то другое, можно было посмотреть реализацию и выяснить все вопросы
лезть же в boost удовольствие сомнительное ;)

Автор: boostcoder 10.4.2011, 13:36
Цитата(MAKCim @  10.4.2011,  13:30 Найти цитируемый пост)
да
входной и выходной буферы сокета независимы
я даже больше скажу, можно одновременно из разных потоков делать sendmsg()

понял. спасибо.

значит нужно автора asio закидать письмами с четкими вопросами.

Цитата(mes @  10.4.2011,  13:30 Найти цитируемый пост)
можно и как у Вас было, только порядок навести..

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

Of course, you still can run async_read and async_write together for one connection. 

мне не кажется, что тут говорится о том, что можно читать и писать одновременно?

Автор: mes 10.4.2011, 13:37
Цитата(boostcoder @  10.4.2011,  12:36 Найти цитируемый пост)
мне не кажется, что тут говорится о том, что можно читать и писать одновременно? 

так Вам все об этом говорят smile что нет на это ограничений smile

Автор: boostcoder 10.4.2011, 13:39
Цитата(MAKCim @  10.4.2011,  13:33 Найти цитируемый пост)
еще раз убедился в том, как С++ на примере boost'а усложняет жизнь
было бы что-то другое, можно было посмотреть реализацию и выяснить все вопросы
лезть же в boost удовольствие сомнительное ;)

но не писать же свою реализацию сокетов со всем вытекающим...

я еще неделю назад подумывал использовать http://pocoproject.org/. но это не удобно, тянуть в проект еще что-то. у меня проект полностью написан с использованием буста. ну еще openssl...

Автор: mes 10.4.2011, 13:39
Цитата(boostcoder @  10.4.2011,  12:36 Найти цитируемый пост)
значит нужно автора asio закидать письмами с четкими вопросами.

пока вопросы возникшие в этой теме освещены документацией... не смотря на то, что она действительно скудная.. 
;)

Добавлено через 41 секунду
Цитата(boostcoder @  10.4.2011,  12:39 Найти цитируемый пост)
но не писать же свою реализацию сокетов со всем вытекающим...

asio Вас уже испугало ?  smile  smile

Добавлено через 5 минут и 47 секунд
Цитата(boostcoder @  10.4.2011,  12:36 Найти цитируемый пост)
что конкретно нужно изменить?

при чтении не делать start_write,
a добавлять в очередь, из которой забирать по writen..

Добавлено через 8 минут и 47 секунд
Цитата(MAKCim @  10.4.2011,  12:33 Найти цитируемый пост)
еще раз убедился в том, как С++ на примере boost'а усложняет жизнь

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

Автор: boostcoder 10.4.2011, 13:49
Цитата(mes @  10.4.2011,  12:19 Найти цитируемый пост)
и еще сервер получив неправильный блок, переправлял его как полный клиенту.. поэтому ошибка только в логах server_out и client_in..

это не так.
в последней версии исходников, я сделал проверку на сервере, на предмет порченных/неожидаемых данных. сервер ни разу не обнаружил несоответствие во входящих данных.

Добавлено через 1 минуту и 50 секунд
Цитата(mes @  10.4.2011,  13:39 Найти цитируемый пост)
при чтении не делать start_write,
a добавлять в очередь, из которой забирать по writen..

переделаю. не вопрос.

Автор: mes 10.4.2011, 13:56
Цитата(boostcoder @  10.4.2011,  12:49 Найти цитируемый пост)
сервер ни разу не обнаружил несоответствие во входящих данных.

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

Цитата(boostcoder @  10.4.2011,  12:49 Найти цитируемый пост)
переделаю. не вопрос. 

и должен стать порядок и на сервере smile


Автор: boostcoder 10.4.2011, 14:13
Цитата(MAKCim @  10.4.2011,  13:30 Найти цитируемый пост)
правда насчет атомарности записи данных (данные каждого sendmsg() запишутся непрерывно) не уверен

хм.. а это очень любопытно..

Добавлено @ 14:15
Цитата(mes @  10.4.2011,  13:39 Найти цитируемый пост)
добавлять в очередь, из которой забирать по writen..

а если очередь пуста, то по какому событию делать проверку/перезапуск отправки из очереди?

Добавлено @ 14:16

ужо 2.2 мильярда)

total requests  : 2212348580
total answers   : 2212269496


Добавлено @ 14:17
Цитата(mes @  10.4.2011,  13:39 Найти цитируемый пост)
при чтении не делать start_write,
a добавлять в очередь, из которой забирать по writen..

хотя.. не вижу принципиальной разницы.. в чем она?

Добавлено @ 14:22
Цитата(MAKCim @  10.4.2011,  13:30 Найти цитируемый пост)
правда насчет атомарности записи данных (данные каждого sendmsg() запишутся непрерывно) не уверен

а разве sendmsg() и recvmsg() не используют writev() и readv() ?
хотя ни в одной из моих книжек по программированию для линукс, не говорится об атомарности записи/чтения... погуглю..

Автор: mes 10.4.2011, 15:21
Цитата(boostcoder @  10.4.2011,  13:13 Найти цитируемый пост)
хотя.. не вижу принципиальной разницы.. в чем она?

представьте ситуацию когда на два чтения должна происходить одна запись smile

Добавлено через 1 минуту и 46 секунд
Цитата(boostcoder @  10.4.2011,  13:13 Найти цитируемый пост)
а если очередь пуста, то по какому событию делать проверку/перезапуск отправки из очереди?

добавить флаг.. если очередь пуста и нет активной write, то после добавления стартовать ее, иначе просто добавлять.. 

Автор: phprus 10.4.2011, 17:09
Цитата(boostcoder @  10.4.2011,  15:34 Найти цитируемый пост)
да.
завтра смогу проверить на 1гбит канале.

Напиши, пожалуйста, результаты (% использования канала) и RTT канала.

Автор: boostcoder 10.4.2011, 18:02
phprus, ок. отпишусь.

mes, вот что получилось. изменил только сервер. оно даже работает smile
Код


#include <iostream>
#include <queue>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>

#include "header.hpp"

/***************************************************************************/

struct queue {
   void push(char* ptr) {
      boost::mutex::scoped_lock locker(mutex);
      queue.push(ptr);
   }

   char* pop() {
      boost::mutex::scoped_lock locker(mutex);
      if ( queue.empty() ) return 0;
      char* ptr = queue.front();
      queue.pop();
      return ptr;
   }

   bool empty() const {
      boost::mutex::scoped_lock locker(mutex);
      return queue.empty();
   }

private:
   mutable boost::mutex mutex;
   std::queue<char*> queue;
} q;

bool in_process = false;

/***************************************************************************/

void start_write(boost::asio::ip::tcp::socket&);

void writen(boost::asio::ip::tcp::socket&,
            char*,
            const boost::system::error_code&,
            size_t);

void start_read(boost::asio::ip::tcp::socket&);

void readed(boost::asio::ip::tcp::socket&,
            char*,
            const boost::system::error_code&,
            size_t);

/***************************************************************************/

void start_write(boost::asio::ip::tcp::socket& sock) {
   if ( char* buf = q.pop() ) {
      boost::asio::async_write(
         sock,
         boost::asio::buffer(buf, send_buffer_size),
         boost::bind(
            &writen,
            boost::ref(sock),
            buf,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred
         )
      );
   }
}

void writen(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t wr)
{
   if ( e ) {
      throw std::runtime_error("writen(): " + e.message());
   }

   if ( wr != send_buffer_size ) {
      std::cout
      << "writen(): " << buf << ", len: " << wr << std::endl;
   }

   delete[] buf;

   if ( !q.empty() ) {
      in_process = true;
      start_write(sock);
   } else {
      in_process = false;
   }
}

/***************************************************************************/

void start_read(boost::asio::ip::tcp::socket& sock) {
   char* buf = new char[recv_buffer_size];
   boost::asio::async_read(
      sock,
      boost::asio::buffer(buf, recv_buffer_size),
      boost::bind(
         &readed,
         boost::ref(sock),
         buf,
         boost::asio::placeholders::error,
         boost::asio::placeholders::bytes_transferred
      )
   );
}

void readed(boost::asio::ip::tcp::socket& sock,
            char* buf,
            const boost::system::error_code& e,
            size_t rd)
{
   if ( e ) {
      throw std::runtime_error("read handler: " + e.message());
   }

   if ( rd != recv_buffer_size ) {
      std::cout
      << "readed(): " << buf << ", len: " << rd << std::endl;
   }

   static boost::uint64_t idx = 0;
   boost::uint64_t tmp = atoll(buf);
   if ( idx != tmp ) {
      fprintf(stdout, "expected: %12llu, get: %12llu, buf: %s", idx, tmp, buf);
      fflush(stdout);
   }

   ++idx;

   q.push(buf);

   if ( !in_process ) {
      in_process = true;
      start_write(sock);
   }
   
   start_read(sock);
}

/***************************************************************************/

int main(int argc, char** argv) {
   if ( argc != 3 ) {
      std::cout << "server ip port" << std::endl;
      return 0;
   }

   if ( init_wrappers() ) {
      return -1;
   }
   try {
      std::string ip = argv[1];
      boost::uint16_t port = atoi(argv[2]);
/*
      dbg::in = fopen("server_in.log", "wb");
      dbg::out= fopen("server_out.log", "wb");
      if ( !dbg::out || !dbg::in ) {
         std::cout << "can`t open file!" << std::endl;
         return 1;
      }
*/
      boost::asio::ip::tcp::endpoint endpoint(
         boost::asio::ip::address::from_string(ip), port
      );

      boost::asio::io_service ios;
      boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
      boost::asio::ip::tcp::socket socket(ios);

      acceptor.accept(socket);
      std::cout << "new connection from: " << socket.remote_endpoint().address().to_string() << std::endl;

      boost::asio::socket_base::non_blocking_io non_blocking_io(true);
      socket.io_control(non_blocking_io);

      start_read(socket);
      
      ios.run();
   } catch ( const std::exception& e ) {
      std::cout << "(exception): " << e.what() << std::endl;
   }
/*
   fclose(dbg::in);
   fclose(dbg::out);
*/
}

/***************************************************************************/



Автор: boostcoder 10.4.2011, 18:20
зы
нехочу постоянно помнить о познаных ограничениях. хочу обертку над сокетом. умную. удобную.

Автор: mes 10.4.2011, 18:55
Цитата(boostcoder @  10.4.2011,  17:02 Найти цитируемый пост)
вот что получилось. изменил только сервер.оно даже работает. smile

Цитата(mes @  10.4.2011,  12:56 Найти цитируемый пост)
значит тогда клиент в порядке.. получается, что рубились только на сервере.. 

smile
Цитата(boostcoder @  10.4.2011,  17:20 Найти цитируемый пост)
нехочу постоянно помнить о познаных ограничениях. хочу обертку над сокетом. умную. удобную. 

а разве asiо не предлагает ? ту которую Вы раньше применяли, в той длинной теме..

Добавлено @ 18:58
оглянувшись назад, можно оценить насколько пророчески были слова MAKCim
Цитата(MAKCim @  29.3.2011,  13:28 Найти цитируемый пост)
Цитата(boostcoder)

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


в этом большая ошибка
я много раз на это же полагался
а в итоге как раз в мегапростом коде и был баг ;)

 smile  smile 

Автор: boostcoder 10.4.2011, 18:59
Цитата(mes @  10.4.2011,  18:55 Найти цитируемый пост)
а разве asiо не предлагает ? ту которую Вы раньше применяли

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

Добавлено через 52 секунды
Цитата(mes @  10.4.2011,  18:55 Найти цитируемый пост)
оглянувшись назад, можно оценить насколько пророчески были слова MAKCim

это да.
не в первый раз раз себя на таком ловлю... эх..

Автор: MAKCim 10.4.2011, 19:04
Цитата(boostcoder @  10.4.2011,  14:13 Найти цитируемый пост)
а разве sendmsg() и recvmsg() не используют writev() и readv() ?

нет

Добавлено через 4 минуты и 45 секунд
boostcoder, 
сейчас, кстати посмотрел код ядра и немного офигел
http://tomoyo.sourceforge.jp/cgi-bin/lxr/source/net/ipv4/tcp.c#L914
что-то я не вижу локов
это наводит на мысль, что в linux'е лучше не пытаться писать в сокет из более одного потока без своих локов

Добавлено через 7 минут и 56 секунд
 smile 
Цитата(boostcoder @  10.4.2011,  18:59 Найти цитируемый пост)
не в первый раз раз себя на таком ловлю... эх.. 

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

просто когда много раз об это спотыкаешься, начинаешь относится к себе более критически дабы решить проблему быстрее

Автор: boostcoder 10.4.2011, 19:19
Цитата(MAKCim @  10.4.2011,  19:04 Найти цитируемый пост)
психика у нас устроена таким образом, что мы априори не хотим признавать, что ошибка может быть у нас

это правда smile 

Автор: MAKCim 10.4.2011, 19:24
короче я нагнал
лок там есть в самом верху, я проглядел, паника отменяется smile 

Автор: boostcoder 10.4.2011, 19:24
Цитата(MAKCim @  10.4.2011,  19:04 Найти цитируемый пост)
нет

в структуре msghdr есть такой член:
Код

struct msghdr {
    ...
    struct iovec *msg_iov; <<<<<<<<<<<<<<
    ...
};


эта же структура используется и для http://linux.die.net/man/2/writev. вот я и подумал, что в sendmsg() используется writev() дабы не плодить код.

Цитата(MAKCim @  10.4.2011,  19:04 Найти цитируемый пост)
сейчас, кстати посмотрел код ядра и немного офигел
http://tomoyo.sourceforge.jp/cgi-bin/lxr/s...ipv4/tcp.c#L914
что-то я не вижу локов
это наводит на мысль, что в linux'е лучше не пытаться писать в сокет из более одного потока без своих локов

ну вот, еще полезная информация.

Добавлено через 52 секунды
Цитата(MAKCim @  10.4.2011,  19:24 Найти цитируемый пост)
лок там есть в самом верху

это он? :
Цитата

lock_sock(sk);

Автор: MAKCim 10.4.2011, 19:26
Цитата(boostcoder @  10.4.2011,  19:24 Найти цитируемый пост)
это он? :

ага

Автор: boostcoder 11.4.2011, 11:04
phprus, привет.
RTT = 50-54ms

в клиенте и сервере пришлось размер массивов увеличить до 10к, т.к. они съедали ядро из-за огромного кол-ва аллокаций и прочего.
использование канала ~82%
т.е. уверенно 800 мбит.

Автор: boostcoder 11.4.2011, 18:59
что-то заблудился..

не понимаю, почему этот код бажил -
Цитата(boostcoder @  10.4.2011,  05:38 Найти цитируемый пост)
   start_write(sock, buf);
   start_read(sock);

а код с очередью нет? smile

Добавлено через 2 минуты
ааа.. у меня новая запись запускалась еще до того как предыдущая завершилась smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)