Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Сети > Почему не нужно использовать BSD-sockets?!


Автор: boostcoder 16.8.2011, 23:01
не понимаю я резона возиться с "этим"(BSD-sockets)! и не понимаю, что заставляет других использовать "это"! 1) отсутствие с++ компилятора? - это вряд ли, 2) не знание английского на столько, что даже доку по какому-то фреймворку/библиотеке(POCO, boost.asio, Qt) прочесть невозможно? - тоже маловероятно. 3) не знание с++? - тоже маловероятно.
так в чем же причина?! - возможно в том, что многие даже не задумываются о том что существуют альтернативы? - а вот это возможно!
а альтернативы есть, уверяю! и одна из них зовется http://www.boost.org/doc/libs/1_47_0/libs/asio/index.html.

фух... высказался...


итак. что же вам даст asio?
1. абстрагирование от платформы/ОС.
2. большое кол-во поддерживаемых платформ/ОС:
  • Linux Kernel 2.4
  • Linux Kernel 2.6
  • Solaris
  • QNX Neutrino
  • Mac OS X
  • FreeBSD
  • AIX
  • HP-UX
  • Tru64
  • Windows 95, 98, Me, NT, 2000, XP, 2003, Vista, Win7
3. высокую расширяемость, как в плане написания своих _всячески_специализированных_ оберток поверх asio, так и заложенные в основу asio такие "турбонаддуватели" как http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/allocation.html и http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/strands.html, и, при правильном проектировании вашего кода, возможность выполнять всю работу в указанном вами кол-ве потоков без использования примитивов синхронизации и "ручного" распределения запросов по рабочим потокам!
вы только вдумайтесь в каждый из перечисленных пунктов: 
3.1. custom_memory_allocation - позволяет вам использовать собственный аллокатор для повышения скорости запросов на выделение памяти, и одновременно, полностью избежать фрагментации памяти! задумайтесь: boost::bind(который вы будите использовать почти везде при использовании asio) при создании функционального объекта выполняет new, а при разрушении - delete. а добавьте к этому аллоцирование буферов для операций чтения/записи, и это уже не мелочь! это здоровый кусок процессорного времени, тратится на не очевидные на первый взгляд действия.
3.2. invocation_strategy - при использовании custom_memory_allocation, позволяет производить вызов функциональных объектов, уже созданных ранее, и лежащих в аллокаторе! в добавок, invocation_strategy, при использовании вашим кодом нескольких рабочих потоков, полностью избавляет вас от необходимости разграничения доступа к общим ресурсам! т.е. никаких мьютексов!
4. очень удобную модель основанную на патернах http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/basics.html и http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/async.html.


одумайтесь!
пожалуйста, не пишите больше "такой" код.

спасибо.


зы
даже не думал кого-то обидеть/задеть/оскорбить.

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




