Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

«Boost.Asio C Network Programming». Глава 7: Boost.Asio – добавочные темы

Anna | 24.06.2014 | нет комментариев

Продолжаю перевод книги John Torjo «Boost.Asio C Network Programming».

Оглавление:

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

  • Если отладка не получается, то вы увидите, что Boost.Asio поможет вам в этом
  • Если вам придется трудиться с SSL, то посмотрите, что вам может предложить Boost.Asio
  • Если вы пишите приложение под определенную OC, то посмотрите, какие добавочные функции есть в Boost.Asio для вас

Asio вопреки Boost.Asio

Авторы Boost.Asio так же обеспечили поддержку Asio. Вы можете думать об этом как об Asio, так как эта библиотека поставляется в 2-х вариантах, Asio (не Boost) и Boost.Asio. Авторы утверждают, что обновления будут вначале возникать в Asio, и периодично будут добавляться в дистрибутив Boost-а.
Если в 2-х словах, то отличия в дальнейшем:

  • Asio определено в нэймспейсе asio::, в то время как Boost.Asio определено в boost::asio::
  • Заголовочный файл в Asio это asio.hpp и boost/asio.hpp в Boost.Asio
  • В Asio есть свой класс для запуска потоков (эквивалент boost::thread)
  • Asio предоставляет свои классы для кодов ошибок (asio::error_code взаменboost::system::error_cod и asio::system_error взамен boost::system::system_error)

Огромнее информации по Asio вы можете получить по ссылке.
Вы обязаны решить для себя, какой вариант вы выберете; лично я выбираю Boost.Asio. Вот несколько пророческой, на которые стоит обратить внимание, делая свой выбор:

  • Новые версии Asio выходят Почаще, чем новые версии Boost.Asio (новые дистрибутивы Boost выходят достаточно редко)
  • Asio чисто заголовочная (в то время как часть Boost.Asio зависит от других библиотек Boost, которые могут потребоваться для компиляции)
  • Обе, и Asio и Boost.Asio абсолютно развитые, следственно если вы не горите желанием применять функции из нового релиза Asio, то Boost.Asio достаточно отличная альтернатива, тем больше что в вашем распоряжении будут и другие библиотеки Boost.

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

Отладка

Отладка синхронного приложения, как правило, проще, чем асинхронного приложения. Для синхронного приложения, если оно заблокируется, вы легко войдете в отладку и получите картину того, где случилось (синхронное обозначает последовательное). Впрочем, когда вы программируете асинхронно, события не происходят ступенчато, следственно, если произойдет оплошность, то ее подлинно сложно будет отловить.
Дабы избежать этого, во-первых, вы обязаны дюже отлично разбираться в сопрограммах. Если программа будет реализована верно, то у вас, фактически, не будs_andmk!gt;8| John, new client list: John
Дозвольте проанализировать всякую строчку:

  • Мы вводим async_connect, которая создает обработчик 1(в нашем случае все обрабатываютtalk_to_svr::step)
  • Вызывается обработчик 1 (позже удачного подключения к серверу)
  • Обработчик 1 вызывает async_send, которая создает обработчик 2 (тут мы посылаем сообщение с логином на сервер)
  • Обработчик 1 закрывается
  • Вызывается обработчик 2 и посылает 11 байт (login John)
  • Обработчик 2 вызывает async_receive, которая создает обработчик 3 (мы ожидаем, когда сервер ответит на наше сообщение с логином)
  • Обработчик 2 закрывается
  • Вызывается обработчик 3 и получает 9 байт (login ok)
  • Обработчик 3 перенаправляет в on_answer_from_server (где создается обработчик 4)
  • Обработчик 3 закрывается
  • Вызывается обработчик 4, тот, что потом запишет в дамп John logged in
  • Обработчик 4 запускает еще один step (обработчик 5), тот, что будет писать ask_clients
  • Обработчик 4 закрывается
  • Открывается обработчик 5
  • Обработчик 5, async_send ask_clients, создает обработчик 6
  • Обработчик 5 закрывается
  • Вводится обработчик 6 (мы удачно отправили ask_clients серверу)
  • Обработчик 6 вызывает async_receive, которая создает обработчик 7 (мы ожидаем, когда сервер отправит нам список существующих заказчиков)
  • Обработчик 6 закрывается
  • Вызывается обработчик 7, и мы принимаем список заказчиков
  • Обработчик 7 запускает on_answer_from_serve (где создается обработчик 8)
  • Обработчик 7 закрывается
  • Открывается обработчик 8, и в дамп записывается список заказчиков (on_clients)

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

