You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ACE 7.0.6无法正确检测/处理带FIN捎带的应用数据问题求助

问题分析与解决方案

核心问题原因

  1. SSL Socket返回值逻辑完全颠倒
    普通TCP Socket中,recv返回0表示收到FIN包(连接正常关闭),返回-1表示出错;但你的代码把req_size_read == 0当成错误,==-1当成EOF,完全搞反了逻辑,导致FIN捎带数据时无法正确识别关闭信号。
    更关键的是,ACE_SSL_SOCK_Stream的recv行为和普通Socket有差异:当SSL连接收到FIN时,recv会返回-1,需通过SSL_get_error判断是否为SSL_ERROR_ZERO_RETURN(表示对方正常关闭),而非返回0。

  2. MSG_PEEK的误用
    你在handleIncomingData中用MSG_PEEK仅探测1字节数据,仅当返回值为1时才进入读取流程。若FIN捎带的数据长度大于1,或SSL层内部缓冲导致peek结果不符合预期,会直接触发错误分支,跳过正常数据读取。

  3. 循环读取未适配SSL特性
    SSL连接中,即使底层TCP收到FIN,SSL层可能还有未读取的应用数据,你的循环逻辑未处理“读完所有数据再处理关闭”的场景。

修复方案

1. 纠正recv返回值判断逻辑

针对ACE_SSL_SOCK_Stream的特性,正确处理返回值与SSL错误。

2. 移除不合理的MSG_PEEK探测

直接进入数据读取流程,避免误判。

3. 完善循环读取逻辑

读完所有可用数据后,再处理连接关闭信号。

修改后的代码示例

int SSLStreamConnection::handle_input(ACE_HANDLE hd)
{
    switch (getState())
    {
    case CONNECTED:
        handleIncomingData();
        break;
    //...
    }
    return 0; // 返回ACE标准处理值,避免事件循环异常
}

void SSLStreamConnection::handleIncomingData()
{
    readIncomingData();
}

void SSLStreamConnection::readIncomingData()
{
    std::array<char, 128> strDebug = { 0 };
    char* req_data_buf = nullptr;
    int req_buf_size = 0;
    ssize_t req_size_read = 0;
    bool connection_closed = false;

    while (true)
    {
        appCallback->rxBufferRequest(DEF_TRANSPORT_BUFFER_SIZE, req_data_buf, req_buf_size);
        const size_t tbuf_size = req_buf_size;
        req_size_read = socket.recv(req_data_buf, tbuf_size - 1, 0);

        if (req_size_read > 0)
        {
            // 正常读取到数据,传递给应用层
            if (appCallback)
                appCallback->receiveData(id, SOCK_RX_DATA, static_cast<int>(req_size_read), req_data_buf, peerAddr, context);
        }
        else if (req_size_read == 0)
        {
            // 普通TCP场景的FIN,SSL场景下极少出现,标记为异常关闭
            sprintf_s(strDebug.data(), strDebug.size(), "异常关闭 socketId=%u, errno=0x%x", id, errno);
            logError(strDebug.data());
            connection_closed = true;
            appCallback->rxBufferFreeCallback(req_data_buf, req_buf_size);
            break;
        }
        else // req_size_read == -1
        {
            // 处理SSL层错误
            unsigned long ssl_err = SSL_get_error(socket.ssl(), static_cast<int>(req_size_read));
            if (ssl_err == SSL_ERROR_ZERO_RETURN)
            {
                // SSL层收到FIN,连接正常关闭
                sprintf_s(strDebug.data(), strDebug.size(), "连接正常关闭 socketId=%u", id);
                logWarning(strDebug.data());
                connection_closed = true;
            }
            else if (ssl_err == SSL_ERROR_WANT_READ || ssl_err == SSL_ERROR_WANT_WRITE)
            {
                // SSL需要重试读写,释放缓冲区后退出循环,等待下一次事件触发
                appCallback->rxBufferFreeCallback(req_data_buf, req_buf_size);
                break;
            }
            else
            {
                // 其他SSL错误,异常关闭
                sprintf_s(strDebug.data(), strDebug.size(), "SSL错误 socketId=%u, err=0x%x", id, ssl_err);
                logError(strDebug.data());
                connection_closed = true;
            }
            appCallback->rxBufferFreeCallback(req_data_buf, req_buf_size);
            break;
        }
    }

    if (connection_closed && appCallback)
    {
        appCallback->receiveData(id, SOCK_CONNECTION_CLOSED, 0, nullptr, peerAddr, context);
        // 移除读事件掩码,避免重复触发handle_input
        reactor()->remove_handler(this, ACE_Event_Handler::READ_MASK);
    }
}

额外注意事项

  • 调用reactor()->remove_handler移除读掩码,防止连接关闭后仍收到无效读事件。
  • 处理SSL_ERROR_WANT_READ/WANT_WRITE:这是SSL层的正常重试请求,需退出当前循环,等待ACE Reactor再次触发事件后继续处理。
  • 不要依赖MSG_PEEK探测SSL连接状态,SSL层内部缓冲会导致peek结果不准确。

内容的提问来源于stack exchange,提问作者Samer Nabulsi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:40:17