【问题标题】:Read string buffer using sscanf使用 sscanf 读取字符串缓冲区
【发布时间】:2025-12-06 17:20:23
【问题描述】:

我有一个 TCP 服务器通过标准 TCP 套接字传输 JSON 格式的字符串。 消息包括:| JSON 对象的长度 | JSON 对象 |

在客户端上,我需要获取此消息并对其进行解析。 所以首先我使用 sscanf 提取长度,移动指针 sscanf'ed 字节,然后提取 JSON 消息。 目前我被困在 sscanf 部分,并在尝试从字符串缓冲区中提取整数值时出现“地址越界”错误。

为什么会这样?请帮忙。

static void * oom_collect_xcvr_data_thread(void *arg)
{
    int offset = 0;
    int recv_len, data_size;
    char *recv_buf = NULL;
    json_t *json_obj = NULL;
    json_error_t j_error;

    recv_buf = calloc(OOM_BUF_SIZE, 1);
    CASSERT(recv_buf != NULL);

    while(1)
    {
        if((recv_len = recv(sock_fd, recv_buf, OOM_BUF_SIZE, 0)) <= 0)
        {
            printf("server connect: tcp receive error %s", strerror(errno));
            free(recv_buf);
            CASSERT(0);
        }

        while(offset < recv_len)
        {
            offset += sscanf(recv_buf + offset, "%d \n", &data_size);
            if ((json_obj = json_loadb(recv_buf + offset, data_size, 0, &j_error)) == NULL)
            {
                    printf("line: %d, column: %d, position: %d, source: %s, Error: %s"
                            ,j_error.line, j_error.column, j_error.position, j_error.source, j_error.text); 
            }

            offset += data_size;
            copy_xcvr_info(json_obj);
        }
        CASSERT(offset == recv_len);
        offset = 0;
    }
}

我正在尝试解析的字符串数据:

1905年 { “静止的”:{ “RX_POWER_HIGH_ALARM”:2.5, “LENGTH_SMF”:10000, “更新时间戳”:251739.961, “编码”:6, “增强选项”:240, "LENGTH_SMF_KM":10000, “TX_POWER_LOW_ALARM”:-8.0, “连接器”:7, “DIAGNOSTIC_MONITORING_TYPE”:104, "TRANSCEIVER_EXT":0, "VENDOR_PN":"FTLX1471D3BCL", “RX_POWER_LOW_WARN”:-18.01, “波长”:1310, “选项”:“001a”, "LENGTH_OM4_OR_CU":0, “TEMP_HIGH_WARN”:73.0, “TEMP_LOW_ALARM”:-13.0, "BR_NOMINAL":10300, “VOLTAGE_LOW_ALARM”:2.9, “BIAS_HIGH_ALARM”:85.0, "LENGTH_62_5UM":0, “VOLTAGE_LOW_WARN”:3.0, "RATE_IDENTIFIER":0, “BIAS_LOW_ALARM”:15.0, "VENDOR_OUI":"009065", “BIAS_LOW_WARN”:20.0, "CABLE_SPEC":"0000", “TX_POWER_HIGH_WARN”:1.0, "EXT_IDENTIFIER":4, “更新计数”:1, "VENDOR_SN":"UK70M7N", “VOLTAGE_HIGH_ALARM”:3.7, “TX_POWER_HIGH_ALARM”:2.0, “标识符”:3, "LENGTH_OM3":0, "BR_MIN":10300, “TEMP_HIGH_ALARM”:78.0, “SFF_8472_COMPLIANCE”:3, “RX_POWER_LOW_ALARM”:-20.0, “VOLTAGE_HIGH_WARN”:3.6, “BIAS_HIGH_WARN”:80.0, "收发器":"2000000000000000", "LENGTH_50UM":0, “TX_POWER_LOW_WARN”:-7.0, "VENDOR_REV":"A", "DATE_CODE":"110212", “RX_POWER_HIGH_WARN”:2.0, "VENDOR_NAME":"FINISAR CORP.", “BR_MAX”:10300, “TEMP_LOW_WARN”:-8.0 }, "port_name":"port18", “端口类型”:“SFP”, “动态的”:{ “L_TX_POWER_WARN”:0, “更新时间戳”:253134.25, "L_TX_POWER_ALARM":0, “TX_POWER”:0.68, “L_RX_POWER_ALARM”:0, "DATA_READY_BAR_STATE":0, "RS_1_STATE":0, "TX_DISABLE_STATE":0, "L_TEMP_WARN":0, "L_VCC_ALARM":0, "L_ALARM_WARN":"000000000000", "SOFT_RATE_SELECT":0, "TX_FAULT_STATE":0, "L_TEMP_ALARM":0, “TX_POWER_DBM”:-1.68, “VCC”:3.41, “温度”:32.99, “TX_BIAS”:37.19, “状态控制”:0, "L_BIAS_ALARM":0, “RX_LOS_STATE”:0, "OPT_LASER_TEMP":0.0, "L_BIAS_WARN":0, “RX_POWER_DBM”:-1.28, "SOFT_TX_DISABLE_SELECT":0, “L_RX_POWER_WARN”:0, "OPT_TEC":0.0, “RX_POWER”:0.75, "L_VCC_WARN":0, “RATE_SELECT_STATE”:0 } }

