Possible Duplicate:
How to check if socket is closed in Boost.Asio?
在asio框架中是否有一种既定的方法可以在不发送任何数据的情况下确定TCP连接的另一端是否关闭?
将 Boost.asio 用于服务器进程,如果客户端在服务器响应请求之前超时或以其他方式断开连接,则服务器在完成请求并生成要发送的响应之前不会发现这一点,当发送立即产生连接中止错误时。
对于一些长时间运行的请求,这可能导致客户端取消并一遍又一遍地重试,堆积并行运行的同一请求的许多实例,使它们花费更长的时间并“滚雪球”成雪崩,使服务器无法使用。本质上,反复按 F5 是拒绝服务攻击。
不幸的是,在请求完成之前我无法开始发送响应,因此“流式传输”结果不是一种选择,我需要能够在请求处理期间的关键点进行检查,如果客户放弃了。
这个问题的关键是避免在接收处理程序中进行请求处理。以前,我在做这样的事情:
async_receive(..., recv_handler)
void recv_handler(error) {
if (!error) {
parse input
process input
async_send(response, ...)
相反,适当的模式更像这样:
async_receive(..., recv_handler)
void async_recv(error) {
if (error) {
canceled_flag = true;
} else {
// start a processing event
if (request_in_progress) {
capture input from input buffer
io_service.post(process_input)
}
// post another read request
async_receive(..., recv_handler)
}
}
void process_input() {
while (!done && !canceled_flag) {
process input
}
async_send(response, ...)
}
显然我遗漏了很多细节,但重要的部分是将处理作为单独的“事件”发布到 io_service 线程池中,以便可以同时运行额外的接收。这允许在处理过程中接收“连接中止”消息。但是请注意,这意味着两个线程必须相互通信,需要某种同步,并且正在处理的输入必须与接收调用所在的输入缓冲区分开保存,因为更多数据可能会因到期而到达附加读取调用。
编辑:
我还应该注意,如果您在处理过程中收到更多数据,您可能不想启动另一个异步处理调用。稍后的处理可能会先完成,结果可能会乱序发送给客户端。除非您使用的是 UDP,否则这可能是一个严重的错误。
这是一些伪代码:
async_read (=> read_complete)
read_complete
store new data in queue
if not currently processing
if a full request is in the queue
async_process (=> process_complete)
else ignore data for now
async_read (=> read_complete)
async_process (=> process_complete)
process data
process_complete
async_write_result (=> write_complete)
write_complete
if a full request is in the queue
async_process (=> process_complete)
因此,如果在处理请求时收到数据,它会排队但不会被处理。一旦处理完成并发送结果,我们就可以使用之前收到的数据再次开始处理。
这可以通过允许在写入先前请求的结果时进行处理来进一步优化,但这需要更加小心以确保结果的写入顺序与收到的请求相同。