Pergunta

Estou escrevendo servidor usando boost.asio. Eu li e escrevo buffer para cada conexão e uso função assíncronizada de leitura/gravação (async_write_some / async_read_some).

Com buffer de leitura e async_read_some, nao há problema. Apenas invocando async_read_some A função está bem porque o buffer de leitura é lido apenas no manipulador de leitura (significa no mesmo thread).

Mas, escreva o buffer precisa ser acessado a partir de vários threads, para que ele precise ser bloqueado para modificar.

PRIMEIRA PERGUNTA!

Existe alguma maneira de evitar o bloqueio de bloqueio para gravar?

Escrevo meu próprio pacote no buffer de pilha e copio -o para o buffer de gravação. Então ligue async_write_some função para enviar o pacote. Dessa forma, se eu enviar dois pacote em série, está tudo bem na invocação async_write_some função duas vezes?

SEGUNDA QUESTÃO!

Qual é a maneira comum de escrita assíncronizada na programação do soquete?

Obrigado pela leitura.

Foi útil?

Solução

Resposta 1:

Você está certo de que o bloqueio é uma abordagem viável, mas há uma maneira muito mais simples de fazer tudo isso. Boost tem uma pequena construção agradável em Asio chamada A strand. Qualquer retorno de chamada que foi embrulhado usando a fita será serializado, garantido, independentemente de qual tópico execute o retorno de chamada. Basicamente, ele lida com qualquer bloqueio para você.

Isso significa que você pode ter tantos escritores quanto quiser e se todos estiverem embrulhados pelo mesmo Strand (então, compartilhe sua fita única entre todos os seus escritores), eles serão executados em série. Uma coisa a observar é garantir que você não esteja tentando usar o mesmo buffer real na memória para fazer todas as gravações. Por exemplo, é isso que evitar:

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);

Lá, você está compartilhando seu buffer de gravação real (buffer_to_write). Se você fez algo assim, você ficará bem:

/* 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);

Aqui, ajudaria se você passasse seu fio para o WritePacket também. Você entendeu, no entanto.

Resposta #2:

Eu acho que você já está adotando uma abordagem muito boa. Uma sugestão que eu ofereceria é usar async_write ao invés de async_write_some Para que você tenha garantido que todo o buffer seja escrito antes que seu retorno de chamada seja chamado.

Outras dicas

Sorry but you have two choices:

  1. Serialise the write statement, either with locks, or better start a separate writer thread which reads requests from a queue, other threads can then stack up requests on the queue without too much contention (some mutexing would be required).

  2. Give each writing thread its own socket! This is actually the better solution if the program at the other end of the wire can support it.

You could queue your modifications and perform them on the data in the write handler.

Network would most probably be the slowest part of the pipe (assuming your modification are not computationaly expensive), so that you could perform mods while the socket layer is sending the previous data.

Incase you are handling large number of clients with frequent connect/disconnect take a look at IO completion ports or similar mechanism.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top