如何确定套接字何时收到 EOF? (C++20 提升 asio 协程)
How to determine when a socket receives EOF? (C++20 boost asio coroutines)
考虑一个简单的回显服务器:
#include <boost/asio.hpp>
#include <boost/asio/experimental/as_tuple.hpp>
#include <iostream>
#include <vector>
using boost::asio::awaitable;
using boost::asio::buffer;
using boost::asio::co_spawn;
using boost::asio::io_context;
using boost::asio::detached;
namespace ip = boost::asio::ip;
using boost::asio::use_awaitable;
// echo server
awaitable<void> serve_coroutine(ip::tcp::socket s) {
std::vector<unsigned char> data_buf;
data_buf.resize(4096);
for (;;) {
auto n = co_await s.async_read_some(buffer(data_buf, data_buf.size()),
use_awaitable);
auto ep = s.remote_endpoint();
std::cout << "R from " << ep.address().to_string()
<< " " << ep.port() << " L=" << n << std::endl;
while (n) {
size_t written = co_await s.async_write_some(buffer(data_buf, n), use_awaitable);
n -= written;
}
}
}
awaitable<void> listen_coroutine(ip::tcp::acceptor& acceptor) {
for (;;) {
auto [e, client] = co_await acceptor.async_accept(
boost::asio::experimental::as_tuple(use_awaitable));
if (!e) {
auto ex = client.get_executor();
// create working coroutine for data-transmission
co_spawn(ex, serve_coroutine(std::move(client)), detached);
} else {
std::cerr << "accept failed: " << e.message() << std::endl;
}
}
}
int main(int argc, char** argv) {
if (argc < 3) {
std::cout << "Usage: " << argv[0]
<< " <bind_address> <bind_port>" << std::endl;
return 1;
}
try {
io_context ctx;
// bind address
ip::tcp::endpoint listen_endpoint{ip::make_address(argv[1]),
static_cast<ip::port_type>(std::stoi(argv[2]))};
// create acceptor
ip::tcp::acceptor acceptor{ctx, listen_endpoint};
// add coroutine to execution queue
co_spawn(ctx, listen_coroutine(acceptor), detached);
// start executing coroutines
ctx.run();
} catch (boost::system::system_error& e) {
std::cerr << "boost system error: " << e.what() << std::endl;
} catch (std::exception& e) {
std::cerr << "E: " << e.what() << std::endl;
}
return 0;
}
(使用 g++ -std=c++20
或 clang++ -stdlib=libc++ -fcoroutines-ts
构建)
当远程端关闭连接时,co_await async_read_some
从不returns
似乎 boost io_service 在关闭连接时简单地破坏了一切
如果我将一个对象插入 serve_coroutine
并跟踪它的构造函数和析构函数,我会发现它在关闭连接时被破坏了
那么处理连接关闭事件的正确方法是什么?如果你在开发游戏,你需要清除玩家的数据,当你确定他的连接关闭时,告诉所有人他已经下线了
你可以捕获异常:
for (;;) {
try {
auto n = co_await s.async_read_some(
buffer(data_buf, data_buf.size()), use_awaitable);
auto ep = s.remote_endpoint();
std::cout << "R from " << ep.address().to_string() << " "
<< ep.port() << " L=" << n << std::endl;
while (n) {
size_t written = co_await s.async_write_some(
buffer(data_buf, n), use_awaitable);
n -= written;
}
} catch (std::exception& e) {
std::cerr << "boost system error: " << e.what() << std::endl;
}
}
哪个会打印
boost system error: End of file [asio.misc:2]
或者您可以使用其他方法接收 error_code。您可以在您的清单中看到一个示例:
auto [e, client] = co_await acceptor.async_accept(
boost::asio::experimental::as_tuple(use_awaitable));
所以,例如:
for (;;) {
auto [ec, n] = co_await s.async_read_some(
buffer(data_buf, data_buf.size()),
boost::asio::experimental::as_tuple(use_awaitable));
auto ep = s.remote_endpoint();
std::cout << "R from " << ep.address().to_string() << " " << ep.port()
<< " L=" << n << " (" << ec.message() << ")" << std::endl;
if (!ec)
break;
while (n) {
size_t written =
co_await s.async_write_some(buffer(data_buf, n), use_awaitable);
n -= written;
}
}
将显示如下内容:
R from 127.0.0.1 51586 L=4 (Success)
R from 127.0.0.1 51586 L=1 (Success)
R from 127.0.0.1 51586 L=1 (Success)
R from 127.0.0.1 51586 L=1 (Success)
R from 127.0.0.1 51586 L=0 (End of file)
R from 127.0.0.1 51590 L=1 (Success)
R from 127.0.0.1 51590 L=1 (Success)
R from 127.0.0.1 51590 L=0 (End of file)
考虑一个简单的回显服务器:
#include <boost/asio.hpp>
#include <boost/asio/experimental/as_tuple.hpp>
#include <iostream>
#include <vector>
using boost::asio::awaitable;
using boost::asio::buffer;
using boost::asio::co_spawn;
using boost::asio::io_context;
using boost::asio::detached;
namespace ip = boost::asio::ip;
using boost::asio::use_awaitable;
// echo server
awaitable<void> serve_coroutine(ip::tcp::socket s) {
std::vector<unsigned char> data_buf;
data_buf.resize(4096);
for (;;) {
auto n = co_await s.async_read_some(buffer(data_buf, data_buf.size()),
use_awaitable);
auto ep = s.remote_endpoint();
std::cout << "R from " << ep.address().to_string()
<< " " << ep.port() << " L=" << n << std::endl;
while (n) {
size_t written = co_await s.async_write_some(buffer(data_buf, n), use_awaitable);
n -= written;
}
}
}
awaitable<void> listen_coroutine(ip::tcp::acceptor& acceptor) {
for (;;) {
auto [e, client] = co_await acceptor.async_accept(
boost::asio::experimental::as_tuple(use_awaitable));
if (!e) {
auto ex = client.get_executor();
// create working coroutine for data-transmission
co_spawn(ex, serve_coroutine(std::move(client)), detached);
} else {
std::cerr << "accept failed: " << e.message() << std::endl;
}
}
}
int main(int argc, char** argv) {
if (argc < 3) {
std::cout << "Usage: " << argv[0]
<< " <bind_address> <bind_port>" << std::endl;
return 1;
}
try {
io_context ctx;
// bind address
ip::tcp::endpoint listen_endpoint{ip::make_address(argv[1]),
static_cast<ip::port_type>(std::stoi(argv[2]))};
// create acceptor
ip::tcp::acceptor acceptor{ctx, listen_endpoint};
// add coroutine to execution queue
co_spawn(ctx, listen_coroutine(acceptor), detached);
// start executing coroutines
ctx.run();
} catch (boost::system::system_error& e) {
std::cerr << "boost system error: " << e.what() << std::endl;
} catch (std::exception& e) {
std::cerr << "E: " << e.what() << std::endl;
}
return 0;
}
(使用 g++ -std=c++20
或 clang++ -stdlib=libc++ -fcoroutines-ts
构建)
当远程端关闭连接时,co_await async_read_some
从不returns
似乎 boost io_service 在关闭连接时简单地破坏了一切
如果我将一个对象插入 serve_coroutine
并跟踪它的构造函数和析构函数,我会发现它在关闭连接时被破坏了
那么处理连接关闭事件的正确方法是什么?如果你在开发游戏,你需要清除玩家的数据,当你确定他的连接关闭时,告诉所有人他已经下线了
你可以捕获异常:
for (;;) {
try {
auto n = co_await s.async_read_some(
buffer(data_buf, data_buf.size()), use_awaitable);
auto ep = s.remote_endpoint();
std::cout << "R from " << ep.address().to_string() << " "
<< ep.port() << " L=" << n << std::endl;
while (n) {
size_t written = co_await s.async_write_some(
buffer(data_buf, n), use_awaitable);
n -= written;
}
} catch (std::exception& e) {
std::cerr << "boost system error: " << e.what() << std::endl;
}
}
哪个会打印
boost system error: End of file [asio.misc:2]
或者您可以使用其他方法接收 error_code。您可以在您的清单中看到一个示例:
auto [e, client] = co_await acceptor.async_accept(
boost::asio::experimental::as_tuple(use_awaitable));
所以,例如:
for (;;) {
auto [ec, n] = co_await s.async_read_some(
buffer(data_buf, data_buf.size()),
boost::asio::experimental::as_tuple(use_awaitable));
auto ep = s.remote_endpoint();
std::cout << "R from " << ep.address().to_string() << " " << ep.port()
<< " L=" << n << " (" << ec.message() << ")" << std::endl;
if (!ec)
break;
while (n) {
size_t written =
co_await s.async_write_some(buffer(data_buf, n), use_awaitable);
n -= written;
}
}
将显示如下内容:
R from 127.0.0.1 51586 L=4 (Success)
R from 127.0.0.1 51586 L=1 (Success)
R from 127.0.0.1 51586 L=1 (Success)
R from 127.0.0.1 51586 L=1 (Success)
R from 127.0.0.1 51586 L=0 (End of file)
R from 127.0.0.1 51590 L=1 (Success)
R from 127.0.0.1 51590 L=1 (Success)
R from 127.0.0.1 51590 L=0 (End of file)