提升asio ssl写入部分数据

boost asio ssl writing part of data

我的 client-server 应用程序,通过 boost asio 进行通信,使用功能:

当连接开始时,客户端向服务器发送一堆请求,服务器发回一些响应。 将 asio::ssl 添加到项目后,我遇到了以下问题。

有时,1/5 次,服务器只读取请求的第一个固定部分。当客户端断开连接时,服务器获取所有错过的请求。

在客户端,一切似乎都很好,调用的 callbakcs 没有错误,写入的大小也合适。但是数据包嗅探器的结果显示客户端没有发送这部分请求。

客户:

每个 "frame" 的大小位于 header,首先必须至少阅读 header。 Thread Worker 用于后台工作,并将准备好的数据包推送到存储。

using SSLSocket = boost::asio::ssl::stream<boost::asio::ip::tcp::socket>;


class AsyncStrategy :
    public NetworkStrategy
{
    // other data...

    void _WriteHandler(const boost::system::error_code& err, size_t bytes);

    bool Connect(const boost::asio::ip::tcp::endpoint& endpoint);

    void _BindMessage();
    void _BindMessageRemainder(size_t size);
    void _AcceptMessage(const boost::system::error_code& err_code, size_t bytes);
    void _AcceptMessageRemainder(const boost::system::error_code& err_code, size_t bytes);

    // to keep io_service running
    void _BindTimer();
    void _DumpTimer(const boost::system::error_code& error);

    void _SolveProblem(const boost::system::error_code& err_code);

    void _Disconnect();

    bool verify_certificate(bool preverified,
        boost::asio::ssl::verify_context& ctx);

    PacketQuery query;

    boost::array <Byte, PacketMaxSize> WriteBuff;
    boost::array <Byte, PacketMaxSize> ReadBuff;

    boost::asio::ip::tcp::endpoint ep;
    boost::asio::io_service service;
    boost::asio::deadline_timer _Timer{ service };
    boost::asio::ssl::context _SSLContext;
    SSLSocket sock;

    boost::thread Worker;

    bool _ThreadWorking;
    bool _Connected = false;
};


AsyncStrategy::AsyncStrategy( MessengerAPI& api)
        : API{api},_SSLContext{service,boost::asio::ssl::context::sslv23 },
                sock{ service,_SSLContext }, _Timer{service},
                    Worker{ [&]() {
                                _BindTimer();
                                service.run();
                        } },  
                _ThreadWorking{ true }
{
    _SSLContext.set_verify_mode(boost::asio::ssl::verify_peer);
    _SSLContext.set_verify_callback(
        boost::bind(&AsyncStrategy::verify_certificate, this, _1, _2));

    _SSLContext.load_verify_file("ca.pem");
}


bool AsyncStrategy::verify_certificate(bool preverified,
    boost::asio::ssl::verify_context& ctx)
{
    return preverified;
}

void AsyncStrategy::_BindMessage()
{
    boost::asio::async_read(sock, buffer(ReadBuff,BaseHeader::HeaderSize()),
            boost::bind(&AsyncStrategy::_AcceptMessage, this, _1, _2));
}

bool AsyncStrategy::Connect(const boost::asio::ip::tcp::endpoint& endpoint)
{
    ep = endpoint;

    boost::system::error_code err;
    sock.lowest_layer().connect(ep, err);

    if (err)
        throw __ConnectionRefused{};

    // need blocking handshake
    sock.handshake(boost::asio::ssl::stream_base::client, err);

    if (err)
        throw __ConnectionRefused{};

    _BindMessage();
    return true;
}


void AsyncStrategy::_AcceptMessage(const boost::system::error_code& err_code, size_t bytes)
{
     // checking header, to see, packet ends or not
     // if there is more data in packet, read rest my binding function
    // pseudocode
    if( need_load_more )
        _BindMessageRemainder(BytesToReceive(FrameSize));
        return;
    }
    // if not use this bind this function next time
    _CheckPacket(ReadBuff.c_array(), bytes);
    _BindMessage();
}

void AsyncStrategy::_AcceptMessageRemainder(const boost::system::error_code& err_code, size_t bytes)
{
    if (err_code)
    {
        _SolveProblem(err_code);
        return;
    }
    _CheckPacket(ReadBuff.c_array(), bytes + BaseHeader::HeaderSize()); 
    _BindMessage();
}

bool AsyncStrategy::Send(const TransferredData& Data)
{
    // alreay known, that that data fits in buffer
    Data.ToBuffer(WriteBuff.c_array()); 
    boost::asio::async_write(sock,
            buffer(WriteBuff, Data.NeededSize()),
            boost::bind(&AsyncStrategy::_WriteHandler, this, _1, _2));

    return true;
}

void AsyncStrategy::_WriteHandler(const boost::system::error_code& err, size_t bytes)
{  
    if (err)
        _SolveProblem(err);
}

去掉所有ssl东西后,数据传输正常。正如我所提到的,在 ssl 集成之前一切正常。

求解决方法,发现如果延迟发送,试过200ms,所有数据传输正常。

Win10,提升 1.60,OpenSSL 1.0.2n

我想我的代码可能有错误,但我几乎尝试了所有我想的。寻求建议。

我们看不到 Send 实际上是如何调用的。

可能需要同步

我们可以它每次重复使用同一个缓冲区,因此两次写入重叠会破坏该缓冲区。

我们可以还看到您没有验证 Data 参数的大小是否适合 PacketMaxSize 缓冲区。

这意味着如果超过预期的缓冲区大小,您不仅会丢失数据,还会调用 Undefined Behaviour