如何在基于 boost::asio 的异步模式下接收未知大小的缓冲区
how to receive unkown size buffer in async mode based boost::asio
我正在尝试接收一个未知大小的缓冲区,我的部分代码如下所示:
void Connection::asyncRead()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(buffer_out),
[this, self](boost::system::error_code ec, std::size_t length)
{
OnRead(ec, length);
});
}
我不知道缓冲区的大小,所以我尝试在固定大小的缓冲区中接收缓冲区,如何知道缓冲区是否结束?
如果您想要 send/receive 没有固定大小的邮件,您可以使用
您定义消息 header 的方法,例如使用 4 字节字段来存储消息内容的大小:
[header(4 bytes) to store the size of message][content of message]
那么你总是知道第一步是读取4个字节,
为数据准备缓冲区并读取更多数据,直到缓冲区被填满。
另一种方法是关闭套接字(以下是伪代码)
The receiving side | the sending side
--------------------------------------------------------------------------------
error_code ec; |
asio::read(sock,buf,ec) [2] |
| prepare some buffer with unknown size
| string buf;
| buf += ...; // add data
| asio::write(sock,buf)
| sock.shutdown(socket_base::shutdown_send); [1]
通过在发送端[1]调用sock.shutdown()
你可以通知
整个消息已发送的接收方。
然后在接收方读取消息后 [2] 您应该检查 ec
error-code 变量的状态是否为 boost::asio::eof
。
如果您收到 end-of-file
,您就知道该消息已完成。如果 ec
与 eof
不同,这意味着发生了错误。
从 1.66 boost 版本开始,您可以使用 dynamic_buffer
来存储数据,它将字符串或向量适配为缓冲区。
或者您可以考虑 streambuf
读取 non-fixed 个缓冲区。
编辑
使用 dynamic_buffer
添加。
根据参考资料,dynamic_buffer
可以在 async_read
、async_until
等自由函数中使用,但不能在 async_read_some
中作为套接字的成员函数使用。下面是客户端和服务器的代码:
服务器:
using namespace boost;
struct Data {
std::shared_ptr<asio::ip::tcp::socket> sock;
std::string buf; // buf is empty [1]
};
void readHandler (
const boost::system::error_code& ec,
size_t length,
std::shared_ptr<Data> d) {
std::cout << "readHandler" << std::endl;
if (ec == boost::asio::error::eof)
{
// here we got the whole message
std::cout << d->buf << std::endl;
}
else
{
std::cout << "Error" << std::endl;
}
}
int main() {
try {
asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(),9999);
asio::io_service ios;
asio::ip::tcp::acceptor acceptor(ios, ep);
std::shared_ptr<asio::ip::tcp::socket> sock{new asio::ip::tcp::socket(ios)};
acceptor.accept(*sock);
std::shared_ptr<Data> data(new Data);
data->sock = move(sock);
boost::asio::async_read (*(data->sock), asio::dynamic_buffer(data->buf),
std::bind(readHandler,std::placeholders::_1, std::placeholders::_2,data)); // [2]
ios.run(); // wait until async_write is complete
}
catch (system::system_error &e) {
std::cout << "error " << e.what() << std::endl;
}
return 0;
}
在 [1] 中我们创建空缓冲区作为字符串 object,在 [2] 中我们调用 async_read
使用 dynamic_buffer
获取数据。当发送方关闭其一侧的套接字时,将调用传入 async_read
的处理程序。
客户:
using namespace boost;
int main() {
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string("127.0.0.1"),9999);
asio::io_service ios;
asio::ip::tcp::socket sock(ios, ep.protocol());
sock.connect(ep);
std::string buf = "some message";
for (int i = 0; i < buf.size(); ++i) {
// synchronous function was used to make simpler code
asio::write(sock,asio::buffer(buf.c_str()+i,1)); // send 1 char
std::this_thread::sleep_for(std::chrono::seconds(1)); // delay 1 second
}
sock.shutdown(asio::socket_base::shutdown_send);
}
catch (system::system_error &e) {
std::cout << "Error " << e.what() << std::endl;
}
return 0;
}
如您所见,我们正在从字符串中逐个字符地发送,延迟为 1 秒。因此,当您启动服务器然后启动客户端时,服务器应该在大约 12 秒后收到整个消息。 async_read
在服务器中等待,直到 eof
到来 - 它由客户端在套接字上调用 shutdown
发送。
我正在尝试接收一个未知大小的缓冲区,我的部分代码如下所示:
void Connection::asyncRead()
{
auto self(shared_from_this());
socket_.async_read_some(boost::asio::buffer(buffer_out),
[this, self](boost::system::error_code ec, std::size_t length)
{
OnRead(ec, length);
});
}
我不知道缓冲区的大小,所以我尝试在固定大小的缓冲区中接收缓冲区,如何知道缓冲区是否结束?
如果您想要 send/receive 没有固定大小的邮件,您可以使用 您定义消息 header 的方法,例如使用 4 字节字段来存储消息内容的大小:
[header(4 bytes) to store the size of message][content of message]
那么你总是知道第一步是读取4个字节, 为数据准备缓冲区并读取更多数据,直到缓冲区被填满。
另一种方法是关闭套接字(以下是伪代码)
The receiving side | the sending side
--------------------------------------------------------------------------------
error_code ec; |
asio::read(sock,buf,ec) [2] |
| prepare some buffer with unknown size
| string buf;
| buf += ...; // add data
| asio::write(sock,buf)
| sock.shutdown(socket_base::shutdown_send); [1]
通过在发送端[1]调用sock.shutdown()
你可以通知
整个消息已发送的接收方。
然后在接收方读取消息后 [2] 您应该检查 ec
error-code 变量的状态是否为 boost::asio::eof
。
如果您收到 end-of-file
,您就知道该消息已完成。如果 ec
与 eof
不同,这意味着发生了错误。
从 1.66 boost 版本开始,您可以使用 dynamic_buffer
来存储数据,它将字符串或向量适配为缓冲区。
或者您可以考虑 streambuf
读取 non-fixed 个缓冲区。
编辑
使用 dynamic_buffer
添加。
根据参考资料,dynamic_buffer
可以在 async_read
、async_until
等自由函数中使用,但不能在 async_read_some
中作为套接字的成员函数使用。下面是客户端和服务器的代码:
服务器:
using namespace boost;
struct Data {
std::shared_ptr<asio::ip::tcp::socket> sock;
std::string buf; // buf is empty [1]
};
void readHandler (
const boost::system::error_code& ec,
size_t length,
std::shared_ptr<Data> d) {
std::cout << "readHandler" << std::endl;
if (ec == boost::asio::error::eof)
{
// here we got the whole message
std::cout << d->buf << std::endl;
}
else
{
std::cout << "Error" << std::endl;
}
}
int main() {
try {
asio::ip::tcp::endpoint ep(asio::ip::address_v4::any(),9999);
asio::io_service ios;
asio::ip::tcp::acceptor acceptor(ios, ep);
std::shared_ptr<asio::ip::tcp::socket> sock{new asio::ip::tcp::socket(ios)};
acceptor.accept(*sock);
std::shared_ptr<Data> data(new Data);
data->sock = move(sock);
boost::asio::async_read (*(data->sock), asio::dynamic_buffer(data->buf),
std::bind(readHandler,std::placeholders::_1, std::placeholders::_2,data)); // [2]
ios.run(); // wait until async_write is complete
}
catch (system::system_error &e) {
std::cout << "error " << e.what() << std::endl;
}
return 0;
}
在 [1] 中我们创建空缓冲区作为字符串 object,在 [2] 中我们调用 async_read
使用 dynamic_buffer
获取数据。当发送方关闭其一侧的套接字时,将调用传入 async_read
的处理程序。
客户:
using namespace boost;
int main() {
try {
asio::ip::tcp::endpoint ep(asio::ip::address::from_string("127.0.0.1"),9999);
asio::io_service ios;
asio::ip::tcp::socket sock(ios, ep.protocol());
sock.connect(ep);
std::string buf = "some message";
for (int i = 0; i < buf.size(); ++i) {
// synchronous function was used to make simpler code
asio::write(sock,asio::buffer(buf.c_str()+i,1)); // send 1 char
std::this_thread::sleep_for(std::chrono::seconds(1)); // delay 1 second
}
sock.shutdown(asio::socket_base::shutdown_send);
}
catch (system::system_error &e) {
std::cout << "Error " << e.what() << std::endl;
}
return 0;
}
如您所见,我们正在从字符串中逐个字符地发送,延迟为 1 秒。因此,当您启动服务器然后启动客户端时,服务器应该在大约 12 秒后收到整个消息。 async_read
在服务器中等待,直到 eof
到来 - 它由客户端在套接字上调用 shutdown
发送。