Чтение строкового буфера с использованием sscanf - PullRequest
0 голосов
/ 06 ноября 2018

У меня есть сервер TCP, передающий строки в формате JSON через стандартный сокет TCP. Сообщение состоит из: | длина объекта JSON | JSON объект |

На клиенте мне нужно взять это сообщение и разобрать его. Итак, сначала я извлекаю длину, используя sscanf, перемещаю указатели в байтах sscanf и извлекаю сообщение 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
{  
   "static":{  
      "RX_POWER_HIGH_ALARM":2.5,
      "LENGTH_SMF":10000,
      "update_timestamp":251739.961,
      "ENCODING":6,
      "ENHANCED_OPTIONS":240,
      "LENGTH_SMF_KM":10000,
      "TX_POWER_LOW_ALARM":-8.0,
      "CONNECTOR":7,
      "DIAGNOSTIC_MONITORING_TYPE":104,
      "TRANSCEIVER_EXT":0,
      "VENDOR_PN":"FTLX1471D3BCL",
      "RX_POWER_LOW_WARN":-18.01,
      "WAVELENGTH":1310,
      "OPTIONS":"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,
      "update_count":1,
      "VENDOR_SN":"UK70M7N",
      "VOLTAGE_HIGH_ALARM":3.7,
      "TX_POWER_HIGH_ALARM":2.0,
      "IDENTIFIER":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,
      "TRANSCEIVER":"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",
   "port_type":"SFP",
   "dynamic":{  
      "L_TX_POWER_WARN":0,
      "update_timestamp":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,
      "TEMPERATURE":32.99,
      "TX_BIAS":37.19,
      "STATUS_CONTROL":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

1 Ответ

0 голосов
/ 06 ноября 2018

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 format " \n" соответствует строке 0 длины, вопреки тому, что ожидает код.

Чтение sscanf справочной страницы в полном объеме необходимо для того, чтобы иметь возможность использовать ее правильно.

...