我是 Boost::Asio 的新手,并且遇到了 asio::ip::tcp::socket 存储的问题。我从 asio::ip::tcp::acceptor.async_accept() 调用接收服务器端套接字并将其存储在分配的内存中。然后,当我启动 socket.async_send() 调用时,什么也没有发生:没有任何操作,没有错误。如果套接字位于全局内存中,则一切正常。我应该怎么办?这是我的(伪)代码:
// Connecting coro
asio::awaitable<void> run_server(asio::io_context &context) // contex is globally allocated
{
asio::ip::tcp::acceptor acceptor(context, asio::ip::tcp::endpoint{asio::ip::make_address_v4(p_joinserver->get_ip_addr()), p_joinserver->get_port()});
while (true)
{
// Preliminary create a soket and place it into unordered map
auto psocket = std::make_shared<socket_t>(context);
auto handle = std::rand();
auto [iter, res] = p_server->connections.emplace(std::make_pair(handle,psocket));
*psocket = co_await acceptor.async_accept(asio::use_awaitable); // Works OK
asio::co_spawn(context, read_requests(handle), asio::detached);
}
}
// Receiving and sending coro
asio::awaitable<void> read_requests(int handle)
{
constexpr size_t buf_size = 1024;
std::string line(buf_size, '\0');
while (true)
{
auto psocket = p_server->connections.at(handle);
auto n_read = co_await psocket->async_read_some(
asio::buffer(line),
asio::use_awaitable); // Works OK
// <>
//..prepare sending
auto _psocket = p_server->connections.at(handle);
auto n_sent = co_await _psocket->async_send(
asio::buffer(line),
asio::use_awaitable); // Does nothing and throw no error
}
}
协程的美妙之处在于它们可以接受实际的参数。我只想写:
#include <boost/asio.hpp>
#include <boost/asio/co_spawn.hpp>
#include <iostream>
namespace asio = boost::asio;
using asio::ip::tcp;
// Receiving and sending coro
asio::awaitable<void> read_requests(tcp::socket s)
{
constexpr size_t buf_size = 1024;
std::string line(buf_size, '\0');
while (true)
{
auto n_read = co_await s.async_read_some(
asio::buffer(line),
asio::use_awaitable); // Works OK
// <>
//..prepare sending
auto n_sent = co_await s.async_send(
asio::buffer(line, n_read),
asio::use_awaitable); // Does nothing and throw no error
}
}
// Connecting coro
struct Config {
std::string get_ip_addr() const { return "127.0.0.1"; }
uint16_t get_port() const { return 8989; }
};
asio::awaitable<void> run_server(Config const* p_joinserver)
{
auto executor = co_await asio::this_coro::executor;
tcp::endpoint ep{
asio::ip::make_address_v4(p_joinserver->get_ip_addr()),
p_joinserver->get_port()};
tcp::acceptor acceptor(executor, ep);
while (true) {
auto socket = co_await acceptor.async_accept(asio::use_awaitable); // Works OK
asio::co_spawn(executor, read_requests(std::move(socket)), asio::detached);
}
}
int main() {
Config const cfg;
asio::io_context ioc;
co_spawn(ioc, run_server(&cfg), asio::detached);
ioc.run_for(std::chrono::seconds(10));
}
如果您坚持服务器还必须维护连接列表,我的首选机制是指向会话对象的共享指针。在这种情况下,
read_requests
应该是会话对象的成员函数。
更好的句柄是该会话的weak_ptr,因为它不仅保证是唯一的,而且本质上总是指向相应的会话对象实例(除非它已经终止,这可以是异步的)。
您可以在该网站上看到大量答案,例如
std::list<std::weak_ptr<Session> > sessions_;
或者,更惯用的说法是:
using Handle = std::weak_ptr<Session>;
std::list<Handle> sessions_;
这将允许服务器对过期的会话进行垃圾收集(那些由于网络错误/对等断开连接而关闭的会话):
sessions_.remove_if(&Handle::expired);