好不容易将cpprestsdk移植到MinGW,并编译通过,出于安全还是先将samples还有tests测试一下是否正常。
用samples/blackjack一测试就出现奇葩现象,server一端会时不时出现一次response中,两次发送headers,并且没有任何征兆。
当用gdb调试,break+command,断点打印浏览时,却从没有发生过一次奇怪现象。毕竟断点使得程序更加慢而且更加偏向串行的方式在运行。
下面是现象的抓包
正常发两次包,第一次72字节header,第二次222字节content。
异常也发了两次包,第一次72字节headers,第二次294 (72 + 222 )字节 headers + contents。
分析代码cpprestsdk/Release/src/http/http_server_asio.cpp,发送是由m_write,m_write_size两成员控制。
但搜索下来,只有一处赋值,“m_write = m_write_size = 0; ”,只能唯有用watch+command来观察。
观察点分别是 14: m_write_size, 15: m_write。
m_write_size 被修改触发断点。
然后序列化headers,并发送。
m_write 被修改触发断点。
发现 m_write_size 在http_headers.match()匹配content_length字段同时赋值,无语。m_write赋值的地方也无语,分析代码根本就不能搜索到。
从代码分析得到流程。
1. http_response的组成,http_headers一个字典容器,body是一个instream,还有一个用于发送的streambuf。
2. 我们的处理代码先向body写入内容,然后调用reply。
3. http_headers先序列化到streambuf,并发送第一个数据包。
4. 完成后将body写入streambuf,并发送第二个数据包。
正常跟异常两种情况下,第一次发包正常,第二次发包有差。原因可能是streambuf,没有清数据。
asio在取出iocp事件后,是先清streambuf数据,然后才会去回调handler,但为什么还要有例外不正常的情况发生呢。原因是cpprestsdk开发小组的成员在偷懒了。(小组压根就不打算移植到mingw,http_server的asio实现版只针对非windows,windows开发环境下默认使用httpsys实现版,所以就不会去考虑asio可能运行在winiocp的情况了)。
没有去理会bytes_transferred,直接忽略, 回调lambda [=](error_code& ec, size_t)。但是winiocp有一种情况,就是WriteEx时马上成功了,没有发生BLOCKING。投递出去的iocp overlapped,将会是没有错误,同时bytes_transferred也为0。这样一来,asio就只会consume(0),根本没有清理数据,数据残留了下来,后续的发送就是乱套的。这种情况下,就必须自己去做处理。
2020/06/17补充说明:
将boost从1.54换成1.66后,async_write的handler再也没有出现error_code跟bytes_transferred同时都为0的情况。这可能是旧版boost实现的问题?