(四)Linux 4G模块实现短信PDU格式编码

本文详细介绍了PDU格式短信的封装流程,并提供了从UTF8到Unicode编码的转换及短信数据打包的C代码实现。通过处理短信中心号码、收件号码和发送数据,确保能够正确发送包含中文的短信。同时,讨论了在使用snprintf函数时的注意事项及其与sprintf的区别。
摘要由CSDN通过智能技术生成

一、前言

在上一篇:Linux 4G模块实现短信发送的两种格式(Text和PDU),了解了4G模块发送短信的两种格式,Text和PDU,Text简单但是不能发送中文,所以引进了PDU格式,但是PDU的转换格式挺麻烦的,特别是UTF8转Unicode编码。不过下面都会一一讲解。

二、PDU格式封装流程

在这里插入图片描述

三、代码实现

(1)获取并处理短信中心号码

可以向运营商要,每个地区的短信中心号码是不一样的,当然作为码农的我们肯定用更方便的方法获取,就是AT指令:

AT+CSCA?

在这里插入图片描述

int processing_center_number(char *center_buf)
{
    char    temp_buf[256] = {0};
    int     i = 0;
    int     temp =0;

    if (!center_buf)
    {
        printf("[%s]The argument invalid!\n", __func__);
        return -1;
    }

    strcpy(center_buf, &center_buf[1]);
    strcat(center_buf, "F");

    for (i = 0; i < strlen(center_buf); i += 2)
    {
        temp = center_buf[i];
        center_buf[i] = center_buf[i+1];
        center_buf[i+1] = temp;
    }

    snprintf(temp_buf, strlen(center_buf)+strlen("0891")+1, "0891%s", center_buf);
    memset(center_buf, 0 , sizeof(center_buf));
    strncpy(center_buf, temp_buf, strlen(temp_buf));

    printf("[%s]Processing center number succeeded:%s\n", __func__, center_buf);

    return 0;
}

(2)处理收件号码

int processing_phone_number(char *phone_buf)
{
    char    temp_buf[256] = {0};
    int     i = 0;
    int     temp =0;

    if (!phone_buf)
    {
        printf("[%s]The argument invalid!\n", __func__);
        return -1;
    }

    strcpy(phone_buf, &phone_buf[1]);
    strcat(phone_buf, "F");

    for (i = 0; i < strlen(phone_buf); i += 2)
    {
        temp = phone_buf[i];
        phone_buf[i] = phone_buf[i+1];
        phone_buf[i+1] = temp;
    }

    sprintf(temp_buf, "11000D91%s000800", phone_buf);
    memset(phone_buf, 0, sizeof(phone_buf));
    strncpy(phone_buf, temp_buf, strlen(temp_buf));

    printf("[%s]Phone_number processing succeeded:%s\n", __func__, phone_buf);

    return 0;
}

(3) 处理发送的数据

先将数据UTF8格式转成Unicode格式,再将字节流格式转成字符流格式,均以代码实现,UTF8格式转成Unicode格式也可以使用Linux下的iconv系列函数,但是在后面字节流转字符流的时候,需要判断数据的长度,strlen()函数遇到ASCII 的0值就停止计算了,所以为了实现准确计算字符流的长度,干脆自己写一个转换函数了。

int utf8_to_unicode(char *utf8_buf, char *unicode_buf, int *len)
{
    char    Byte1, Byte2, Byte3, Byte4;
    char    *ptr = unicode_buf;
    int     i = 0;
    *ptr    = 0;

    if (!utf8_buf)
    {
        printf("[%s]The argument invalid!\n", __func__);
        return -1;
    }
    
    while (*utf8_buf)
    {
        if ((*utf8_buf & 0x80) == 0x00)
        {
            ptr++;
            *ptr = *utf8_buf;
            ptr++;
            utf8_buf++;
            i +=2;
        }
        else if ((*utf8_buf & 0xE0) == 0xC0)
        {
            Byte1 = *utf8_buf;
            Byte2 = *(utf8_buf + 1);

            if ((Byte2 & 0xC0) != 0x80)
                return -2;
            
            *ptr = ((Byte1 >> 2) & 0x07);
            ptr++;
            *ptr = (Byte1 << 6) + (Byte2 & 0x3F);
            ptr++;
            utf8_buf +=2;
            i +=2;
        }
        else if ((*utf8_buf & 0xF0) == 0xE0)
        {
            Byte1 = *utf8_buf;
            Byte2 = *(utf8_buf + 1);
            Byte3 = *(utf8_buf + 2);

            *ptr = (Byte1 << 4) + ((Byte2 >> 2) & 0x0F);
            ptr++;
            *ptr = (Byte2 << 6) + (Byte3 & 0x3F);
            ptr++;
            utf8_buf += 3;
            i += 2;
        }
        else if ((*utf8_buf & 0xF8) == 0xF0)
        {
            Byte1 = *utf8_buf;
            Byte2 = *(utf8_buf + 1);
            Byte3 = *(utf8_buf + 2);
            Byte4 = *(utf8_buf + 3);

            *ptr = ((Byte1 & 0x07) << 2) + ((Byte2 >> 4) & 0x03);
            ptr++;
            *ptr = (Byte2 << 4) + ((Byte3 >> 2) & 0x0F);
            ptr++;
            *ptr = (Byte2 <<6) + (Byte4 & 0x3F);
            utf8_buf +=3;
            i += 4;
        }
    }

    *len = i;

    //printf("strlen(unicode_buf):%d\n", strlen(unicode_buf));

    return 0;
}


