我正在使用boost::asio::ip::tcp::socket
来接收数据。我需要一个接口,允许我指定一个缓冲区,并在异步填充此缓冲区后调用完成处理程序。
从套接字读取时,我们可以使用async_read_some
方法。
但是,async_read_some
方法可能会读取少于请求的字节数,因此如果发生这种情况,它必须使用缓冲区的其余部分调用自身。这是我目前的做法:
template<typename CompletionHandler>
void read(boost::asio::ip::tcp::socket* sock, char* target, size_t size, CompletionHandler completionHandler){
struct ReadHandler {
boost::asio::ip::tcp::socket* sock;
char* target;
size_t size;
CompletionHandler completionHandler;
ReadHandler(ip::tcp::socket* sock, char* target, size_t size, CompletionHandler completionHandler)
: sock(sock),target(target),size(size),completionHandler(completionHandler){}
// either request the remaining bytes or call the completion handler
void operator()(const boost::system::error_code& error, std::size_t bytes_transferred){
if(error){
return;
}
if(bytes_transferred < size){
// Read incomplete
char* newTarg =target+bytes_transferred;
size_t newSize = size-bytes_transferred;
sock->async_read_some(boost::asio::buffer(newTarg, newSize), ReadHandler(sock,newTarg,newSize,completionHandler));
return;
} else {
// Read complete, call handler
completionHandler();
}
}
};
// start first read
sock->async_read_some(boost::asio::buffer(target, size), ReadHandler(this,target,size,completionHandler));
}
基本上,我们调用async_read_some
直到整个缓冲区被填满,然后我们调用完成处理程序。到现在为止还挺好。但是,一旦我在第一次调用完成接收之前多次调用此方法,我认为事情会变得混乱:
void thisMayFail(boost::asio::ip::tcp::socket* sock){
char* buffer1 = new char[128];
char* buffer2 = new char[128];
read(sock, buffer1, 128,[](){std::cout << "Buffer 1 filled";});
read(sock, buffer2, 128,[](){std::cout << "Buffer 2 filled";});
}
当然,前128个接收字节应该进入第一个缓冲区,第二个128应该进入第二个缓冲区。但根据我的理解,情况可能会发生在这里:
假设第一个async_read_some
仅返回70个字节,那么它将发出第二个async_read_some
,剩余的58个字节。但是,这个读取将在第二个128字节读取(!)后排队,因此第一个缓冲区将接收前70个字节,接下来的128个将进入第二个缓冲区,最后的50个进入第一个缓冲区。 即,在这种情况下,第二个缓冲区甚至会在第一个填充完全填充之前填充。这可能不会发生。
如何解决这个问题?我知道有async_read
方法,但是它的文档说它只是通过多次调用async_read_some
来实现,所以它与我的read
实现基本相同,并且无法解决问题
答案 0 :(得分:1)
您根本无法同时激活两个异步读取操作:这是未定义的行为。
你可以
使用自由函数async_read_until
或async_read
函数,它们已经具有更高级别的语义,循环调用套接字async_read_some
,直到条件匹配或缓冲区已满。
使用异步操作链接对第一个之后的下一个异步读取进行排序。简而言之,您在第一个完成处理程序中启动第二个boost::asio::async_read*
调用。
注意:
在运行多个IO服务线程时使用strand;请参阅Why do I need strand per connection when using boost::asio?