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

TCP/IP与Simatic SPS通信异常排查:recv函数理解偏差、While循环问题还是单Socket使用导致?

单个TCP Socket与Simatic SPS通信时的循环阻塞问题

我来帮你拆解下这个问题:首先可以明确的是,单个TCP Socket同时完成收发是完全合规且可行的——TCP是全双工协议,本身就支持同一连接上同时进行数据的发送和接收,不存在官方禁止这种用法的情况,所以你的这个假设可以先排除。

回到你的问题场景:

  • 首次启动流程:SPS接收、处理并回传数据后,你的循环既不执行CsvWriter也无法退出
  • 等SPS完成回传后再启动程序:循环能正常执行CsvWriter并退出

结合你贴的代码,我认为问题大概率出在recv的行为处理和循环逻辑的漏洞上,具体分析如下:

代码里的几个潜在问题

  1. 未处理recv返回0的情况
    当SPS发送完数据后主动关闭连接时,recv会返回0(表示对方已经关闭连接,没有更多数据可读)。但你的代码里完全没处理这种情况,这会导致循环继续执行,下一次调用recv就会返回SOCKET_ERROR,但你的错误处理也没设置active为false,会陷入无效循环。

  2. 错误处理不完整
    当recv返回SOCKET_ERROR时,你只关闭了socket和清理WSACleanup,但没有设置active = false,也没有跳出循环,这会导致循环继续执行,后续的recv调用会持续出错,却无法退出。

  3. CsvWriter返回值的不确定性
    如果首次启动时,SPS回传的数据存在不完整(比如TCP拆包导致单次recv只拿到部分数据),或者格式不符合CsvWriter的预期,会导致CsvWriter返回非0值,此时active仍然为true,循环会继续执行下一次recv——但此时SPS已经发完所有数据,recv会进入阻塞状态,看起来就像"循环无法退出"。而等SPS发完所有数据再启动程序时,recv能一次性拿到完整数据,CsvWriter返回0,循环正常退出。

修改后的代码示例

针对这些问题,我调整了你的代码,补充了必要的处理逻辑:

bool active = true;
do {
    char buf[4096];
    ZeroMemory(buf, 4096);
    int bytesReceived = recv(sock, buf, 4096, 0);
    
    // 处理recv错误
    if (bytesReceived == SOCKET_ERROR) {
        int errCode = WSAGetLastError();
        // 建议添加日志打印,方便调试错误原因
        // printf("recv failed with error: %d\n", errCode);
        closesocket(sock);
        WSACleanup();
        active = false;
        break;
    }
    
    // 处理SPS关闭连接的情况
    if (bytesReceived == 0) {
        // 对方已关闭连接,没有更多数据
        closesocket(sock);
        WSACleanup();
        active = false;
        break;
    }
    
    if (bytesReceived > 0) {
        short int* ibuf = (short int*)buf;
        std::vector<int> InputData;
        for (int i = 0; i < bytesReceived / 2; ++i, ++ibuf) {
            short result = ntohs(*ibuf);
            InputData.push_back(result);
        }
        
        // 调试用:打印接收的数据量和转换后的条目数,确认是否完整
        // printf("Received %d bytes, converted to %d data items\n", bytesReceived, InputData.size());
        
        if (CsvWriter(InputData) == 0) {
            active = false;
            // 主动关闭连接,告诉SPS通信完成
            closesocket(sock);
            WSACleanup();
        }
    }
} while (active == true);

额外调试建议

  1. 添加日志/打印:在首次启动时,打印bytesReceived的值、InputData的大小和内容,确认是否和预期一致,同时检查CsvWriter的返回值,判断是不是数据不完整导致它返回非0。
  2. 检查SPS侧逻辑:确认首次启动时,SPS回传的数据格式、长度是否和后续启动时完全一致,有没有可能首次回传时少发了数据?
  3. 处理TCP粘包/拆包:如果SPS回传的数据长度是固定的,可以先读取数据长度,再循环读取对应字节数,确保接收完整数据后再调用CsvWriter。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 10:19:10