Я отправляю двоичные данные с клиента (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-соединений. Что случилось? Может ли причина быть брандмауэром?
Я попробовал простой 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 и сообщу, если ничего не найдено.