Запись информации отслеживания обработчиков в файл

По умолчанию информация отслеживания обработчиков выводится в типовой поток ошибок (эквивалентstd::cerr). Дюже возможно, что вы захотите перенаправить данный итог в другое место. С одной стороны, по умолчанию, для консольных приложений, итог и сброс ошибок происходит в одно место, то есть в консоль. Но для Windows (не консольных) приложений, поток ошибок по умолчанию является пустым.
Вы можете перенаправить итог ошибок с поддержкой командной строки, скажем, так:

some_application 2>err.txt

Если вы не поленитесь, то можете сделать это программно, как показано в дальнейшем фрагменте кода:

// for Windows
HANDLE h = CreateFile("err.txt", GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 
	FILE_ATTRIBUTE_NORMAL , 0);
SetStdHandle(STD_ERROR_HANDLE, h);
// for Unix
int err_file = open("err.txt", O_WRONLY);
dup2(err_file, STDERR_FILENO);

SSL

Boost.Asio предоставляет классы для поддержки некоторых основных вероятностей SSL. Внутри себя она использует OpenSSL. Так что, если вы хотите применять SSL, то вначале загрузите и соберите OpenSSL . Следует подметить, что, как правило, построение OpenSSL задача не из легких, исключительно, если у вас нет знаменитых компиляторов, таких как Visual Studio.
Если у вас есть удачно собранный OpenSSL, то Boost.Asio имеет некоторые классы надстройки над ним:

  • ssl::stream: применяется взамен класса ip::<protocol>::socket
  • ssl::context: это контекст для начала сеанса
  • ssl::rfc2818_verification: данный класс является простым методом проверки сертификата по имени хоста в соответствии с правилами RFC 2818

Вначале необходимо сделать и инициализировать SSL контекст, после этого открыть сокет, применяя данный контекст и данный узел, подключиться к удаленному хосту и произвести SSL «рукопожатие». Позже этого вы можете применять само­стоятельные функции из Boost.Asio read*/write*.
Вот примитивный пример HTTPS заказчика, тот, что подключается к Yahoo!:

#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
using namespace boost::asio;
io_service service;
int main(int argc, char* argv[]) 
{
	typedef ssl::stream<ip::tcp::socket> ssl_socket;
	ssl::context ctx(ssl::context::sslv23);
	ctx.set_default_verify_paths();
	// Open an SSL socket to the given host
	io_service service;
	ssl_socket sock(service, ctx);
	ip::tcp::resolver resolver(service);
	std::string host = "www.yahoo.com";
	ip::tcp::resolver::query query(host, "https");
	connect(sock.lowest_layer(), resolver.resolve(query));
	// The SSL handshake 
	sock.set_verify_mode(ssl::verify_none);
	sock.set_verify_callback(ssl::rfc2818_verification(host));
	sock.handshake(ssl_socket::client);
	std::string req = "GET /index.html HTTP/1.0rnHost: " 
		  host   "rnAccept: */*rnConnection: closernrn";
	write(sock, buffer(req.c_str(), req.length()));
	char buff[512];
	boost::system::error_code ec;
	while ( !ec) 
	{
		int bytes = read(sock, buffer(buff), ec);
		std::cout << std::string(buff, bytes);
	}
}

Первые строки нуждаются в пояснении. При подключении к удаленному хосту, вы используетеsock.lowest_layer(), другими словами, вы используете стержневой сокет (ssl::stream каждого лишь врапер). Следующие три строки исполняют «рукопожатие». Как только это произойдет, вы сделаете HTTP запрос с поддержкой функции write() из Boost.Asio и причитаете (функция read()) входящие сообщения.
При реализации SSL серверов все становится немножко труднее. Boost.Asio поставляется с примером SSL- сервера, тот, что вы можете обнаружить в boost/libs/asio/example/ssl/server.cpp.

