基于C语言的电信数据自注册代码实现

模组要过电信的入网认证必须要有电信自注册功能,分为短信自注册和数据自注册,一般送测的时候把自注册功能打开,本章讲一下数据自注册的要求和代码实现;

1. 自注册要求

1.模组自注册要求上报模组基础信息和sim卡信息,可以使用HTTP/COAP协议,我使用的是HTTP协议;
2.要求模组上电五分钟内上报,这就要求模组有自动拨号功能;
3.上报失败后自动重试 间隔时间为1小时,最大重试次数10次,都失败则本次开机不再发起注册;
4.上报的内容需要以json格式组装,然后进行base64编码,作为HTTP的承载发送;
5.注册成功后,保存本次注册的SIM卡ICCID,下次注册需要检测当前SIM卡ICCID是否和之前注册成功的相同,相同则不发起注册;
6.具体要求如下数据:

字段含义
REGVER自注册版本号 示例代码使用的是5.0
MEID终端的串号
MODEL厂商代码-产品型号 厂商代码不超过3位
SWVER软件版本号,应当与申请表的版本号一致
IMEI模组IMEI号
SIM1ICCIDSIM卡ICCID号码
SIM1LTEIMSIIMSI号码结构为:MCC+MNC+MSIN (国际移动客户识别 +国内移动客户识别+移动客户识别码 )MCC=移动国家号码,由3位数字组成,唯一地识别移动客户所属的国家。中国为460。MNC=移动网号,由2位数字组成,用于识别移动客户所归属的移动网。
SIM1CELLID当前注册的主小区ID
REGDATE当前时间 示例格式:2022-12-05 14:50:48

2. 请求、响应数据包解析

image-20221208142913186

请求数据包为HTTP POST方式,需要注意的是Content-Type项必须是 Content-Type: application/encrypted-json

image-20221208143043064

响应数据包如上,响应json内容resultDesc为Success 且 resultCode为0即为注册成功,否则都认为注册失败

3. 服务器地址和端口

服务器地址为 zzhc.vnet.cn 端口为 9999

4. 代码实现

本文总共三个文件
telecom_data_auto_reg.c 自注册接口实现文件
telecom_data_auto_reg.h 自注册接口头文件
telecom_data_auto_reg_mian.c 自注册信息获取并发起注册实现文件
功能实现需要依赖cJSON接口和base64编解码接口,需要这部分接口代码可以联系我,有什么问题也欢迎讨论

