【发布时间】:2023-03-25 13:41:02
【问题描述】:
我将二进制数据从客户端 (Debian 6.0.3) 发送到服务器 (Windows Server 2003)。为了绕过大多数防火墙,我使用 HTTPS POST。客户端和服务器使用 Boost.Asio 和 OpenSSL 实现。首先,我实现了最简单的版本,它运行良好。
HTTP 标头:
POST / HTTP/1.1
User-Agent: my custom client v.1
[binary data]
(如果这很重要,[binary data] 不是 base64 编码的)
然后,在另一台客户端机器上它失败了(连接到同一台服务器机器)。行为不稳定。连接始终建立良好(端口 443)。大多数时候我通过 SSL 握手很好,但服务器没有收到数据(几乎没有数据,有时实际上收到一两个数据包)。有时我会收到 SSL 握手错误“短读”。有时我会收到无效数据。
客户端连接到服务器,握手,发送 HTTP POST 标头,然后无限发送二进制数据,直到出现问题。对于测试,我使用自定义生成的 SSL 证书。
服务器代码:
namespace ssl = boost::asio::ssl;
ssl::context context(io_service, ssl::context::sslv23);
context.set_options(ssl::context::default_workarounds | ssl::context::no_sslv2);
context.use_certificate_chain_file("server.pem");
context.use_private_key_file("server.pem", boost::asio::ssl::context::pem);
ssl::stream<tcp::socket> socket(io_service, context);
// standard connection accepting
socket.async_handshake(ssl::stream_base::server, ...);
...
boost::asio::async_read_until(socket, POST_header, "\r\n\r\n", ...);
...
客户端代码:
ssl::context context(io_service, ssl::context::sslv23);
context.load_verify_file("server.crt");
socket.reset(new ssl::stream<tcp::socket>(io_service, context));
socket->set_verify_mode(ssl::verify_none);
// standard connection
socket.async_handshake(ssl::stream_base::client, ...);
...
(错误处理连同无关代码一起省略)
如您所见,这是最简单的 SSL 连接。怎么了?原因可能是防火墙吗?
我在同一个 443 端口上尝试了不带 SSL 的简单 TCP,效果很好。
编辑:
尝试添加“Content-Type: application/octet-stream”,没有帮助。
编辑 2:
通常我会收到 HTTP POST 标头。然后我以chunk-size(4 bytes)chunk(chunk-size bytes)... 发送数据块。服务器收到chunk-size 很好,但是什么也没有。客户端不通知服务器问题(无错误)并继续发送数据。有时服务器可以接收一两个块,有时它接收到无效的chunk-size,但大多数时候什么都没有。
编辑 3:
比较在客户端和服务器上捕获的流量,没有发现任何差异。
解决方案
我从一开始就被这个问题误导了。将其缩小到令人惊讶的细节:
如果我在 Boost v.1.48(目前最新的一个)中使用 Boost.Asio 多缓冲区,则通过 SSL 套接字发送会失败。示例:
// data to send, protocol is [packet size: 4 bytes][packet: packet_size bytes]
std::vector<char> packet = ...;
uint32_t packet_size = packet.size();
// prepare buffers
boost::array<boost::asio::const_buffer, 2> bufs = {{boost::asio::buffer(&packet_size, sizeof(packet_size)), boost::asio::buffer(packet)}};
// send multi buffers by single call
boost::asio::async_write(socket, bufs, ...);
在此示例中分别发送packet_size 和packet 可以解决此问题。我绝不会将任何可疑行为称为错误,尤其是当它与 Boost 库有关时。但这一个看起来真的像一个错误。在 Boost v.1.47 上试过 - 工作正常。尝试使用通常的 TCP 套接字(不是 SSL 套接字) - 工作正常。在 Linux 和 Windows 上都一样。
我将在 Asio 邮件列表中找到有关此问题的任何报告,如果没有找到,我将报告。
【问题讨论】:
-
不使用 SSL 是否可以工作?您确定二进制数据不包含嵌入的换行符吗?
-
@AlanStokes:它可以工作,至少在长时间测试期间在另一台客户端机器上工作。嵌入式数据可以包含任何东西,这有关系吗?
-
@AlanStokes:抱歉,前面的评论不准确:它在这台机器上不使用 SSL,在另一台机器上与 SSL 一起工作。我只是无法理解有什么区别。这些机器位于不同的网络中。这就是为什么我怀疑防火墙,但重新检查并没有发现。
-
我建议你在工作和不工作上尝试 Wireshark 并进行比较。 (当然,关闭 SSL 会更容易。)
-
@AlanStokes:是的,尝试分析流量。在双方我看到相同:“客户端->服务器:TLSv1应用程序数据,应用程序数据,...”,“服务器->客户端:TCP https ->一些端口[ACK] ...”
标签: c++ ssl https boost-asio