您应该在服务器中异步处理每个请求,即将处理分成单独的执行线程。这样服务器就可以保持响应,即他们可以在与其他客户端或服务器交谈时对新请求做出反应。
因此,在您的情况下,当两个客户端 1 和 2 向服务器 A 和 B 发送请求时,只有另一台服务器可以回答(或不回答),这两个服务器可能如下所示:
Server A: Server B:
Thread 0 | Thread 1 | Thread 2 Thread 0 | Thread 1 | Thread 2
listen... listen...
-> req 1 -> req 2
listen... | handle req 1 listen... | handle req 2
listen... | forward to B listen... | forward to A
-> req B | wait... -> req A | wait...
listen... | wait... | handle req B listen... | wait... | reject req A
listen... | -> B: rejected | answer req B listen... | wait...
listen... | forward to C listen... | -> A: answer
listen... | -> C: answer listen... | req 2 done
listen... | req 1 done listen...
listen... listen...
在这里,每个服务器的线程 0 除了侦听传入请求并旋转处理这些请求的其他线程之外没有其他用途。其他线程每个都处理一个请求,要么回答它,要么将它转发到所有服务器,或者如果它已被“污染”,则拒绝它。
注意:这些线程不一定必须是真正的线程对象。它们可以是 ASIO 异步*调用序列或某些线程框架(如 TBB)中的轻量级任务。
更新:我将为您发布一些 sceleton 伪代码,我将如何使用 Boost.Asio 实现服务器。为此,我想介绍一个对理解 Asio 的执行很有用的概念:您可以将其视为状态机,其中async_*
调用是状态转换,而处理程序是状态。通常,async_*
每个处理程序执行都有一个 -call,这意味着您从一种状态转到另一种状态。如果您在处理程序中有多个后续async_*
-call,则意味着该处理程序正在产生辅助执行线程。如果处理程序没有调用任何async_*
函数,则相应的执行线程结束。
现在到实施。
Thread 0 就像典型的 Asio 教程所示,创建一个套接字并监听传入的连接。唯一的事情是,在每个新的客户端连接上,都会产生一个新的执行线程(读取:处理程序序列):
accept_handler(client connection) {
async_read(client_request, request_handler) //spawn new thread of execution
async_accept(next client connection, accept_handler) //transition to accept_handler
}
线程 N: 以 :request_handler
开头
request_handler(client_request) {
if canProcess
async_send_answer(client, done_handler) //transition to done_handler
else //relay request to first server on list
async_connect(first server on list, connect_handler) //transition to connect_handler
}
通常done_handler
会记录成功的答案而不调用另一个async_*
函数,这意味着与客户端的连接将被关闭并且执行线程结束。
将请求发送到其他服务器的处理程序序列是典型的连接-发送-接收-断开序列:
connect_handler -- async_send(request) ---------> send_handler
send_handler -- async_read(answer) ----------> read_handler
read_handler (no answer) -- async_connect(next server) --> connect_handler
如果从其中一台服务器接收到答案或列表中没有更多服务器,则该循环结束:
read_handler (answer ok) -- async_send_answer(client) --> done_handler
read_handler (no more servers) -- async_send_fail(client) ----> done_handler