一、前言
在上一篇: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, ¢er_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返回的是需要写进字符缓冲区的字符数量值,如果需要写进的字符数量大于字符缓冲区的大小,就会被截断。