简体   繁体   English

如何防止基于ASIO的服务器终止

[英]How to prevent ASIO based server from terminating

I have been reading some Boost ASIO tutorials. 我一直在阅读一些Boost ASIO教程。 So far, my understanding is that the entire send and receive is a loop that can be iterated only once. 到目前为止,我的理解是整个发送和接收是一个循环,只能循环一次。 Please have a look at the following simple code: 请看下面的简单代码:

client.cpp: client.cpp:

#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <iostream>
#include <string>

boost::asio::io_service io_service;
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::socket sock(io_service);
boost::array<char, 4096> buffer;

void read_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
  if (!ec)
  {
    std::cout << std::string(buffer.data(), bytes_transferred) << std::endl;
    sock.async_read_some(boost::asio::buffer(buffer), read_handler);
  }
}

void connect_handler(const boost::system::error_code &ec)
{
  if (!ec)
  {        
    sock.async_read_some(boost::asio::buffer(buffer), read_handler);
  }
}

void resolve_handler(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator it)
{
  if (!ec)
  {
    sock.async_connect(*it, connect_handler);
  }
}

int main()
{
  boost::asio::ip::tcp::resolver::query query("localhost", "2013");
  resolver.async_resolve(query, resolve_handler);
  io_service.run();
}

the program resolves an address, connects to server and reads the data, and finally ends when there is no data. 该程序resolves一个地址, connects到服务器并reads数据,最后在没有数据时ends My question: How can i continue this loop? 我的问题:如何继续循环? I mean, How can I keep this connection between a client and server during the entire lifetime of my application so that the server sends data whenever it has something to send? 我的意思是,如何在应用程序的整个生命周期中保持客户端与服务器之间的这种连接,以便服务器在任何可发送内容时发送数据? I tried to break this circle but everything seams trapped inside io_service.run() Same question holds in case of the my sever also: 我试图打破这个圈子,但所有缝隙都陷在io_service.run()中,如果我的服务器也遇到同样的问题:

server.cpp : server.cpp:

#include <boost/asio.hpp>
#include <string>

boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 2013);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
boost::asio::ip::tcp::socket sock(io_service);
std::string data = "Hello, world!";

void write_handler(const boost::system::error_code &ec, std::size_t bytes_transferred)
{
}

void accept_handler(const boost::system::error_code &ec)
{
  if (!ec)
  {
    boost::asio::async_write(sock, boost::asio::buffer(data), write_handler);
  }
}

int main()
{
  acceptor.listen();
  acceptor.async_accept(sock, accept_handler);
  io_service.run();
}

This is just an example. 这只是一个例子。 In a real application, I may like to keep the socket open and reuse it for other data exchanges(both read and write). 在实际的应用程序中,我可能想保持套接字打开状态,并将其重用于其他数据交换(读和写)。 How may I do that. 我该怎么做。

I value your kind comments. 我重视您的评论。 If you have references to some easy solutions addressing this issue, I appreciate if you mention it. 如果您引用了一些解决此问题的简单解决方案,那么如果您提到它,我将不胜感激。 Thank you 谢谢

Update (server sample code) 更新(服务器示例代码)

Based on the answer given below(update 2), I wrote the server code. 根据下面给出的答案(更新2),我编写了服务器代码。 Please note that the code is simplified (able to compile&run though). 请注意,代码已简化(尽管可以编译和运行)。 Also note that the io_service will never return coz it is always is waiting for a new connection. 还要注意,io_service永远不会返回coz,它始终在等待新的连接。 And that is how the io_service.run never returns and runs for ever . 这就是io_service.run永远不会返回并永远运行的方式 whenever you want io_service.run to return, just make the acceptor not to accept anymore. 每当您希望io_service.run返回时,只需使接受者不再接受即可。 please do this in one of the many ways that i don't currently remember.(seriously, how do we do that in a clean way? :) ) 请以我目前不记得的多种方式之一进行此操作。(严重的是,我们如何以一种干净的方式来做到这一点?:))

enjoy: 请享用:

#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <string>
#include <iostream>
#include <vector>
#include <time.h>
boost::asio::io_service io_service;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 2013);
boost::asio::ip::tcp::acceptor acceptor(io_service, endpoint);
//boost::asio::ip::tcp::socket sock(io_service);
std::string data = "Hello, world!";

class Observer;
std::vector<Observer*> observers;

class Observer
{
public:
    Observer(boost::asio::ip::tcp::socket *socket_):socket_obs(socket_){}
    void notify(std::string data)
    {
        std::cout << "notify called data[" << data << "]" << std::endl;
        boost::asio::async_write(*socket_obs, boost::asio::buffer(data) , boost::bind(&Observer::write_handler, this,boost::asio::placeholders::error));
    }
    void write_handler(const boost::system::error_code &ec)
    {
         if (!ec) //no error: done, just wait for the next notification
             return;
         socket_obs->close(); //client will get error and exit its read_handler
         observers.erase(std::find(observers.begin(), observers.end(),this));
         std::cout << "Observer::write_handler  returns as nothing was written" << std::endl;
    }
private:
        boost::asio::ip::tcp::socket *socket_obs;
};


class server
{
public:
     void CreatSocketAndAccept()
     {
          socket_ = new boost::asio::ip::tcp::socket(io_service);
          observers.push_back(new Observer(socket_));
          acceptor.async_accept(*socket_,boost::bind(&server::handle_accept, this,boost::asio::placeholders::error));
     }
      server(boost::asio::io_service& io_service)
      {
          acceptor.listen();
          CreatSocketAndAccept();
      }

      void handle_accept(const boost::system::error_code& e)
      {
          CreatSocketAndAccept();
      }
private:
  boost::asio::ip::tcp::socket *socket_;
};

