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


    一、前言

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

    二、PDU格式封装流程

    在这里插入图片描述

    三、代码实现

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

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

    AT+CSCA?
    
    • 1

    在这里插入图片描述

    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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    (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;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    (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
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    四、问题解决

    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返回的是需要写进字符缓冲区的字符数量值,如果需要写进的字符数量大于字符缓冲区的大小,就会被截断。

  • 相关阅读:
    科学中的人工智能:量子、原子和连续体技术概述
    通过源码了解Java的自动装箱拆箱
    【miniconda+jupyter环境安装】
    容器技术 -- 简单了解 Kubernetes 的对象
    数字化新零售营销模式如何落地?数字化新零售营销功能推荐
    openGauss学习笔记-101 openGauss 数据库管理-管理数据库安全-客户端接入之用SSH隧道进行安全的TCP/IP连接
    React Swiper.js使用(详细版)3D聚焦特效,自定义导航按钮等
    磁盘空间不够引发的控制录像程序崩溃到后端的解决方式
    Java学习笔记(十八)
    【面试题】如何理解 前端设计模式-测策略模式?
  • 原文地址:https://blog.csdn.net/weixin_45880057/article/details/125986532