О буфере записи в общем сетевом программировании - PullRequest
0 голосов
/ 06 августа 2009

Я пишу сервер, используя boost.asio.Я прочитал и записал буфер для каждого соединения и использую асинхронную функцию чтения / записи (async_write_some / async_read_some).

С буфером чтения и async_read_some проблем нет.Просто вызывать функцию async_read_some нормально, потому что буфер чтения доступен только для чтения в обработчике чтения (обычно в том же потоке).

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

ПЕРВЫЙ ВОПРОС!

Есть ли способ избежать LOCK для буфера записи?

Я записываю свой собственный пакет в буфер стека и копирую егов буфер записи.Затем вызовите функцию async_write_some, чтобы отправить пакет.Таким образом, если я отправляю два пакета последовательно, все ли в порядке, вызывая функцию async_write_some два раза?

ВТОРОЙ ВОПРОС!

Каков обычный способ асинхронной записи в сокет?программирование?

Спасибо за чтение.

Ответы [ 3 ]

2 голосов
/ 06 августа 2009

Извините, но у вас есть два варианта:

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

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

1 голос
/ 01 декабря 2009

Ответ №1:

Вы правы, что блокировка - это жизнеспособный подход, но есть гораздо более простой способ сделать все это. Boost имеет симпатичную небольшую конструкцию в ASIO, которая называется strand. Любой обратный вызов, который был упакован с использованием цепочки, будет сериализован, гарантированно, независимо от того, какой поток выполняет обратный вызов. По сути, он обрабатывает любую блокировку для вас.

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

char buffer_to_write[256];  // shared among threads

/* ... in thread 1 ... */
memcpy(buffer_to_write, packet_1, std::min(sizeof(packet_1), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);

/* ... in thread 2 ... */
memcpy(buffer_to_write, packet_2, std::min(sizeof(packet_2), sizeof(buffer_to_write)));
my_socket.async_write_some(boost::asio::buffer(buffer_to_write, sizeof(buffer_to_write)), &my_callback);

Там вы делитесь своим реальным буфером записи (buffer_to_write). Если вы сделали что-то подобное, вы будете в порядке:

/* A utility class that you can use */
class PacketWriter
{
private:
  typedef std::vector<char>  buffer_type;

  static void WriteIsComplete(boost::shared_ptr<buffer_type> op_buffer, const boost::system::error_code& error, std::size_t bytes_transferred)
  {
    // Handle your write completion here
  }

public:
  template<class IO>
  static bool WritePacket(const std::vector<char>& packet_data, IO& asio_object)
  {
    boost::shared_ptr<buffer_type> op_buffer(new buffer_type(packet_data));

    if (!op_buffer)
    {
      return (false);
    }

    asio_object.async_write_some(boost::asio::buffer(*op_buffer), boost::bind(&PacketWriter::WriteIsComplete, op_buffer, boost::asio::placeholder::error, boost::asio::placeholder::bytes_transferred));
  }
};

/* ... in thread 1 ... */
PacketWriter::WritePacket(packet_1, my_socket);

/* ... in thread 2 ... */
PacketWriter::WritePacket(packet_2, my_socket);

Здесь будет полезно, если вы также передадите свою ветвь в WritePacket. Вы поняли, хотя.

Ответ № 2:

Я думаю, вы уже используете очень хороший подход. Одно из предложений, которое я бы предложил, - это использовать async_write вместо async_write_some, чтобы гарантировать, что весь буфер будет записан до вызова вашего обратного вызова.

0 голосов
/ 06 августа 2009

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

Сеть, скорее всего, будет самой медленной частью канала (при условии, что ваша модификация не вычислительно дорого), так что вы можете выполнять моды, пока слой сокета отправляет предыдущие данные.

Если вы работаете с большим количеством клиентов с частым подключением / отключением, взгляните на Порты завершения ввода-вывода или аналогичный механизм.

...