Я отправляю двоичные данные с клиента (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-подтверждение, но сервер не получает данных (почти никаких данных, иногда пакет или два фактически получены). Иногда я получаю сообщение об ошибке подтверждения связи "короткое чтение". Иногда я получаю недействительные данные.
Клиент подключается к серверу, рукопожатиям, отправляет 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. Что не так? Может ли причина быть брандмауэром?
Я пробовал простой TCP без SSL по тому же 443-порту, это прекрасно работает.
EDIT:
Пробовал добавлять "Content-Type: application/octet-stream", не помогает.
ИЗМЕНИТЬ 2:
Обычно я получаю заголовок HTTP POST. Затем я отправляю куски данных как chunk-size(4 bytes)chunk(chunk-size bytes)...
. Сервер получает chunk-size
штраф, но затем ничего. Клиент не уведомляет о проблемах сервера (без ошибок) и продолжает отправлять данные. Иногда сервер может получать кусок или два, иногда он получает недействительный chunk-size
, но в большинстве случаев просто ничего.
ИЗМЕНИТЬ 3:
Сравнивая захваченный трафик на клиенте и сервере, не обнаружил различий.
Решение
Я был введен в заблуждение с самого начала этой проблемой. Сузили его до неожиданных деталей:
Отправка через сокет SSL завершается с ошибкой, если я использую мультибуферы Boost.Asio в Boost v.1.48 (самый последний в данный момент). Пример:
// 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 и сообщит об этом, если ничего не найдено.