1. Основы
1.1. http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/io_service.html:
Основа любой программы использующей asio - io_service. Это означает, что Вы не можете создать объект сокета/аксептора не имея объекта io_service`а.
Код

#include <boost/asio.hpp>

int main() {
   boost::asio::io_service ios;

   boost::asio::ip::tcp::acceptor acceptor(ios);
   boost::asio::ip::tcp::socket socket(ios);
}


http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/io_service/run.html
Если Вы используете только блокирующие операции, такие как http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/basic_socket_acceptor/accept.html или http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/basic_stream_socket/receive.html/http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/basic_stream_socket/send.html/http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/read.html/http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/write.html, то Вам вовсе не нужно вызывать io_service::run(). Иначе, читаем далее...
Способы и правила вызова io_service::run()
Бессмысленно вызывать io_service::run() раньше чем Вы создали хотя бы одну задачу. Т.е., задача - это результат создания любой асинхронной операции(не путать с _результат_выполнения_). Так, к примеру, http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/async_read.html создает задачу, которая будет выполняться до тех пор, пока не будет прочтено требуемое кол-во байт, или, пока не произойдет ошибка.
Код

#include <boost/asio.hpp>

void handler(
  const boost::system::error_code& error, // Result of operation.

  std::size_t bytes_transferred           // Number of bytes copied into the
                                          // buffers. If an error occurred,
                                          // this will be the  number of
                                          // bytes successfully transferred
                                          // prior to the error.
)
{
   // ...
}

int main() {
   boost::asio::io_service ios;
   
   boost::asio::ip::tcp::socket socket(ios);
   
   char buff[1024];
   boost::asio::async_read(socket, boost::asio::buffer(buff), &handler);
   
   ios.run(); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
}

В этом примере, мы, при помощи async_read(), создаем _задачу_асинхронного_чтения_, и только после этого, вызываем io_service::run(). Иначе, если вызов io_service::run() и async_read() поменять местами, то наша программа завершится не успев начаться smile

Если же у Вас высокопроизводительный сервер, то, возможно, Вы столкнетесь с тем, что Вам будет недостаточно одного рабочего потока. И Вы, как и любой порядочный программист, начнете изменять архитектуру для того, чтоб распределять задачи между несколькими рабочими потоками(трэды, очереди ,и т.д...). НЕ ДЕЛАЙТЕ ЭТОГО! В asio, это уже сделано за Вас.
Немного изменяем предыдущий пример так:
Код

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

void handler(
  const boost::system::error_code& error, // Result of operation.

  std::size_t bytes_transferred           // Number of bytes copied into the
                                          // buffers. If an error occurred,
                                          // this will be the  number of
                                          // bytes successfully transferred
                                          // prior to the error.
)
{
   // ...
}

int main() {
   boost::asio::io_service ios;
   
   boost::asio::ip::tcp::socket socket(ios);
   
   char buff[1024];
   boost::asio::async_read(socket, boost::asio::buffer(buff), &handler);
   
   const size_t threads_count = 4; // нам необходимы четыре рабочих потока
   boost::thread_group threads;
   for ( size_t idx = 0; idx < threads_count; ++idx ) {
      threads.create_thread(
         boost::bind(&boost::asio::io_service::run, boost::ref(ios))
      );
   }

   threads.join_all();
}


Все! Теперь asio распределяет задачи между четырьмя рабочими потоками.
Но в этом примере есть один не очевидный(на первый взгляд) подвох: когда закончатся задачи, io_service::run() вернет управление, и, как следствие, завершатся все рабочие потоки. И любая асинхронная операция более никогда не выполнится.
Чтоб такого не произошло, изменяем пример следующим образом:
Код

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/thread.hpp>
#include <boost/shared_ptr.hpp>

void handler(
  const boost::system::error_code& error, // Result of operation.

  std::size_t bytes_transferred           // Number of bytes copied into the
                                          // buffers. If an error occurred,
                                          // this will be the  number of
                                          // bytes successfully transferred
                                          // prior to the error.
)
{
   // ...
}

int main() {
   boost::asio::io_service ios;
   boost::shared_ptr<boost::asio::io_service::work> work(
      new boost::asio::io_service::work(ios)
   ); // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
   
   boost::asio::ip::tcp::socket socket(ios);
   
   char buff[1024];
   boost::asio::async_read(socket, boost::asio::buffer(buff), &handler);
   
   const size_t threads_count = 4; // нам необходимы четыре рабочих потока
   boost::thread_group threads;
   for ( size_t idx = 0; idx < threads_count; ++idx ) {
      threads.create_thread(
         boost::bind(&boost::asio::io_service::run, boost::ref(ios))
      );
   }
   
   threads.join_all();
}

http://liveworkspace.org/code/167382d827125fa9ced4bf8905cca716
Теперь, вне зависимости от кол-ва задач, объект io_service`а будет продолжать работать и ожидать поступления новых задач. Для этого, мы создали объект типа http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/io_service__work.html. Почему объект io_service::work мы храним в смарт_поинтере? - потому что это дает нам возможность удалить его вызовом "work.reset()" для того чтоб завершить рабочие потоки и программу.

