并发读写问题

对于同一个socket,不能在一个线程读的同时,在另一个线程中执行写操作。意味着 

socket.async_receive(...); | socket.async_write_some(...);不能同时在两个线程分别执行

如果你希望把一个io_service对象绑定到多个线程。此时需要boost::asio::strand来确保handler不会被同时执行,因为异步操作,比如async_write、async_receive_from之类会影响到临界区buffer(记录下暂时不清楚哪个临界区)


default_max_transfer_size_t

completion_condition.hpp文件定义了一次操作发送的最小数据单元,一旦超过,就会进行分拆发送

// The default maximum number of bytes to transfer in a single operation.

enum default_max_transfer_size_t { default_max_transfer_size = 65536 };

所以在数据量比较小时(比如小于65535),多线程操作socket一般也不会有什么问题。

实际上对于高并发视音频数据传输中的情况下,超过65535这种情况肯定是不可避免出现的。


例子

class strand_client
{
public:
    strand_client(asio::io_context& io_context,
        const tcp::resolver::results_type& endpoints)
        : io_context_(io_context), strand_(io_context),
        socket_(io_context)
    {
        do_connect(endpoints);
    }

    void write(const std::string& msg)
    {
        asio::async_write(socket_, asio::buffer(msg.data(), msg.size()),
            asio::bind_executor(strand_,
                [this](std::error_code ec, std::size_t length)
                {
                    if (!ec)
                    {
                        do_read();
                    }
                    else
                    {
                        close();
                    }
                }));
    }

    void close()
    {
        asio::post(io_context_, [this]() {
            asio::error_code ignored_ec;
            socket_.shutdown(tcp::socket::shutdown_both, ignored_ec);
            socket_.close(ignored_ec);
        });
    }

private:
    void do_connect(const tcp::resolver::results_type& endpoints)
    {
        asio::async_connect(socket_, endpoints,
            [this](std::error_code ec, tcp::endpoint)
            {
                if (!ec)
                {
                    do_read();
                }
            });
    }

    void do_read()
    {
        socket_.async_read_some(asio::buffer(read_msg_, 512),
                [this](std::error_code ec, std::size_t length)
                {
                    if (!ec)
                    {
                        std::cout << "recieve msg: " << std::string(read_msg_, length) << "\n";
                        do_read();
                    }
                    else
                    {
                        socket_.close();
                    }
                });
    }

private:
    asio::io_context& io_context_;
    asio::io_context::strand strand_;
    tcp::socket socket_;
    char read_msg_[512];
};

对于上述例子,可能会出现同时读写的情况,因为读操作没有采用strand进行封装

官网例子

参考boost_1_67_0\libs\asio\example\cpp03\http\server3

//
// connection.cpp
// ~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2018 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include "connection.hpp"
#include <vector>
#include <boost/bind.hpp>
#include "request_handler.hpp"

namespace http {
namespace server3 {

connection::connection(boost::asio::io_context& io_context,
    request_handler& handler)
  : strand_(io_context),
    socket_(io_context),
    request_handler_(handler)
{
}

boost::asio::ip::tcp::socket& connection::socket()
{
  return socket_;
}

void connection::start()
{
  socket_.async_read_some(boost::asio::buffer(buffer_),
      boost::asio::bind_executor(strand_,
        boost::bind(&connection::handle_read, shared_from_this(),
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred)));
}

void connection::handle_read(const boost::system::error_code& e,
    std::size_t bytes_transferred)
{
  if (!e)
  {
    boost::tribool result;
    boost::tie(result, boost::tuples::ignore) = request_parser_.parse(
        request_, buffer_.data(), buffer_.data() + bytes_transferred);

    if (result)
    {
      request_handler_.handle_request(request_, reply_);
      boost::asio::async_write(socket_, reply_.to_buffers(),
          boost::asio::bind_executor(strand_,
            boost::bind(&connection::handle_write, shared_from_this(),
              boost::asio::placeholders::error)));
    }
    else if (!result)
    {
      reply_ = reply::stock_reply(reply::bad_request);
      boost::asio::async_write(socket_, reply_.to_buffers(),
          boost::asio::bind_executor(strand_,
            boost::bind(&connection::handle_write, shared_from_this(),
              boost::asio::placeholders::error)));
    }
    else
    {
      socket_.async_read_some(boost::asio::buffer(buffer_),
          boost::asio::bind_executor(strand_,
            boost::bind(&connection::handle_read, shared_from_this(),
              boost::asio::placeholders::error,
              boost::asio::placeholders::bytes_transferred)));
    }
  }

  // If an error occurs then no new asynchronous operations are started. This
  // means that all shared_ptr references to the connection object will
  // disappear and the object will be destroyed automatically after this
  // handler returns. The connection class's destructor closes the socket.
}

void connection::handle_write(const boost::system::error_code& e)
{
  if (!e)
  {
    // Initiate graceful connection closure.
    boost::system::error_code ignored_ec;
    socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
  }

  // No new asynchronous operations are started. This means that all shared_ptr
  // references to the connection object will disappear and the object will be
  // destroyed automatically after this handler returns. The connection class's
  // destructor closes the socket.
}

} // namespace server3
} // namespace http

不管是读还是写都采用同一个strand_做读写保护