gdb 错误信息:

Program received signal SIGSEGV, Segmentation fault.
rawmemchr () at ../sysdeps/i386/rawmemchr.S:70
70      ../sysdeps/i386/rawmemchr.S: No such file or directory.
(gdb) bt
#0  rawmemchr () at ../sysdeps/i386/rawmemchr.S:70
#1  0xf63de127 in _IO_str_init_static_internal (sf=0xee807838, ptr=0x969d046 <Address 0x969d046 out of bounds>, size=157929542, pstart=0x0) at strops.c:45
#2  0xf63d1c43 in _IO_vsscanf (string=0x969d046 <Address 0x969d046 out of bounds>, format=0x80d5fb7 "%d \n", args=0xee807908 "\260\202\200\356") at iovsscanf.c:44
#3  0xf63bf59b in __sscanf (s=0x969d046 <Address 0x969d046 out of bounds>, format=0x80d5fb7 "%d \n") at sscanf.c:34
#4  0x08091013 in oom_collect_xcvr_data_thread (arg=0x0) at /home/sfreeman/wspace/swapp/src/interface/agent/ia_l2.c:172
#5  0xf730f954 in start_thread (arg=0xee808b70) at pthread_create.c:304
#6  0xf644295e in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:130

【问题讨论】:

  • JSON 消息是什么字段?
  • JSON 对象是封装在 '{' '}' 中的所有数据,在 1905 之后就是 JSON 消息
  • 您假设 recv() 在一次读取中返回所有数据。它实际上可以(并且将)读取数据的一小部分。另外:返回 -1 的 read() 不是错误 在退出之前检查 errno。
  • 我建议始终使用json aware tool

标签: c sockets segmentation-fault scanf


【解决方案1】:

sscanf导致SIGSEGV当接收缓冲区已满且不以0结尾。您需要确保接收到的数据以0结尾,例如:

recv_len = recv(sock_fd, recv_buf, OOM_BUF_SIZE - 1, 0);
if(recv_len > 0) {
    recv_buf[recv_len] = 0;
}
else {
    // Handle disconnect or error.
}

我还发现了以下错误:

  • 代码无法处理部分消息。
  • sscanf 返回分配的项目数,而不是像代码预期的那样消耗的字节数。
  • 不检查sscanf 返回值是否有错误。
  • 不检查号码是否已全部收到。 sscanf 格式 " \n" 匹配 0 长度的字符串,这与代码的预期相反。

需要完整阅读 sscanf 手册页才能正确使用它。

【讨论】: