为同步服务器提升asio,保持TCP会话开放(使用google proto缓冲区)

Coo*_*kie 6 c++ sockets tcp boost-asio

我目前有一个非常简单的boost :: asio服务器,在连接时发送状态更新(使用google proto缓冲区):

try
{
  boost::asio::io_service io_service;
  tcp::acceptor acceptor(io_service,tcp::endpoint(tcp::v4(), 13));
  for (;;)
  {
    tcp::socket socket(io_service);
    acceptor.accept(socket);
    ...
    std::stringstream message;
    protoMsg.SerializeToOstream(&message);
    boost::system::error_code ignored_error;
    boost::asio::write(socket, boost::asio::buffer(message.str()), ignored_error);
  }
}
catch (std::exception& e) { }
Run Code Online (Sandbox Code Playgroud)

我希望在接受新连接后将其扩展到第一次读取,检查收到的请求,并根据此消息发回不同的消息.我还想保持TCP连接打开,以便客户端不必重新连接,并且想要处理多个客户端(不是很多,可能是2或3).

我看了几个关于boost asio的例子,即异步时间tcp服务器聊天服务器,但两者都有点超过我的想法.我甚至不知道我是否需要异步服务器.我想我之后可以做一个阅读acceptor.accept(socket),但我想我不会继续听取进一步的请求.如果我进入循环,我想这意味着我只能处理一个客户端.所以我猜这意味着我必须去异步?是否有一个更简单的例子可能不是250行代码?或者我只是想通过这些例子咬我的方式?谢谢

mur*_*att 12

您从Boost.Asio文档中提到的示例实际上非常适合查看工作原理.你是对的,起初它可能看起来有点难以理解,特别是如果你对这些概念不熟悉的话.但是,我建议您从聊天服务器示例开始,并在您的计算机上构建它.这将使您能够更仔细地研究事物并开始改变事物,以了解它是如何工作的.让我引导您完成一些我认为重要的事情.

从您的描述中您想要做什么,似乎聊天服务器为您提供了一个良好的起点,因为它已经拥有您需要的类似部分.让服务器异步是您想要的,因为您可以轻松地使用单个线程处理多个客户端.从一开始就没有什么太复杂的了.

在这种情况下简化,异步意味着您的服务器在队列中工作,接受处理程序(任务)并执行它.如果队列中没有任何内容,则只等待将某些内容放入队列.在您的情况下,这意味着它可以是来自客户端的连接,来自客户端的新消息读取或类似的东西.为了使其工作,需要设置每个处理程序(处理对特定事件的反应的函数).

让我用聊天服务器示例中的代码解释一下.

服务器源文件中,您将看到在构造函数chat_server中调用的类start_accept.这里接受处理程序设置.

void start_accept()
{
    chat_session_ptr new_session(new chat_session(io_service_, room_)); // 1
    acceptor_.async_accept(new_session->socket(),                       // 2
        boost::bind(&chat_server::handle_accept, this, new_session,     // 3
            boost::asio::placeholders::error));                         // 4
}
Run Code Online (Sandbox Code Playgroud)

第1行:chat_session创建一个对象,表示一个客户端和服务器之间的会话.为accept创建会话(尚未连接任何客户端).

第2行:套接字的异步接受......

第3行:... chat_server::handle_accept当它发生时必然会调用.会话被传递以供连接的第一个客户端使用.

现在,如果我们看一下,handle_accept我们看到在客户端连接时,会start被调用会话(这只是在服务器和这个客户端之间启动).最后,如果其他客户也希望连接,则会提供新的接受.

void handle_accept(chat_session_ptr session, 
                   const boost::system::error_code& error)
{
    if (!error)
    {
        session->start();
    }
    start_accept();
}
Run Code Online (Sandbox Code Playgroud)

这也是你想拥有的.对传入连接的出色接受.如果多个客户端可以连接,则应始终存在其中一个未完成的服务器,以便服务器可以处理接受.

服务器和客户端如何交互都在会话中,您可以遵循相同的设计并修改它以执行您想要的操作.你提到服务器需要查看发送的内容并执行不同的操作.看看服务器调用chat_sessionstart功能和handle_accept.

void start()
{
    room_.join(shared_from_this());
    boost::asio::async_read(socket_,
        boost::asio::buffer(read_msg_.data(), chat_message::header_length),
        boost::bind(
            &chat_session::handle_read_header, shared_from_this(),
            boost::asio::placeholders::error));
}
Run Code Online (Sandbox Code Playgroud)

这里重要的是打电话给boost::asio::async_read.这也是你想要的.这会在套接字上显示一个优秀的读取,因此服务器可以读取客户端发送的内容.有一个绑定到此事件的处理程序(函数)chat_session::handle_read_header.只要服务器在套接字上读取内容,就会调用此方法.在此处理函数中,您可以开始使用特定代码来确定在发送特定消息时要执行的操作等等.

重要的是要知道,无论何时调用这些异步boost :: asio函数,都不会在该调用中发生(即如果调用函数read,则不会读取套接字).这是异步方面.你只需要为某些东西注册处理程序,并在发生这种情况时回调你的代码.因此,当调用此读取时,它将立即返回,并且您将返回handle_accept到服务器中(如果您按照调用的方式进行调用).如果你还记得那里我们也打电话start_accept来设置另一个异步接受.此时,您有两个未完成的处理程序正在等待另一个客户端连接或第一个客户端发送内容.根据先发生的事情,将调用该特定处理程序.

另外需要了解的是,每当运行某些东西时,它将不间断地运行,直到它完成所需的一切为止.其他处理程序必须等待,即使有突发事件触发它们.

最后,为了运行服务器,您需要io_service这是Asio的核心概念.

io_service.run();
Run Code Online (Sandbox Code Playgroud)

这是您在main函数中看到的一行.这只是说线程(示例中只有一个)应该运行io_service,这是处理程序在有工作要做的时候排队的队列.什么都没有,io_service只是等待(当然阻止主线程).

我希望这可以帮助您开始想要做的事情.你可以做很多事情和要学习的东西.我发现它是一个很棒的软件!祝好运!