1.2. Буферы
Скорее всего, вам это никогда не пригодится. Можете смело пропускать этот раздел smile 

Заглядывая в документацию по таким функциям/методам как http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/basic_stream_socket/receive.html/http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/basic_stream_socket/send.html/http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/read.html/http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/write.html, вы будите видеть типы параметров буферов, к примеру, http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/MutableBufferSequence.html, http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/ConstBufferSequence.html. Что же означают эти типы, и какие к ним требования? Все просто.
Давайте рассмотрим требования к типу http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/MutableBufferSequence.html.
В требованиях говорится про:
value_type - который должен быть синонимом T.
const_iterator - тип итератора, соответствующего требованиям bidirectional-итератора(http://cplusplus.com/reference/std/iterator/BidirectionalIterator/).
const_iterator begin(); - метод, возвращающий итератор указывающий на первый элемент.
const_iterator end(); - метод, возвращающий итератор указывающий на последний элемент.

http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/ConstBufferSequence.html Имеет такие же требования, за исключением спецификаторов константности.

1.3. Время жизни буферов

1.4. Completion-handlers

...в процессе...

Автор: serghd 16.8.2011, 23:27
Про custom_memory_allocation наслышан, да. В высоконагруженных системах самое оно.
Пример invocation_strategy при использовании custom_memory_allocation если имеется, пожалуйста. Особенно интересен вызов функторов, лежащих в аллокаторе.

Автор: boostcoder 16.8.2011, 23:32
Цитата(serghd @  16.8.2011,  23:27 Найти цитируемый пост)
Пример invocation_strategy при использовании custom_memory_allocation если имеется, пожалуйста.

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

Автор: Sahab 16.8.2011, 23:33
А Вы не думали сударь, что иногда бывает навязана стратегия реализации проекта. Например, использовать только бздишные сокеты?

Автор: boostcoder 16.8.2011, 23:35
Цитата(Sahab @  16.8.2011,  23:33 Найти цитируемый пост)
А Вы не думали сударь

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

Автор: serghd 24.8.2011, 20:28
Код

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

void handler
(
 const boost::system::error_code& error, // Result of operation.
 
 std::size_t bytes_transferred           // Number of bytes copied into the
                                         // buffers. If an error occurred,
                                         // this will be the  number of
                                         // bytes successfully transferred
                                         // prior to the error.
) { std::cout << "txt" << std::endl; }

int main() 
{
 boost::asio::io_service ios;
   boost::shared_ptr<boost::asio::io_service::work> work(
      new boost::asio::io_service::work(ios)
   );
   
 boost::asio::ip::tcp::socket socket(ios);

 char buff[1024];
 boost::asio::async_read(socket, boost::asio::buffer(buff), &handler);

 const size_t threads_count = 4; // нам необходимы четыре рабочих потока
 boost::thread_group threads;
 for ( size_t idx = 0; idx < threads_count; ++idx ) 
 {
  threads.create_thread(
   boost::bind(&boost::asio::io_service::run, boost::ref(ios))
  );
 }
 threads.join_all(); // <<<<<<<<< ждём завершения всех потоков
}

http://liveworkspace.org/code/6baaa49522192ee72db32b0b3f9af140
Вопрос, наверное, ламерский, но тем не менее. Почему текст выводится один, а не 4 раза?smile
Т.е. я понимаю, что после первого вывода ios должен возвратить управление. Но чтобы он работал дальше, мы используем work. Тогда в чём подвох?

Автор: boostcoder 25.8.2011, 14:57
async_read() создает одну задачу. и выполняется одна задача.
ты ведь не задачу создаешь четыре раза, а рабочие потоки.

Автор: serghd 25.8.2011, 18:19
просто думал, что каждый поток вызывает свой async_read() (т.е. все они используют при этом один и тот же ios). Но если нет, значит нет)

Автор: Леопольд 9.9.2011, 13:20
Цитата(boostcoder @  16.8.2011,  23:01 Найти цитируемый пост)
Если же у Вас высокопроизводительный сервер, то, возможно, Вы столкнетесь с тем, что Вам будет недостаточно одного рабочего потока. И Вы, как и любой порядочный программист, начнете изменять архитектуру для того, чтоб распределять задачи между несколькими рабочими потоками(трэды, очереди ,и т.д...). НЕ ДЕЛАЙТЕ ЭТОГО! В asio, это уже сделано за Вас.
Немного изменяем предыдущий пример так:
А что если вылетит исключение в потоке?

Автор: boostcoder 9.9.2011, 13:23
Цитата(Леопольд @  9.9.2011,  13:20 Найти цитируемый пост)
А что если вылетит исключение в потоке?

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

Добавлено через 1 минуту и 53 секунды
ибо при возникновении исключения в любом и элементов очереди io_service, из очереди будет выброшен только этот элемент. поэтому этот же io_service спокойно может продолжить работать. иначе вообще, ничего бы не могло в asio работать нормально, при таком раскладе smile

Автор: Леопольд 9.9.2011, 13:36
Цитата(boostcoder @  9.9.2011,  13:23 Найти цитируемый пост)
обрабатываете его в том же потоке
Т.е. во всех хендлерах, где может быть исключение, нужны try/catch?

Автор: newbee 9.9.2011, 13:42
А где можно посмотреть непредвзятое сравнение высоконагруженного многопользовательского сервера на этом азио и линуксовом сишном epoll? Можно с распределением на равное число потоков или процессов. Интересует скорость реакции и число обработанных запрросов в единицу времени.

Автор: boostcoder 9.9.2011, 13:50
Цитата(Леопольд @  9.9.2011,  13:36 Найти цитируемый пост)
Т.е. во всех хендлерах, где может быть исключение, нужны try/catch?

нет. try-catch нужно только в точке вызова io_service::run()

Автор: boostcoder 9.9.2011, 14:42
Цитата(newbee @  9.9.2011,  13:42 Найти цитируемый пост)
на этом азио и линуксовом сишном epoll?

asio в лине и использует epoll.

Автор: newbee 9.9.2011, 14:46
Цитата(boostcoder @  9.9.2011,  15:42 Найти цитируемый пост)

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

Автор: Леопольд 9.9.2011, 16:36
Цитата(boostcoder @  9.9.2011,  13:50 Найти цитируемый пост)
нет. try-catch нужно только в точке вызова io_service::run()
В принципе, так и сделал (если я правильно уловил мысль), завернул io_service::run() в функцию с блоком try/catch. Наверное, в примере лучше тоже так сделать...

Автор: boostcoder 10.9.2011, 02:01
Цитата(Леопольд @  9.9.2011,  16:36 Найти цитируемый пост)
В принципе, так и сделал (если я правильно уловил мысль), завернул io_service::run() в функцию с блоком try/catch.

а в доку не пытались заглянуть? ;)
http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.effect_of_exceptions_thrown_from_handlers.
так же, есть способ который при помощи маленькой хитрости, позволяет отличать исключения возникшие в самой библиотеке(к примеру, при обрыве соединения), от исключений возникающих в пользовательских хэндлерах. но об этом в соответствующей главе.

Цитата(Леопольд @  9.9.2011,  16:36 Найти цитируемый пост)
Наверное, в примере лучше тоже так сделать

будет глава посвященная обработке ошибок/исключений.

Автор: Lazin 19.11.2011, 14:28
Это глупое фанбойство! ACE как минимум ничем не хуже, а может даже лучше чем asio. А boost это же вообще ужас. smile 

Автор: Олег2005 19.11.2011, 16:34
Модератор: Для предотвращение холивара и оффтопа тема временно закрыта. Для дискуссий желающие могут  открыть новую тему 

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