int byte_to_string(char *dest_buf, char *src_buf, int src_len)
{
    int i = 0;
    char *ptr = dest_buf;

    for (i; i< src_len; i++)
    {
        printf("src_buf[%d]:%02x\n", i, src_buf[i]);
        sprintf(ptr, "%02x",src_buf[i]);
        ptr += 2;
    }

    return 0;
}

(4) 对三部分数据进行打包

PDU == 中心号码部分+收件号码部分+数据部分

int pdu_packet(char *center_buf, char *phone_buf, char *sms_buf, char *pdu_buf, int *cmgs_length)
{
    char    temp[512] = {0};
    char    dest_buf[512] = {0};
    char    unicode_buf[512] = {0};
    int     unicode_len = 0;

    if (!center_buf || !phone_buf || !sms_buf)
    {
        printf("[%s]The argument invalid!\n", __func__);
        return -1;
    }

    processing_center_number(center_buf);
    printf("[%s]center_buf:%s\n", __func__, center_buf);

    printf("[%s]phone_buf:%s\n", __func__, phone_buf);
    processing_phone_number(phone_buf);
    printf("[%s]phone_buf:%s\n", __func__, phone_buf);


    printf("[%s]sms_buf:%s\n", __func__, sms_buf);
    utf8_to_unicode(sms_buf, unicode_buf, &unicode_len);

    byte_to_string(dest_buf, unicode_buf, unicode_len);
    printf("[%s]dest_buf:%s\n", __func__, dest_buf);

    sprintf(temp, "%s%02x%s", phone_buf, strlen(dest_buf)/2, dest_buf);

    strncat(pdu_buf, center_buf, strlen(center_buf));
    strncat(pdu_buf, temp, strlen(temp));
    *cmgs_length  = strlen(pdu_buf)/2;

    printf("[%s]cmgs_length:%d\n pdu_buf:%s\n", __func__, *cmgs_length, pdu_buf);

    return 0;
}
#endif

四、问题解决

1、utf8转unicode编码,UTF8格式转成Unicode格式也可以使用Linux下的iconv系列函数,但是在使用PDU格式发送的短信包括中文和英文的时候,每个英文字符的转码需要补一个0x00的16进制字节。在后面字符流转字节流的时候,需要判断数据的长度,strlen()函数遇到ASCII 的0值就停止计算了,所以为了实现准确计算字符流的长度,干脆自己写一个转换函数了。

2、在使用sprintf()的时候,觉得不太安全,所以用了snprintf(),但是我给的size是需要写进buffer里面的长度,然后就出现了问题,数据被截断了,后来才知道是snprintf会在字符串写进后,自动在后面加上一个\n ,以下是我的总结。
sprintf()和snprintf()的区别:
他们的作用都是将一个字符串写到对应的数组里面,但是snpirntf可用指定一个size值,避免字符串溢出存储区。
int sprintf(char *str, const char *format, …);
int snprintf(char *str, size_t size, const char *format, …);
在使用snprintf的时候:我的size的值是刚好要写进字符缓冲区str里面的字符串大小,结果实际写进str字符缓冲区里面的字符串少了一个,我直接蒙圈了,然后发现,size的大小必须比所传字符串的长度大1以上,小于字符数组的值,因为snprintf会自动在后面添加一个\n。不然就会出现字符串被截断的情况。而且sprintf和snprintf的返回值也是不同的,sprintf返回的是实际写进字符缓冲区的字符数量值,而snprintf返回的是需要写进字符缓冲区的字符数量值,如果需要写进的字符数量大于字符缓冲区的大小,就会被截断。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值