Структура программы для двунаправленной связи TCP с использованием Boost:: Asio

Во-первых, я надеюсь, что мой вопрос имеет смысл и даже возможен! Из того, что я прочитал о сокетах TCP и Boost:: ASIO, я думаю, что это должно быть.

То, что я пытаюсь сделать, - это настроить две машины и работать с двунаправленным каналом чтения/записи через TCP между ними. Любая из сторон должна иметь возможность отправлять некоторые данные, которые будут использоваться другой стороной.

Первая запутанная часть о TCP (/IP?) заключается в том, что для нее требуется эта модель клиент/сервер. Тем не менее, чтение показывает, что любая из сторон способна писать или читать, поэтому я еще не полностью обескуражен. Я не против создания произвольной стороны как клиента, а другой как сервера. В моей заявке, которую можно обсудить досрочно и не беспокоит меня.

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

Я представляю какой-то цикл, в котором я вызываю io_service.poll(). Если опрос показывает, что другая сторона ожидает отправки некоторых данных, она вызывет read() и примет эти данные. Если в очереди ничего не ждет, и у него есть данные для отправки, тогда он вызывается write(). Когда обе стороны это делают, они должны иметь возможность читать и писать друг другу.

Моя забота заключается в том, как избежать ситуаций, в которых оба одновременно входят в какую-либо синхронную операцию write(). У них обоих есть данные для отправки, а затем сидеть там, ожидая отправки с обеих сторон. Эта проблема просто подразумевает, что я должен выполнять асинхронные write() и read()? В этом случае будут ли вещи взорваться, если обе стороны соединения попытаются написать асинхронно одновременно?

Я надеюсь, что кто-то может в идеале:

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

или, несколько менее идеально,

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

Ответ 1

Моя забота - как избежать ситуаций в которые оба входят в некоторые синхронной записи() на в то же время. Оба они имеют данные для отправить, а затем сидеть там, ожидая отправьте его с обеих сторон. Это проблема просто подразумевает, что я должен делать асинхронные write() и read()? В в этом случае все будет взорвано, если оба стороны соединения пытаются написать асинхронно в то же время?

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

При написании сервера вы должны использовать асинхронные методы. Синхронные методы подходят для некоторых тривиальных клиентских приложений.

Ответ 2

То, что вы хотите сделать, абсолютно возможно. Веб-трафик является хорошим примером ситуации, когда "клиент" отправляет что-то задолго до того, как сервер делает. Я думаю, что вас подстегивают слова "клиент" и "сервер".

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

Что касается поиска кода примера, который вы можете использовать в качестве основы для своей работы, я настоятельно рекомендую вам взглянуть на книгу W. Richard Stevens "Unix Network Programming". Любого издания хватит, хотя второе издание будет более актуальным. Это будет только C, но это нормально, потому что API сокетов - только C. boost:: asio хорошо, но похоже, что вы можете увидеть некоторые гайки и болты под капотом.

Ответ 3

TCP является полнодуплексным, что означает, что вы можете отправлять и получать данные в том порядке, в котором вы хотите. Чтобы предотвратить тупик в вашем собственном протоколе (поведение вашего уровня на высоком уровне), когда у вас есть возможность как отправлять, так и получать, вы должны получать в качестве приоритета. С epoll в режиме с триггером уровня, который выглядит так: epoll для отправки и получения, если вы можете это сделать, в противном случае, если вы можете отправить и отправить что-нибудь, сделайте это. Я не знаю, как boost:: asio или threads подходят здесь; вам нужна определенная степень контроля над тем, как передаются и получаются чередование.

Ответ 4

Слово, которое вы ищете, является "неблокирующим", что полностью отличается от асинхронного ввода-вывода POSIX (который включает в себя сигналы).

Идея заключается в том, что вы используете что-то вроде fcntl(fd,F_SETFL,O_NONBLOCK). write() вернет число успешно записанных байтов (если положительно), и оба read() и write() возвращают -1 и устанавливают errno = EAGAIN, если "невозможно выполнить прогресс" (нет данных для чтения или записи в окне).

Затем вы используете что-то вроде select/epoll/kqueue, которое блокируется до тех пор, пока сокет не будет доступен для чтения/записи (в зависимости от установленных флагов).