class Agent
{
public:
    void update(std::string data)
    {
        if(!observers.empty())
        {
//          std::cout << "calling notify data[" << data << "]" << std::endl;
            observers[0]->notify(data);
        }
    }

};
Agent agent;
void AgentSim()
{
    int i = 0;
    sleep(10);//wait for me to start client
    while(i++ < 10)
    {
        std::ostringstream out("");
        out << data << i ;
//      std::cout << "calling update data[" << out.str() << "]" << std::endl;
        agent.update(out.str());
        sleep(1);
    }
}
void run()
{
    io_service.run();
    std::cout << "io_service returned" << std::endl;
}
int main()
{
    server server_(io_service);

    boost::thread thread_1(AgentSim);
    boost::thread thread_2(run);
    thread_2.join();
    thread_1.join();
}

You can simplify the logic of asio based porgrams like follows: each function that calls an async_X function provides a handler. 您可以简化基于asio的程序图的逻辑,如下所示:每个调用async_X函数的函数都提供一个处理程序。 This is a bit like transitions between states of a state machine, where the handlers are the states and the async-calls are transitions between states. 这有点像状态机状态之间的转换,其中处理程序是状态,异步调用是状态之间的转换。 Just exiting a handler without calling a async_* function is like a transition to an end state. 仅退出处理程序而不调用async_ *函数,就像过渡到结束状态一样。 Everything the program "does" (sending data, receiving data, connecting sockets etc.) occurs during the transitions. 程序“做”的所有事情(发送数据,接收数据,连接套接字等)在过渡期间都会发生。

If you see it that way, your client looks like this (only "good path", ie without errors): 如果您以这种方式看到它,那么您的客户端将如下所示(仅“良好路径”,即没有错误):

<start>         --(resolve)----> resolve_handler
resolve_handler --(connect)----> connect_handler
connect_handler --(read data)--> read_handler
read_handler    --(read data)--> read_handler

Your server loks like this: 您的服务器如下所示:

<start>         --(accept)-----> accept handler
accept_handler  --(write data)-> write_handler
write_handler   ---------------> <end>

Since your write_handler does not do anything, it makes a transition to the end state, meaning ioservice::run returns. 由于您的write_handler不执行任何操作,因此会转换为结束状态,这意味着ioservice::run返回。 The question now is, what do you want to do, after the data has been written to the socket? 现在的问题是,将数据写入套接字后,您想做什么? Depending on that, you will have to define a corresponding transition, ie an async-call that does what you want to do. 依赖于此,您将必须定义一个相应的过渡,即完成您想要执行的操作的异步调用。

Update: from your comment I see you want to wait for the next data to be ready ie for the next tick. 更新:从您的评论中,我看到您想等待下一个数据准备好,例如下一个刻度。 The transitions then look like this: 过渡看起来像这样:

write_handler   --(wait for tick/data)--> dataready
dataready       --(write data)----------> write_handler

You see, this introduces a new state (handler), I called it dataready , you could as well call it tick_handler or something else. 您会看到,这引入了一个新状态(处理程序),我将其称为dataready ,也可以将其tick_handler或其他名称。 The transition back to the write_handler is easy: 转换回write_handler很容易:

void dataready()
{
  // get the new data...
  async_write(sock, buffer(data), write_handler);
}

The transition from the write_handler can be a simple async_wait on some timer. write_handler的过渡可以是某个计时器上的简单async_wait If the data come from "outside" and you don't know exactly when they will be ready, wait for some time, check if the data are there, and if they are not, wait some more time: 如果数据来自“外部”,并且您不确定确切的数据准备就绪时间,请等待一段时间,检查数据是否存在,如果没有,请再等待一些时间:

write_handler    --(wait some time)--> checkForData
checkForData:no  --(wait some time)--> checkForData
checkForData:yes --(write data)------> write_handler

or, in (pseudo)code: 或者,在(伪)代码中:

void write_handler(const error_code &ec, size_t bytes_transferred)
{
  //...
  async_wait(ticklenght, checkForData);
}

void checkForData(/*insert wait handler signature here*/)
{
  if (dataIsReady())
  {
    async_write(sock, buffer(data), write_handler);
  }
  else
  {
    async_wait(shortTime, checkForData):
  }
}

Update 2: According to your comment, you already have an agent that does the time management somehow (calling update every tick). 更新2:根据您的评论,您已经有一个以某种方式进行时间管理的代理(每隔一个滴答叫一次更新)。 Here's how I would solve that: 这是我要解决的方法:

  1. Let the agent have a list of observers that get notified when there is new data in an update call. 让代理具有观察者列表,这些观察者列表将在更新调用中有新数据时得到通知。
  2. Let each observer handle one client connection (socket). 让每个观察者处理一个客户端连接(套接字)。
  3. Let the server just wait for incomming connections, create observers from them and register them with the Agent. 让服务器只等待传入的连接,从中创建观察者,然后将它们注册到代理。

I am not very firm in the exact syntax of ASIO, so this will be handwavy pseudocode: 我不太清楚ASIO的确切语法,因此这将是手工波浪的伪代码:

Server: 服务器:

void Server::accept_handler()
{
    obs = new Observer(socket);
    agent.register(obs);
    new socket; //observer takes care of the old one
    async_accept(..., accept_handler);
}

Agent: 代理:

void Agent::update()
{
    if (newDataAvailable())
    {
        for (auto& obs : observers)
        {
             obs->notify(data);
        }
    }
}

Observer: 观察报:

void Observer::notify(data)
{
    async_write(sock, data, write_handler);
}

void Observer::write_handler(error_code ec, ...)
{
     if (!ec) //no error: done, just wait for the next notification
         return;
     //on error: close the connection and unregister
     agent.unregister(this);
     socket.close(); //client will get error and exit its read_handler
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM