Loongson1B开发板+GSM模块彩信发送实现
一、文档简介:
本文档用于介绍1B开发板+GSM模块发送彩信的实现过程,以及彩信MMS协议封装过程。
开发环境参数:
操作系统:Ubuntu 11.04
本机平台:X86
目标平台:Loongson 1B 开发板+华为GSM模块MG323
交叉编译工具链:gcc-3.4.6-2f(1B交叉编译工具链)
所需软件及工具包:
1b-linux-3.0内核git版本号:115a0a040ffdaaba5f3afa58cb08625020d3fde8
PPP工具源码包:ppp-2.4.5.tar.gz
二、实现过程1.实现步骤:A. 开发板上PPP连接GPRS上网
B. 与移动网关建立TCP-Socket连接
C. 封装MMS-PDU数据包
D. 封装HTTP-POST数据包
E. 发送HTTP报文至移动网关
F. 接收网关回复信息
2.开发板上PPP连接GPRS上网首先配置kernel使它支持如下PPP选项:
Make menuconfig--->Device Drivers--->Network device support--->
PPP(point-to-point) support
PPP multilink support(EXPERIMENTAL)
PPP support for async serial prots
PPP support for sync tty ports
PPP Deflate compression
PPP BSD-Compress compression
接下来编译交叉编译ppp-2.4.5。
编译完成后将./pppd/pppd, ,/chat/chat, ./pppdump/pppdump, ./pppstats/pppstats四个文件拷贝到1B开发板文件系统的/usr/sbin目录下,并将他们的文件属性改为755。
检查/etc/host.conf脚本,确保这一行:order hosts,bind
在1B开发板文件系统/etc/ppp目录下建立如下四个PPP配置文件:
File1:/etc/ppp/peers/gprs
#/etc/ppp/peers/gprs
# Usage: root>pppd call gprs
/dev/ttyS0 #改成自己的GSM模块设备号
115200 #改成自己串口波特率
nocrtscts
#可能你的串口是需要crtscts,硬件流控的,这是由你的串口决定的,一般嵌入式系统的串口没有带硬件流控,也不需要就加nocrtscts
modem #这个参数使得pppd进程将等待模块发回的CD (Carrier Detect)信号,与local真好相反
#noauth
debug #把调试信息输出到/var/log/messages,在调试成功后去掉它,以减少垃圾的产生。
nodetach
#hide-password
usepeerdns #以下的3个参数一般不可少
noipdefault
defaultroute
user "cmnet" #设置接入的用户名,在chap-secrets或者pap-secets中使用
0.0.0.0:0.0.0.0 #本地和远端的ip都设为0使得接入的isp分配本地的ip地址
ipcp-accept-local #要求peer也就是isp给自己非配动态的IP地址
#ipcp-accept-remote
#lcp-echo-failure 12
#lcp-echo-interval 3
noccp #不需要压缩控制协议,有可能对端不需要,根据自己的isp的情况
#novj
#novjccomp
persist #保证在连接断开的情况下不退出,并尝试重新打开连接
connect '/usr/sbin/chat -s -v -f /etc/ppp/gprs-connect-chat'
#pppd调用chat会话进程接入对端isp,启动对端的pppd,然后本地pppd与对端的pppd一起进行协
#商网络参数和chap/pap认证,成功后,再进行ncp层的ip的分配。
File2:/etc/ppp/gprs-connect-chat
#/etc/ppp/gprs-connect-chat
#chat script for China Mobile, used wavecom module by lee.
TIMEOUT 15
ABORT '\nDELAYED\r'
ABORT '\nBUSY\r'
ABORT '\nERROR\r'
ABORT '\nNO DIALTONE\r'
ABORT '\nNO CARRIER\r'
'' \rAT
OK ATS0=0
OK ATE0V1
OK AT+CGDCONT=1,"IP","CMWAP"
OK ATDT*99***1#
CONNECT ''
File3:/etc/ppp/chap-secrets
#cat /etc/ppp/chap-secrets
#client server secret IP address
"cmnet" * "cmnet" *
File4:/etc/ppp/pap-secrets
#cat /etc/ppp/pap-secrets
# Secrets for authentication using PAP# client server secret IP addresses
######## redhat-config-network will overwrite this part!!! (begin) ##########
######## redhat-config-network will overwrite this part!!! (end) ############
"cmnet" * "cmnet" *
完成以上配置,然后在板上执行以下指令就能实现PPP连接GPRS了:
[root@Loongson-gz:/]#pppd call gprs &
[root@Loongson-gz:/]#ifconfig
ppp0 Link encap:Point-to-Point Protocol
inet addr:10.55.53.150 P-t-P:192.200.1.21 Mask:255.255.255.255
UP POINTOPOINT RUNNING NOARP MULTICAST MTU:1500 Metric:1
RX packets:4 errors:0 dropped:0 overruns:0 frame:0
TX packets:5 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:3
RX bytes:58 (58.0 B) TX bytes:98 (98.0 B)
[root@Loongson-gz:/]#ping 10.0.0.172
PING 10.0.0.172 (10.0.0.172): 56 data bytes
64 bytes from 10.0.0.172: seq=0 ttl=252 time=556.702 ms
64 bytes from 10.0.0.172: seq=1 ttl=252 time=293.645 ms
64 bytes from 10.0.0.172: seq=2 ttl=252 time=492.732 ms
64 bytes from 10.0.0.172: seq=3 ttl=252 time=290.228 ms
^C
--- 10.0.0.172 ping statistics ---
4 packets transmitted, 4 packets received, 0% packet loss
round-trip min/avg/max = 290.228/408.326/556.702 ms
看到以上打印信息,就说明成功使用PPP拨通GSM模块的GPRS连接上网络。
3.MMS封装及发送过程彩信和其它WAP应用的架构差不多,都要经过WAP Gateway中转,彩信在中国移动以及中国联通的GPRS接入点均一样,都是10.0.0.172的80端口或者9201端口。要注意的是彩信并非直接投递给接收方,而是像邮件一样,先发送给一个中间服务器MMS Proxy-Relay。MMS Proxy-Relay暂时保存彩信,然后通过push协议给彩信接收方发送一个通知,彩信接收方收到通知后从MMS Proxy-Relay上获取彩信内容。MMS Client和WAP Gateway之间用WAP传输协议传输,而WAP Gateway和MMS Proxy-Relay之间走传统的TCP/IP协议。
要实现发送彩信,作为客户端,我们需要完成的主要是与10.0.0.172建立TCP连接,将彩信数据包发送至WAP Gateway即可,其他部分工作由WAP Gateway与MMS Proxy-Relay自行完成。
将彩信数据包发送至WAP Gateway有两种实现方式,由于MMS是基于WAP协议的,移动的WAP代理服务器80/8080端口支持WAP2.0,可以采用HTTP方式传输数据;也可以通过WSP/WTP/WDP这一套传输协议传输数据,WAP代理服务器9201端口支持WSP/WTP/WDP协议。在这里我们选择的是HTTP方式。
以下是采用HTTP方式传输数据的数据包结构:
1
HTTP-HEADER
HTTP-DATA
2
MMS-HEADER
Message Body
接下来从MMS-PDU封装实现开始,主要参考MMS的编码协议文档《WAP-209-MMSEncapsulation-20010601-a.pdf》。
首先需要了解一下MMS PDU的结构,MMS PDU(Protocol Data Unit,协议数据单元)由MMS头和MMS消息体组成,MMS头由多个域名和域值组成,由客户端指定,MMS头里面的一些域可以被MMS Proxy-Replay修改或补充,MMS Proxy-Replay使用这些头域信息生成MM通知以及构造接收MM PDU中的相关头域,连同消息实体一同送往接收方。消息体跟在MMS头之后,大多数MMS PDU只含有MMS头,它们起到建立和维持通信的作用,只有在M-Send.req和M-Retrieve.conf PDU中才有消息体。MMS PDU和HTTP PDU极为类似,但要简单一些。一个MMS PDU对应一种消息格式。不同类型的MMS PDU有不同的MMS Header 。MMS Header根据由一系列的域组成,这些域定义了PDU的各种属性,包括PDU类型,版本号,接受方,发送方,主题,发送时间等。MMS Header中的域分为可选项和必选项,根据PDU的类型不同而不同。
常见的PDU的类型有:
发送请求: M-send.req
发送确认: M-send.conf
彩信通知: M-notification.ind
通知回应: M-notifyresp.ind
获取彩信回应: M-retrieve.conf
接收确认: M-acknowledge.ind
彩信回执: M-delivery.ind
而要实现发送彩信功能,我们需要完成一个M-send.req Message的封装。M-send.req Message由两部分组成,MMS Header后面接的是Message Body。根据MMS Message Body组装的是否有序(是否有位置控制信息,有显示先后顺序),M-send.req Message的组装方式分为:
application/vnd.wap.multipart.mixed方式,所有的消息内容混合在一起,没有时间上的顺序,内容怎么显示由客户端的显示控制策略来决定。
application/vnd.wap.multipart.related方式,各消息内容之间有一定关系,该关系可能是显示的时间上的先后,显示的位置等。这样在终端显示该消息的时候,就可以以幻灯片的方式显示一系列消息。
我们仅需要简单实现文本以及图片的发送,所以选择application/vnd.wap.multipart.mixed组装方式。
根据协议文档,M-send.req Message Header由以下内容组成:
Name
Content
Comments
Value
X-Mms-Message-Type
Message-type-value=m-send-req
Mandatory
0x0C
X-Mms-Transaction-ID
Transaction-id-value
Mandatory
0x18
X-Mms-MMS-Version
MMS-version-value
Mandatory
0x0D
Date
Date-value
Optional
0x05
From
From-value
Mandatory
0x09
To
To-value
Optional
0x17
Cc
Cc-value
Optional
0x02
Bcc
Bcc-value
Optional
0x01
Subject
Subject-value
Optional
0x16
X-Mms-Message-Class
Message-class-value
Optional
0x0A
X-Mms-Expiry
Expiry-value
Optional
0x08
X-Mms-Delivery-Time
Delivery-time-value
Optional
0x07
X-Mms-Priority
Priority-value
Optional
0x0F
X-Mms-Sender-Visibility
Sender-visibility-value
Optional
0x14
X-Mms-Delivery-Report
Delivery-report-value
Optional
0x06
X-Mms-Read-Reply
Read-reply-value
Optional
0x10
Content-Type
Content-type-value
Mandatory
0x04
在MMS-HEADER中,X-Mms-Message-Type ,X-Mms-Transaction-ID和X-Mms-MMS-Version必须位于MMS-HEADER的开始,并且按照前面所列的顺序。Content-Type必须在MMS-HEADER域的最后,其后为消息体,其它域的顺序可以随意安排。而为了使传输的数据更紧凑,MMS协议规定,对于Header Field Name的编码,数值的最高位(即bit7)置1。因此,上述编码值在实际的MMS PDU中需要加上0x80。
下面主要以一段MMS PDU数据为例进行分析各个域值:
8c 80 98 30 00 8d 90 89 01 81 97 2b 38 36 31 35 38 31 34 35 34 32 39 37 35 2f 54 59 50 45 3d 50 4c 4d 4e 00 96 74 65 73 74 00 84 a3 02 1c 0c 0a 83 85 31 2e 74 78 74 00 81 ea c0 22 3c 31 2e 74 78 74 3e 00 8e 31 2e 74 78 74 00 4d 4d 53 20 42 79 20 45 74 68 61 6e 12 df 22 9d c0 22 3c 31 2e 67 69 66 3e 00 8e 31 2e 67 69 66 00 ff d8 ... ff d9
8C:X-Mms-Message-Type
80:M-send.req
98:X-Mms-Transaction-ID
30 00:MMS Transaction ID为48
8D:X-Mms-Version
90:MMS Version值为1.0
89:From 发送方
01:后接一个字节数据
81:发送方号码占位符
97:To 接收方
2b 38 36 31 35 38 31 34 35 34 32 39 37 35 2f 54 59 50 45 3d 50 4c 4d 4e 00:接收方数据“+8615814542975/TYPE=PLMN ”
84:Content-Type
a3:表示:application/vnd.wap.multipart.mixed组装方式
02:表示后接两部分内容数据
接下来讲述一下内容数据部分的结构:
每一部分的内容数据均由两部分组成,分别是Header+Data;
首先包含一个HeaderLen,用来指示ContentType域和Header域的总长度,是一个Uintvar变量;
接下来时DataLen,用来指示后面Data域的长度,在这里指的是一块多媒体数据的字节数;
然后是ContentType域用来表示后面的数据块是什么类型的数据(如txt文本,jpeg图片,还是vidoe数据流);
再后边是Content Location和Content ID值,通常是多媒体数据的文件名,Content Location域以单引号"开始,后面一对<>里面包含文件名,Content ID域则直接就是一对<>里面包含文件名;
最后接Data域,它的长度由DataLen指定;
1c 0c 0a 83 85 31 2e 74 78 74 00 81 ea c0 22 3c 31 2e 74 78 74 3e 00 8e 31 2e 74 78 74 00 4d 4d 53 20 42 79 20 45 74 68 61 6e :第一段内容的Header+Data
1c:Header的长度为28字节
0c:Data的长度为12字节
0a:指明后边数据为txt文本类型
85 ~00:这段数据的指明文件名为1.txt
4d~6e:文本数据
12 df 22 9d c0 22 3c 31 2e 67 69 66 3e 00 8e 31 2e 67 69 66 00 ff d8 ... ff d9 第二段内容的Header+Data
12:Header的长度为18字节
df 22:Data的长度为1219字节
HeaderLen和DataLen均是采用Uintvar变量,特点是对超过7bit的数值进行拆分,低7位存放数据,高位补1表示有后续,最大32bit。
9d:指明后边数据为gif格式,但是在测试过程中,发现jpg格式也可以发送成功并显示。
22 ~00:这段数据的指明文件名为1.gif
ff~d9:图片数据,这是一张jpg图片
至此,便完成了整个MMS PDU的组装,在组装的过程中,需计算好整个MMS PDU的长度。
接下来是对MMS PDU进行HTTP封装,主要方法是为MMS PDU增加一个HTTP HEADER,以下是HTTP HEADER相关代码:
if(mms_main.net_choose == UNIWAP)
{
//联通彩信接入点
sprintf(http_request,, "POST http://mmsc.myuni.com.cn/ HTTP/1.1\r\n"
"Host:10.0.0.172:80\r\nUser-Agent: Ethan\r\n"
"Content-Type: application/vnd.wap.multipart.related\r\n"
"Accept: application/vnd.wap.multipart.related\r\n"
"Content-Length: %d\r\n\r\n", mms_pdu_len);
}
else if(mms_main.net_choose == CMWAP)
{
//移动彩信接入点
sprintf(http_request, "POST http://mmsc.monternet.com/ HTTP/1.1\r\n"
"Host:10.0.0.172:80\r\nUser-Agent: Ethan\r\n"
"Content-Type: application/vnd.wap.multipart.related\r\n"
"Accept: application/vnd.wap.multipart.related\r\n"
"Content-Length: %d\r\n\r\n", mms_pdu_len);
}
其中mms_pdu_len为之前MMS PDU的长度,若Content-Length错误,则网关回复HTTP 400错误(Content-Length<mms_pdu_len)或者HTTP 500错误(Content-Length>mms_pdu_len)。
然后将MMS PDU整个数据包置于HTTP HEADER之后,再发送出去即可。
发送成功,网关回复:
HTTP/1.1 200 OK
Content-Type: application/vnd.wap.mms-message
Content-Length: 32
Date: Thu, 07 Jun 2012 07:00:56 GMT
Server: MMSC
x-mmsc-code: -128
x-mmsc-from: 8613026872332
x-mmsc-tid: 0
x-mmsc-to: +86***********