我一直在阅读一些Boost ASIO教程。到目前为止,我的理解是整个发送和接收是一个只能迭代一次的循环。请查看以下简单代码:
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();
}
程序resolves
地址,connects
到服务器和reads
数据,最后 ends
,当没有数据时。
我的问题:我怎样才能继续这个循环?我的意思是,如何在应用程序的整个生命周期内保持客户端和服务器之间的连接,以便服务器在 要发送的内容时发送数据?
我试图打破这个圈子但是所有接缝都被困在io_service.run()
内
如果我的服务器也同样问题:
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();
}
这只是一个例子。在实际应用程序中,我可能希望保持套接字打开并将其重用于其他数据交换(读取和写入)。我怎么能这样做。
我非常重视你的评论。如果你有一些解决这个问题的简单解决方案的参考,我很感激,如果你提到它。 谢谢
更新(服务器示例代码)
根据下面给出的答案(更新2),我编写了服务器代码。请注意,代码已经简化(能够编译和运行)。另请注意,io_service永远不会返回,因为它始终在等待新的连接。 这就是io_service.run
永远不会返回并永远运行的方式 。每当你想要io_service.run返回时,只需让接受者不再接受。请以我目前不记得的许多方式之一来做这件事。(说真的,我们如何以干净的方式做到这一点?:))
享受:
#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();
}
答案 0 :(得分:5)
您可以简化基于asio的图表的逻辑,如下所示:调用async_X函数的每个函数都提供了一个处理程序。这有点像状态机状态之间的转换,其中处理程序是状态,异步调用是状态之间的转换。只是在不调用async_ *函数的情况下退出处理程序就像转换到结束状态一样。程序“执行”(发送数据,接收数据,连接套接字等)的所有内容都发生在转换过程中。
如果您这样看,您的客户端看起来像这样(只有“好路径”,即没有错误):
<start> --(resolve)----> resolve_handler
resolve_handler --(connect)----> connect_handler
connect_handler --(read data)--> read_handler
read_handler --(read data)--> read_handler
你的服务器是这样的:
<start> --(accept)-----> accept handler
accept_handler --(write data)-> write_handler
write_handler ---------------> <end>
由于write_handler
没有做任何事情,它会转换到结束状态,这意味着ioservice::run
会返回。现在的问题是,在将数据写入套接字后,您想要做什么?根据这一点,您必须定义相应的转换,即执行您想要执行的操作的异步调用。
<强>更新强> 从你的评论我看到你想等待下一个数据准备就绪,即下一个滴答。然后转换看起来像这样:
write_handler --(wait for tick/data)--> dataready
dataready --(write data)----------> write_handler
你看,这引入了一个新的状态(处理程序),我称之为dataready
,你可以称之为tick_handler
或其他东西。转回write_handler很容易:
void dataready()
{
// get the new data...
async_write(sock, buffer(data), write_handler);
}
write_handler
在某个计时器上的转换可能很简单async_wait
。如果数据来自“外部”并且您不确切知道它们何时准备就绪,请等待一段时间,检查数据是否存在,如果不存在,请等待一段时间:
write_handler --(wait some time)--> checkForData
checkForData:no --(wait some time)--> checkForData
checkForData:yes --(write data)------> write_handler
或,在(伪)代码中:
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):
}
}
更新2: 根据你的评论,你已经有一个代理人以某种方式进行时间管理(每次打勾调用更新)。以下是我将如何解决这个问题:
我对ASIO的确切语法不是很坚定,所以这将是handwavy伪代码:
服务器:
void Server::accept_handler()
{
obs = new Observer(socket);
agent.register(obs);
new socket; //observer takes care of the old one
async_accept(..., accept_handler);
}
代理:
void Agent::update()
{
if (newDataAvailable())
{
for (auto& obs : observers)
{
obs->notify(data);
}
}
}
观察:
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
}