/*********************************************************************
 * 版权所有 (c) 2021-2022  axk. 保留所有版权
 * 文件名称: telecom_data_auto_reg_main.c
 * 文件描述: 电信自注册功能测试文件
 * 作    者: axk
 * 创建日期: 2022/11/30
 * 作    者                 日    期                 版    本          修改摘要
 * axk                   2022/11/30               1.0           文件创建
**********************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <string.h>

#include "axk_base_data_type.h"
#include "telecom_data_auto_reg.h"


/***********************************************************************
 * 函数名称: my_get_reg_version
 * 功能描述: 获取自注册版本号
 * 输    入:无
 * 输    出:reg_version  存储reg_version buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_reg_version(char* reg_version)
{
    strcpy(reg_version, "5.0");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_meid
 * 功能描述: 获取MEID
 * 输    入:无
 * 输    出:meid  存储meid buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_meid(char* meid)
{
    /* 当前平台获取不到MEID 可写空 */
    sprintf(meid, "%s", "");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_model
 * 功能描述: 获取产品型号
 * 输    入:无
 * 输    出:model  存储model buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_model(char* model)
{
    /* 厂商代码-产品型号 厂商代码不超过3位 */
    sprintf(model, "AXK-%s", "myversion");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_software_version
 * 功能描述: 获取软件版本号
 * 输    入:无
 * 输    出:sw_ver  存储sw_ver buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_software_version(char* sw_ver)
{
    sprintf(sw_ver, "%s", "MYVERSION");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_imei
 * 功能描述: 获取imei
 * 输    入:无
 * 输    出:imei  存储imei buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_imei(char* imei)
{
    sprintf(imei, "%s", "myimei");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_iccid
 * 功能描述: 获取iccid
 * 输    入:无
 * 输    出:iccid  存储iccid buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_iccid(char* iccid)
{
    sprintf(iccid, "%s", "myiccid");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_imsi
 * 功能描述: 获取imsi
 * 输    入:无
 * 输    出:imsi  存储imsi buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_imsi(char* imsi)
{
    sprintf(imsi, "%s", "myimsi");
    return 0;
}

/***********************************************************************
 * 函数名称: my_get_cellid
 * 功能描述: 获取小区id
 * 输    入:无
 * 输    出:cellid  存储小区ID buf
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int my_get_cellid(char* cellid)
{
    sprintf(cellid, "%s", "mycellid");
    return 0;
}

/***********************************************************************
 * 函数名称: autoreg_get_info
 * 功能描述: 获取自注册模组信息
 * 输    入:无
 * 输    出:reg_info  自注册模组信息
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/12/05
 * 作    者: axk
 ***********************************************************************/
int autoreg_get_info(auto_reg_info_s_type *reg_info)
{
    if (NULL == reg_info)
    {
        printf("Invalid parameter.\n");
        return -1;
    }

    my_get_reg_version(reg_info->reg_version);
    my_get_meid(reg_info->meid);
    my_get_model(reg_info->model);
    my_get_software_version(reg_info->swVer);
    my_get_imei(reg_info->imei);
    my_get_iccid(reg_info->iccid);
    my_get_imsi(reg_info->imsi);
    my_get_cellid(reg_info->cellid);

    return 0;
}

/***********************************************************************
 * 函数名称: autoreg_get_cfg
 * 功能描述: 获取自注册配置
 * 输    入:无
 * 输    出:reg_cfg  自注册配置信息
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/11/30
 * 作    者: axk
 ***********************************************************************/
int autoreg_get_cfg(auto_reg_cfg_s_type *reg_cfg)
{
    if (NULL == reg_cfg)
    {
        printf("Invalid parameter.\n");
        return -1;
    }

    /* 自己实现功能代码需要替换这块,从配置文件读取 */
    /* 特别是ICCID,每次注册成功后需要写入到文件 下次开机读取用于判断是否需要发起自注册 */
    reg_cfg->enable = 1;
    strcpy(reg_cfg->server_addr, AUTO_REG_SERVER_ADDR);
    reg_cfg->port = AUTO_REG_SERVER_PORT;
    reg_cfg->interval = AUTO_REG_INTERVAL;
    strcpy(reg_cfg->reg_version, ATUO_REG_VERSION);
    strcpy(reg_cfg->ue_iccid, "0");

    return 0;
}

int main(int argc,char *argv[])
{
    int ret = 0;
    int retryCount = 0;
    auto_reg_info_s_type reg_info;
    auto_reg_cfg_s_type reg_cfg;

    memset(&reg_info, 0x0, sizeof(reg_info));
    memset(&reg_cfg, 0x0, sizeof(reg_cfg));

    /* 获取自注册配置 */
    autoreg_get_cfg(&reg_cfg);
    printf("enable:%d. server_addr:%s, port:%d, interval:%d, reg_version:%s, ue_iccid:%s.\n", \
        reg_cfg.enable, reg_cfg.server_addr, reg_cfg.port, \
        reg_cfg.interval, reg_cfg.reg_version, reg_cfg.ue_iccid);

    /* 如果不需要自注册 直接return */
    if (0 == reg_cfg.enable)
    {
        printf("No need start autoreg\n");
        return 0;
    }

    /* 获取ICCID 如果ICCID和内部存储的ICCID相同,则不发起自注册 return */
    if (0 != strlen(reg_cfg.ue_iccid))
    {
        my_get_iccid(reg_info.iccid);

        if (0 != strcmp(reg_cfg.ue_iccid, reg_info.iccid))
        {
            strcpy(reg_info.ue_iccid, reg_cfg.ue_iccid);
        }
        else
        {
            printf("The current iccid same as UE iccid,No need start autoreg, UE iccid %s\n", reg_cfg.ue_iccid);
            return 0;
        }
    }

    autoreg_get_info(&reg_info);

    while (AUTO_REG_RETRY_MAX > retryCount)
    {
        ret = auto_reg_start(&reg_cfg, &reg_info);
        if (0 != ret)
        {
            printf("auto reg failed! ret is %d!\n", ret);
            sleep(AUTO_REG_INTERVAL);
            continue;
        }

        printf("atuoreg success! ue iccid is %s\n", reg_cfg.ue_iccid);
        /* 注册成功后需要把ICCID写入到板子flash存储,下次开机或者发起注册时用于比较判断是否发起注册 */
        break;
    }

    return 0;
}

/*********************************************************************
 * 版权所有 (c) 2021-2022  axk. 保留所有版权
 * 文件名称: telecom_data_auto_reg.c
 * 文件描述: 电信自注册功能接口实现文件
 * 作    者: axk
 * 创建日期: 2022/12/01
 * 作    者                 日    期                 版    本          修改摘要
 * axk                   2022/12/01               1.0           文件创建
**********************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include "cJSON.h"
#include "axk_base_data_type.h"
#include "telecom_data_auto_reg.h"

/***********************************************************************
 * 函数名称: auto_reg_encode_package
 * 功能描述: 根据模组自注册信息编码注册请求数据包
 * 输    入:reg_info   自注册模组信息
 * 输    出:reg_package  编码完成的自注册HTTP数据包承载内容
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/12/01
 * 作    者: axk
 ***********************************************************************/
int auto_reg_encode_package(auto_reg_info_s_type* reg_info, char* reg_package)
{
    cJSON* root = NULL;
    char* json_data = NULL;
    time_t ts = 0;
    struct tm *tm = NULL;
    char timeBuf[128] = {0};

    if (NULL == reg_info || NULL == reg_package)
    {
        AXK_LOGE("para error!");
        return -1;
    }

    root = cJSON_CreateObject();
    if (NULL == root)
    {
        AXK_LOGE("cJSON_CreateObject failed.");
        return -1;
    }

    ts = time(0);
    tm = localtime(&ts);
    sprintf(timeBuf, "%d-%02d-%02d %02d:%02d:%02d", \
            (tm->tm_year+1900), ((tm->tm_mon+1)%13), (tm->tm_mday%32), \
            (tm->tm_hour%24), (tm->tm_min%60), (tm->tm_sec%60));

    cJSON_AddItemToObject(root, "REGVER", cJSON_CreateString(reg_info->reg_version));
    cJSON_AddItemToObject(root, "MEID", cJSON_CreateString(reg_info->meid));
    cJSON_AddItemToObject(root, "MODEL", cJSON_CreateString(reg_info->model));
    cJSON_AddItemToObject(root, "SWVER", cJSON_CreateString(reg_info->swVer));
    cJSON_AddItemToObject(root, "IMEI1", cJSON_CreateString(reg_info->imei));
    cJSON_AddItemToObject(root, "SIM1CDMAIMSI", cJSON_CreateString(""));
    cJSON_AddItemToObject(root, "SIM1ICCID", cJSON_CreateString(reg_info->iccid));
    cJSON_AddItemToObject(root, "SIM1LTEIMSI", cJSON_CreateString(reg_info->imsi));
    cJSON_AddItemToObject(root, "SIM1CELLID", cJSON_CreateString(reg_info->cellid));
    cJSON_AddItemToObject(root, "REGDATE", cJSON_CreateString(timeBuf));

    json_data = cJSON_Print(root);
    AXK_LOGD("%s\n", json_data);

    base64_encode_api(json_data, reg_package, (int)strlen(json_data));

    cJSON_Delete(root);
    root = NULL;
    free(json_data);
    json_data = NULL;

    AXK_LOGD("%s\n", reg_package);
    return 0;
}

/***********************************************************************
 * 函数名称: auto_reg_http_process
 * 功能描述: 根据模组自注册配置和组好的数据包内容发起HTTP请求并解析
 * 输    入:reg_cfg   自注册配置
           content  组好的注册数据包
 * 输    出:
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/12/01
 * 作    者: axk
 ***********************************************************************/
int auto_reg_http_process(auto_reg_cfg_s_type* reg_cfg, const char *content)
{
    char http_req_buf[1024] = {0};
    char http_rsp_buf[512] = {0};
    struct sockaddr_in addr_serv = {0};
    int socket_flags= 0;
    fd_set wset = {0};
    struct timeval tval = {0};
    char ipaddr[128] = {0};
    int ret = 0;
    int sock_fd;
    struct hostent* host;
    char* rsp_data = NULL;

    /* 组装HTTP请求报文 */
    sprintf(http_req_buf, \
                "POST / HTTP/1.1\r\n"
                "Host: %s:%d\r\n"
                "Accept: */*\r\n"
                "Content-Type: application/encrypted-json\r\n"
                "Content-Length: %d\r\n\r\n"
                "%s", reg_cfg->server_addr, reg_cfg->port, \
                strlen(content), content);

    /* 发送报文 */
    sock_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sock_fd)
    {
        AXK_LOGE("socket_init error\n");
        return -1;
    }

    host = gethostbyname(reg_cfg->server_addr);
    if (NULL == host)
    {
        AXK_LOGE("dns prase error\n");
        close(sock_fd);
        return -1;
    }

    /* 获取IP地址 */
    strncpy(ipaddr, inet_ntoa(*(struct in_addr*)host->h_addr), 16);

    socket_flags = fcntl(sock_fd, F_GETFL, 0);
    fcntl(sock_fd, F_SETFL, socket_flags | O_NONBLOCK);
    memset(&addr_serv, 0, sizeof(addr_serv));
    addr_serv.sin_family = AF_INET;
    addr_serv.sin_port = htons(reg_cfg->port);
    addr_serv.sin_addr.s_addr = inet_addr(ipaddr);

    AXK_LOGD("ip is %s prot is %d\n", ipaddr, reg_cfg->port);

    connect(sock_fd, (struct sockaddr*)(&addr_serv), sizeof(addr_serv));
    FD_ZERO(&wset);
    FD_SET(sock_fd, &wset);
    tval.tv_sec = 5;
    tval.tv_usec = 0;

    if (0 >= select(sock_fd + 1, NULL, &wset, NULL, &tval))
    {
        AXK_LOGE("select error, errno:%d\n", errno);
        close(sock_fd);
        return -1;
    }
    fcntl(sock_fd, F_SETFL, socket_flags);  /* restore file status flags */

    /* 发送HTTP请求 */
    ret = send(sock_fd, http_req_buf, strlen(http_req_buf), 0);
    if (0 > ret)
    {
        AXK_LOGE("send_data error\n");
        close(sock_fd);
        return -1;
    }

    /* 阻塞接收响应 */
    ret = recv(sock_fd, http_rsp_buf, sizeof(http_rsp_buf), 0);
    if (0 > ret)
    {
        AXK_LOGE("receive_data error\n");
        close(sock_fd);
        return -1;
    }

    close(sock_fd);

    AXK_LOGD("rsp data: %s\n", http_rsp_buf);
    rsp_data = strstr(http_rsp_buf, "\r\n\r\n");
    if (NULL == rsp_data)
    {
        AXK_LOGE("autoreg failed!\n");
        return -1;
    }
    rsp_data += strlen("\r\n\r\n");

    if (NULL == strstr(rsp_data, "Success"))
    {
        AXK_LOGE("autoreg failed!\n");
        return -1;
    }

    AXK_LOGD("autoreg success!\n");

    return 0;
}

/***********************************************************************
 * 函数名称: auto_reg_start
 * 功能描述: 根据配置信息和模组信息发起电信自注册
 * 输    入:reg_cfg   自注册配置
           reg_info  自注册模组信息
 * 输    出:
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/12/01
 * 作    者: axk
 ***********************************************************************/
int auto_reg_start(auto_reg_cfg_s_type* reg_cfg, auto_reg_info_s_type* reg_info)
{
    char reg_package[600] = {0};
    int ret = 0;

    /* 对获取到的数据进行编码 */
    ret = auto_reg_encode_package(reg_info, reg_package);
    if (0 != ret)
    {
        AXK_LOGE("auto_reg_encode_package failed!");
        return -1;
    }

    /* 编码HTTP数据,编码发送并解析响应 */
    ret = auto_reg_http_process(reg_cfg, reg_package);
    if (0 != ret)
    {
        AXK_LOGE("auto_reg_http_process failed!");
        return -1;
    }

    return 0;
}
/*********************************************************************
 * 版权所有 (c) 2021-2022  axk. 保留所有版权
 * 文件名称: telecom_data_auto_reg.h
 * 文件描述: 电信自注册功能接口头文件
 * 作    者: axk
 * 创建日期: 2022/11/30
 * 作    者                 日    期                 版    本          修改摘要
 * axk                   2022/11/30               1.0           文件创建
**********************************************************************/

#ifndef __TELECOM_DATA_AUTO_REG_H_
#define __TELECOM_DATA_AUTO_REG_H_

#ifdef __cplusplus
extern "C" {
#endif

/****************************TELECOM_DATA_AUTO_REG 宏定义 start****************************************************/
/* 默认电信自注册服务地址 */
#define AUTO_REG_SERVER_ADDR           "zzhc.vnet.cn"

/* 默认电信自注册服务端口 */
#define AUTO_REG_SERVER_PORT           (9999)

/* 默认电信自注册失败重试间隔 */
#define AUTO_REG_INTERVAL              (3600)

/* 发起自注册 最大重试次数 */
#define AUTO_REG_RETRY_MAX             (10)

/* 默认电信自注册版本号 */
#define ATUO_REG_VERSION               "5.0"

/****************************TELECOM_DATA_AUTO_REG 宏定义 end****************************************************/


/****************************TELECOM_DATA_AUTO_REG 枚举定义 start****************************************************/
/****************************TELECOM_DATA_AUTO_REG 枚举定义 end****************************************************/


/****************************TELECOM_DATA_AUTO_REG 结构体定义 start****************************************************/

/* 自注册配置信息结构体 */
typedef struct
{
    /* 自注册开关 0-关闭 1-开启 */
    int enable;

    /* 服务器地址 */
    char server_addr[128];

    /* 服务器端口 */
    int port;

    /* 重试间隔时间 单位为秒 */
    int interval;

    /* 自注册版本号 */
    char reg_version[16];

    /* 之前注册成功的iccid */
    char ue_iccid[128];
} auto_reg_cfg_s_type;


/* 自注册模组信息结构体 */
typedef struct
{
    /* 自注册版本号 */
    char reg_version[32];

    /* 模组MEID,没有可为空 */
    char meid[32];

    /* 模组型号 */
    char model[20];

    /* 软件版本号 */
    char swVer[128];

    /* IMEI号 */
    char imei[32];

    /* 当前sim卡iccid */
    char iccid[32];

    /* imsi号 */
    char imsi[32];

    /* 小区id */
    char cellid[32];

    /* 上次注册成功的iccid */
    char ue_iccid[32];
} auto_reg_info_s_type;
/****************************TELECOM_DATA_AUTO_REG 结构体定义 end****************************************************/

/****************************TELECOM_DATA_AUTO_REG 函数声明 start****************************************************/
/***********************************************************************
 * 函数名称: auto_reg_start
 * 功能描述: 根据配置信息和模组信息发起电信自注册
 * 输    入:reg_cfg   自注册配置
           reg_info  自注册模组信息
 * 输    出:
 * 返 回 值: 成功返回0 失败返回对应错误码
 * 其    他:
 * 日    期: 2022/12/01
 * 作    者: axk
 ***********************************************************************/
int auto_reg_start(auto_reg_cfg_s_type* reg_cfg, auto_reg_info_s_type* reg_info);

/****************************TELECOM_DATA_AUTO_REG 函数声明 end****************************************************/
#ifdef __cplusplus
}
#endif
#endif
  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论
中国电信移动终端需求白皮书-短信自注册功能分册 ................................ 1 1 范围....................................................................... 1 2 规范性引用文件 ............................................................. 1 3 缩略语、术语和定义 ......................................................... 1 3.1 缩略语................................................................. 1 3.2 术语和定义............................................................. 1 4 要求等级................................................................... 1 5 CDMA 短信自注册功能 ........................................................ 1 AUTOReg-50001 [必选] 单卡终端短信自注册流程要求 ...................... 1 AUTOReg-50002 [必选] 双卡终端短信自注册流程要求 ...................... 2 AUTOReg-50003 [必选] 终端短信自注册消息格式及内容要求 ................ 3 AUTOReg-50004 [必选] 终端短信自注册业务流程要求 ...................... 5 6 IMS 短信自注册功能 ......................................................... 7 AUTOReg-60001 [必选] 单卡终端 IMS 短信自注册流程要求 .................. 7 AUTOReg-60002 [必选] 双卡终端 IMS 短信自注册流程要求 .................. 8 AUTOReg-60003 [必选] 终端 IMS 短信自注册消息格式及内容要求 ............ 8 AUTOReg-50004 [必选] 终端 IMS 短信自注册业务流程要求 ................. 10

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

axk0909

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值