模组要过电信的入网认证必须要有电信自注册功能,分为短信自注册和数据自注册,一般送测的时候把自注册功能打开,本章讲一下数据自注册的要求和代码实现;
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号 |
SIM1ICCID | SIM卡ICCID号码 |
SIM1LTEIMSI | IMSI号码结构为:MCC+MNC+MSIN (国际移动客户识别 +国内移动客户识别+移动客户识别码 )MCC=移动国家号码,由3位数字组成,唯一地识别移动客户所属的国家。中国为460。MNC=移动网号,由2位数字组成,用于识别移动客户所归属的移动网。 |
SIM1CELLID | 当前注册的主小区ID |
REGDATE | 当前时间 示例格式:2022-12-05 14:50:48 |
2. 请求、响应数据包解析
请求数据包为HTTP POST方式,需要注意的是Content-Type项必须是 Content-Type: application/encrypted-json
响应数据包如上,响应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(®_info, 0x0, sizeof(reg_info));
memset(®_cfg, 0x0, sizeof(reg_cfg));
/* 获取自注册配置 */
autoreg_get_cfg(®_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(®_info);
while (AUTO_REG_RETRY_MAX > retryCount)
{
ret = auto_reg_start(®_cfg, ®_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