Boost.Asio Windows компоненты

Особенности, которые стоит использовать только в операционной системе Windows.

Дескрипторы потока

Boost.Asio разрешает создавать враперы над дескрипторами Windows, позже чего вы можете применять множество самостоятельных функций, таких как read(), read_until(), write(), async_read(), async_read_until(), и async_write(). Вот как дозволено прочитать строку из файла:

HANDLE file = ::CreateFile("readme.txt", GENERIC_READ, 0, 0, 
	OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
windows::stream_handle h(service, file);
streambuf buf;
int bytes = read_until(h, buf, 'n');
std::istream in(&buf);
std::string line;
std::getline(in, line);
std::cout << line << std::endl;

Класс stream_handle доступен только при заключении применения порта ввода/вывода (тот, что применяется по умолчанию). Если это так, то нужно определить дальнейший макрос:BOOST_ASIO_HAS_WINDOWS_STREAM_HANDLE.

Дескрипторы произвольного доступа

Boost.Asio разрешает вам делать произвольно-доступными операции чтения и записи в дескрипторы, которые относятся к обыкновенными файлам. Вновь же, вы создаете врапер над дескриптором, а после этого используете самостоятельные функции, такие как read_at(), write_at(), async_read_at(), либоasync_write_at(). Дабы прочитать 50 символов, начиная с 1000-ого, вы можете применять дальнейший код:

HANDLE file = ::CreateFile("readme.txt", GENERIC_READ, 0, 0, 
	OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED, 0);
windows::random_access_handle h(service, file);
char buf[50];
int bytes = read_at(h, 1000, buffer( buf));
std::string msg(buf, bytes);
std::cout << msg << std::endl;

Для Boost.Asio дескрипторы произвольного доступа обеспечивают только произвольный доступ, вы не можете применять их как потоковые дескрипторы. Иными словами, само­стоятельные функции, такие как read(), read_until(), write(), а так же их асинхронные сотрудники не могут быть использованы для дескрипторов произвольного доступа. Класс random_access_handle доступен только позже заключения применения порта ввода/вывода (применяемый по умолчанию). Если это так, то определен макросBOOST_ASIO_HAS_WINDOWS_RANDOM_ACCESS_HANDLE.

Дескрипторы объекта

Вы можете ждать дескрипторы Windows от объектов ядра, скажем, уведомления об изменении консольного ввода, событие, уведомление о источниках памяти, процессов, семафоров, потоков, таймеров. Либо, проще говоря, вы можете вызвать WaitForSingleObject. Для них вы можете сделать врапер object_handle и применять wait() либо async_wait() для них:

void on_wait_complete(boost::system::error_code err) {}
...
HANDLE evt = ::CreateEvent(0, true, true, 0);
windows::object_handle h(service, evt);
// synchronous wait
h.wait();
// asynchronous wait
h.async_wait(on_wait_complete);

Особенности Boost.Asio в POSIX системах

Особенности, которые стоит использовать только в операционных системах Unix.

Локальные сокеты

Boost.Asio включает базовую поддержку локальных сокетов (так же знаменитых как Unix сокеты).
Локальный сокет, это сокет, тот, что дозволено получить только из приложения, которое работает на хост-машине. Вы можете применять локальные сокеты для комфорта межпроцессорной связи. Вы можете подключить как клиентский сокет, так и серверный. Для локальных сокетов endpoint это имя файла, скажем,/tmp/whatever. Здорово теснее то, что вы можете назначать права на данный файл, следственно, вы можете запретить пользователям на вашей машине создавать сокеты на файл.
Вы можете подключить клиентский сокет дальнейшим образом:

local::stream_protocol::endpoint ep("/tmp/my_cool_app");
local::stream_protocol::socket sock(service);
sock.connect(ep);

Так же вы можете сделать серверный сокет как показано ниже:

::unlink("/tmp/my_cool_app"); 
local::stream_protocol::endpoint ep("/tmp/my_cool_app");
local::stream_protocol::acceptor acceptor(service, ep);
local::stream_protocol::socket sock(service);
acceptor.accept(sock);

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

Подключение локальных сокетов

Наконец, вы можете объединить два сокета, либо без установления соединения (датаграммы), либо ориентированные на подключение (потоки):

// connection oriented
local::stream_protocol::socket s1(service);
local::stream_protocol::socket s2(service);
local::connect_pair(s1, s2);
// connection-less
local::datagram_protocol::socket s1(service);
local::datagram_protocol::socket s2(service);
local::connect_pair(s1, s2);

Внутри connect_pair есть гнусная POSIX функция socketpair(). Все, что она делает, это подключает два сокета без трудного процесса создания сокета; каждого одна строчка кода и готово. Прежде это был самый легкой метод межпотоковой коммуникации. В то время как в современном программировании вы можете этого избежать, вы можете обнаружить ее пригодной при работе со ветхим кодом, в котором применяются сокеты.

Файловые дескрипторы POSIX

Boost.Asio разрешает применять синхронные и асинхронные операции с применением некоторых файловых дескрипторов POSIX, такие как пайпы, типовые потоки ввода/вывода и другие устройства (но не для обыкновенных файлов).
Позже создания экземпляра stream_descriptor, скажем, файлового дескриптора POSIX, вы можете применять некоторые само­стоятельные функции предоставляемые Boost.Asio, такие как read(), read_until(), write(), async_read(), async_read_until(), и async_write().
Вот как вы будете читать одну строку из стандартного потока ввода и записывать ее в типовой поток итога:

size_t read_up_to_enter(error_code err, size_t bytes) { ... }
posix::stream_descriptor in(service, ::dup(STDIN_FILENO));
posix::stream_descriptor out(service, ::dup(STDOUT_FILENO));
char buff[512];
int bytes = read(in, buffer(buff), read_up_to_enter);
write(out, buffer(buff, bytes));

Класс stream_descriptor доступен только если его поддерживает целевая операционная система, а именно, если определен макрос BOOST_ASIO_HAS_POSIX_STREAM_DESCRIPTOR.

Fork

Boost.Asio поддерживает программы, которые применяют вызов системной функции fork(). Вы обязаны известить экземпляру io_service, что собираетесь вызвать функцию fork() и когда это произойдет. Глядите дальнейший фрагмент кода:

service.notify_fork(io_service::fork_prepare);
if (fork() == 0) 
{
	// child
	service.notify_fork(io_service::fork_child);
	...
} 
else 
{
	// parent
	service.notify_fork(io_service::fork_parent);
	...
}

Рекомендую применять service, тот, что будет вызван в ином потоке. Правда, Boost.Asio разрешает это, я настойчиво советую вам применять потоки, которые применяют boost::thread.

Резюме

Тяготитесь, Дабы ваш код был простым и легким для понимания. Постигайте и используйте сопрограммы. Это сведет к минимуму отладку того, что вам необходимо сделать, но изредка происходят сложно-отлавливаемые ошибки, скрывающиеся в коде, Boost.Asio может подмогнуть вам в этом, как было показано в разделе Отладка.
В случае, если вам придется иметь дело с SSL, Boost.Asio предоставляет основные классы для работы с ним.
Наконец, если ваше приложение ориентировано под определенную OC, то вы можете воспользоваться функциями из Boost.Asio, предоставляемые для определенной операционной системы.
Сетевое программирование имеет весомое значение в реальное время. Познание Boost.Asio является непременным для всякого C программиста 21 столетия. Мы разобрали теоретические и фактические основы применения этой библиотеки, написали небольшую коллекцию примеров, которые вы можете легко осознать, протестировать и расширить. Верю, что вам было увлекательно читать. И, определенно, это было дюже отрадно переводить!

Источники к этой статье: ссылка

В следующих нескольких статьях хочу предложить переводы первых глав нескольких книг (3, допустимо 4), какая огромнее придется вам по душе, ту и продолжу переводить первой.

Каждому огромное спасибо, до новых встреч!

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB