使用async_accept处理多个客户端
Handling multiple clients with async_accept
我正在用boost ASIO和协程编写一个安全的SSL回声服务器。我希望这个服务器能够服务多个并发客户端,这是我的代码
try {
boost::asio::io_service io_service;
boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield) {
auto ctx = boost::asio::ssl::context{ boost::asio::ssl::context::sslv23 };
ctx.set_options(
boost::asio::ssl::context::default_workarounds
| boost::asio::ssl::context::no_sslv2
| boost::asio::ssl::context::single_dh_use);
ctx.use_private_key_file(..); // My data setup
ctx.use_certificate_chain_file(...); // My data setup
boost::asio::ip::tcp::acceptor acceptor(io_service,
boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port));
for (;;) {
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sock{ io_service, ctx };
acceptor.async_accept(sock.next_layer(), yield);
sock.async_handshake(boost::asio::ssl::stream_base::server, yield);
auto ec = boost::system::error_code{};
char data_[1024];
auto nread = sock.async_read_some(boost::asio::buffer(data_, 1024), yield[ec]);
if (ec == boost::asio::error::eof)
return; //connection closed cleanly by peer
else if (ec)
throw boost::system::system_error(ec); //some other error, is this desirable?
sock.async_write_some(boost::asio::buffer(data_, nread), yield[ec]);
if (ec == boost::asio::error::eof)
return; //connection closed cleanly by peer
else if (ec)
throw boost::system::system_error(ec); //some other error
// Shutdown gracefully
sock.async_shutdown(yield[ec]);
if (ec && (ec.category() == boost::asio::error::get_ssl_category())
&& (SSL_R_PROTOCOL_IS_SHUTDOWN == ERR_GET_REASON(ec.value())))
{
sock.lowest_layer().close();
}
}
});
io_service.run();
}
catch (std::exception& e)
{
std::cerr << "Exception: " << e.what() << "n";
}
无论如何,我不确定上面的代码是否可以:理论上调用async_accept将把控制权返回给io_service管理器。
如果一个连接已经被接受,即它已经超过了async_accept线,另一个连接会被接受吗?
要理解你的问题的细节有点困难,因为代码是不完整的(例如,在你的块中有一个return
,但不清楚块的部分是什么)。
尽管如此,文档中还是包含了一个使用协程的TCP回显服务器示例。看来你基本上需要添加SSL支持,以适应您的需求。
如果您查看main
,它有以下块:
boost::asio::spawn(io_service,
[&](boost::asio::yield_context yield)
{
tcp::acceptor acceptor(io_service,
tcp::endpoint(tcp::v4(), std::atoi(argv[1])));
for (;;)
{
boost::system::error_code ec;
tcp::socket socket(io_service);
acceptor.async_accept(socket, yield[ec]);
if (!ec) std::make_shared<session>(std::move(socket))->go();
}
});
这个循环是无止境的,并且,在每次(成功)调用async_accept
之后,处理接受下一个连接(当这个连接和其他连接可能仍然是活动的)。
return; //connection closed cleanly by peer
为了说明这一点,这里有两个应用程序。
第一个是Python多处理echo客户端,改编自PMOTW:
import socket
import sys
import multiprocessing
def session(i):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_address = ('localhost', 5000)
print 'connecting to %s port %s' % server_address
sock.connect(server_address)
print 'connected'
for _ in range(300):
try:
# Send data
message = 'client ' + str(i) + ' message'
print 'sending "%s"' % message
sock.sendall(message)
# Look for the response
amount_received = 0
amount_expected = len(message)
while amount_received < amount_expected:
data = sock.recv(16)
amount_received += len(data)
print 'received "%s"' % data
except:
print >>sys.stderr, 'closing socket'
sock.close()
if __name__ == '__main__':
pool = multiprocessing.Pool(8)
pool.map(session, range(8))
细节不是那么重要(尽管它是Python,因此很容易阅读),但关键是它打开了8个进程,每个进程都使用相同的asio echo服务器(见下图)和300条消息。
运行时输出
...
received "client 1 message"
sending "client 1 message"
received "client 2 message"
sending "client 2 message"
received "client 3 message"
received "client 0 message"
sending "client 3 message"
sending "client 0 message"
...
显示回显会话确实是交错的。
现在是回显服务器。我稍微调整了文档中的示例:
#include <cstdlib>
#include <iostream>
#include <memory>
#include <utility>
#include <boost/asio.hpp>
using boost::asio::ip::tcp;
class session :
public std::enable_shared_from_this<session> {
public:
session(tcp::socket socket) : socket_(std::move(socket)) {}
void start() { do_read(); }
private:
void do_read() {
auto self(
shared_from_this());
socket_.async_read_some(
boost::asio::buffer(data_, max_length),
[this, self](boost::system::error_code ec, std::size_t length) {
if(!ec)
do_write(length);
});
}
void do_write(std::size_t length) {
auto self(shared_from_this());
socket_.async_write_some(
boost::asio::buffer(data_, length),
[this, self](boost::system::error_code ec, std::size_t /*length*/) {
if (!ec)
do_read();
});
}
private:
tcp::socket socket_;
enum { max_length = 1024 };
char data_[max_length];
};
class server {
public:
server(boost::asio::io_service& io_service, short port) :
acceptor_(io_service, tcp::endpoint(tcp::v4(), port)),
socket_(io_service) {
do_accept();
}
private:
void do_accept() {
acceptor_.async_accept(
socket_,
[this](boost::system::error_code ec) {
if(!ec)
std::make_shared<session>(std::move(socket_))->start();
do_accept();
});
}
tcp::acceptor acceptor_;
tcp::socket socket_;
};
int main(int argc, char* argv[]) {
const int port = 5000;
try {
boost::asio::io_service io_service;
server s{io_service, port};
io_service.run();
}
catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "n";
}
}
这表明这个服务器确实是交错的。
注意,这是而不是协程版本。虽然我曾经尝试过协程版本,但我无法在我目前的机器上构建它(而且,正如她在下面的评论中指出的那样,你可能无论如何都更喜欢现在这个更主流的版本)。
然而,这不是根本的区别,请原谅你的问题。非协程版本有显式回调,显式地启动新操作,提供下一个回调;协程版本使用了更顺序的范式。在两个版本中,每个调用都返回到asio
的控制循环,该循环监视所有当前可以进行的操作。
asio
协程文档:
协程允许您创建反映实际程序逻辑的结构。异步操作不拆分函数,因为没有处理程序来定义异步操作完成时应该发生什么。程序可以使用顺序结构,而不是让处理程序相互调用。
并不是说顺序结构使所有的操作都是顺序的——那样就完全不需要asio
了。
- 提升 Asio TCP 服务器 处理多个客户端
- 如何使用 winsock32 处理多客户端
- 如何处理在 CPP 中连接到服务器的多个客户端?
- VC++ 异常处理在 x86 和 x64 上对于 IBPP / Firebird 客户端有所不同
- 从QT Web服务器处理多个客户端
- ZeroMQ REQ/REP如何处理多个客户端
- 使用ACE_dll动态加载客户端C++dll,错误为未处理的异常
- 当函数有很多参数和客户端代码只需更改其中时,如何处理情况就可以处理
- 在C++winsock中处理多个客户端的方法
- 多线程服务器在一个线程中处理多个客户端
- 处理客户端连接的最有效方法(套接字编程)
- c++tcp多线程客户端/服务器-如何与线程套接字处理程序进行通信
- 在Windows下处理多个客户端连接的最佳方法(不使用线程)
- 加速和 Windows 套接字 - 正确处理 TCP 客户端断开连接方案
- 客户端类构造函数中未处理的提升解析调用
- 改进服务器以处理多个客户端
- Thrift Java 客户端无法正确处理联合
- 用于处理许多客户端 c++ 的分叉服务器
- C++自定义客户端处理程序
- 如何使用SOAP处理多个客户端