并发读写问题
对于同一个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_做读写保护