我目前有一个非常简单的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) { }
我想在接受新连接后将其扩展到第一次读取,检查收到的请求,并根据此消息发回不同的消息。我还想保持TCP连接打开,这样客户端就不必重新连接,并且想要处理多个客户端(不多,也许是2或3)。
我看了一些关于提升asio的例子,即the async time tcp server和chat server,但两者都有点超过我的想法。我甚至不知道我是否需要异步服务器。我想我可以在acceptor.accept(socket)
后进行阅读,但我想我不会继续听取进一步的请求。如果我进入循环,我想这意味着我只能处理一个客户端。所以我猜这意味着我必须去异步?是否有一个更简单的例子可能不是250行代码?或者我只是想通过这些例子咬我的方式?感谢
答案 0 :(得分:12)
您从Boost.Asio文档中提到的示例实际上非常适合查看工作原理。你是对的,起初它可能看起来有点难以理解,特别是如果你对这些概念不熟悉的话。但是,我建议您从聊天服务器示例开始,并在您的计算机上构建它。这将使您能够更仔细地研究事物并开始改变事物,以了解它是如何工作的。让我引导您完成一些我认为重要的事情。
从您的描述中您想要做什么,似乎聊天服务器为您提供了一个良好的起点,因为它已经拥有您需要的类似部分。让服务器异步是您想要的,因为您可以轻松地使用单个线程处理多个客户端。从一开始就没有什么太复杂的了。
简化,在这种情况下异步意味着您的服务器在队列中工作,接受处理程序(任务)并执行它。如果队列中没有任何内容,则只等待将某些内容放入队列。在您的情况下,这意味着它可以是来自客户端的连接,来自客户端的新消息读取或类似的东西。为了使其工作,需要设置每个处理程序(处理对特定事件的反应的函数)。
让我用聊天服务器示例中的代码解释一下。
在server source file中,您会看到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
}
第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();
}
这也是你想拥有的。对传入连接的出色接受。如果多个客户端可以连接,则应始终存在其中一个未完成的服务器,以便服务器可以处理接受。
服务器和客户端之间的交互方式都在会话中,您可以遵循相同的设计并修改它以执行您想要的操作。你提到服务器需要查看发送的内容并执行不同的操作。请查看chat_session
中start
和服务器调用的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));
}
这里重要的是致电boost::asio::async_read
。这也是你想要的。这会在套接字上显示一个优秀的读取,因此服务器可以读取客户端发送的内容。有一个处理程序(函数)绑定到此事件chat_session::handle_read_header
。只要服务器在套接字上读取内容,就会调用此方法。在此处理函数中,您可以开始使用特定代码来确定在发送特定消息时要执行的操作等等。
重要的是要知道,无论何时调用这些异步boost :: asio函数,都不会在该调用中发生(即如果调用函数read,则不会读取套接字)。这是异步方面。你只需要为某些东西注册处理程序,并在发生这种情况时回调你的代码。因此,当调用此读取时,它将立即返回,并且您将返回服务器的handle_accept
(如果您按照调用的方式进行调用)。如果你还记得那里我们也会调用start_accept
来设置另一个异步接受。此时,您有两个未完成的处理程序正在等待另一个客户端连接或第一个客户端发送一些东西。根据先发生的事情,将调用该特定处理程序。
另外需要了解的是,无论何时运行某些东西,它都会不间断地运行,直到它完成所需的一切为止。其他处理程序必须等待即使有突发事件触发它们。
最后,为了运行服务器,您需要io_service
这是Asio的核心概念。
io_service.run();
这是您在main
函数中看到的一行。这只是说线程(示例中只有一个)应该运行io_service,这是处理程序在有工作要做的时候排队的队列。什么都没有,io_service只是等待(当然阻止主线程)。
我希望这有助于您开始使用您想要做的事情。你可以做很多事情和要学习的东西。我发现它是一个很棒的软件!祝你好运!
答案 1 :(得分:3)
如果其他人想要这样做,这里是达到目标的最小值:(类似于教程,但有点短,有点不同)
class Session : public boost::enable_shared_from_this<Session>
{
tcp::socket socket;
char buf[1000];
public:
Session(boost::asio::io_service& io_service)
: socket(io_service) { }
tcp::socket& SocketRef() { return socket; }
void Read() {
boost::asio::async_read( socket,boost::asio::buffer(buf),boost::asio::transfer_at_least(1),boost::bind(&Session::Handle_Read,shared_from_this(),boost::asio::placeholders::error));
}
void Handle_Read(const boost::system::error_code& error) {
if (!error)
{
//read from buffer and handle requests
//if you want to write sth, you can do it sync. here: e.g. boost::asio::write(socket, ..., ignored_error);
Read();
}
}
};
typedef boost::shared_ptr<Session> SessionPtr;
class Server
{
boost::asio::io_service io_service;
tcp::acceptor acceptor;
public:
Server() : acceptor(io_service,tcp::endpoint(tcp::v4(), 13)) { }
~Server() { }
void operator()() { StartAccept(); io_service.run(); }
void StartAccept() {
SessionPtr session_ptr(new Session(io_service));
acceptor.async_accept(session_ptr->SocketRef(),boost::bind(&Server::HandleAccept,this,session_ptr,boost::asio::placeholders::error));
}
void HandleAccept(SessionPtr session,const boost::system::error_code& error) {
if (!error)
session->Read();
StartAccept();
}
};
从我通过试验和错误阅读中收集到的内容:我在运算符()()中将其踢开,这样您就可以在后台运行另一个线程。您运行一个Server实例。要处理多个客户端,您需要一个额外的类,我将其称为会话类。要使asio清理死会话,您需要一个如上所述的共享指针。否则代码应该让你开始。