http://blog.csdn.net/axman/article/details/1671531
mms如果不支持smil那就不能叫真正的MMS,mixed的格式对于多个附件的MMS来说不仅没有"排版"功能,
而且即使用你想一个文件一个文件"拆单了看",也不得不看一次手工播放一次,就象要手动"翻页".所以不支持
smil干脆就别玩MMS了.
但基于related格式的支持smil的PDU打包方式怎么也搜索不到,很多厂商和技术人员都故作神秘,不就是一
堆规范的实现吗?只好下了几个文档来研究,经过反复调试,其中用nowSMS的mmscomp打包出来的格式竟然
是错误的,最后多次抓包比较,总算成功了.在『别人原来的mixed方式』的基础上修改成related方式的.有需要的
就自己用去吧.(注意基础类是别人原来提供的,我只是提供了related方式的打包逻辑)
工程文件在:http://dl2.csdn.net/down4/20070706/06201955542.rar
using
System;
using
System.Net;
using
System.IO;
using
System.Diagnostics;
using
System.Threading;
using
System.Collections;
using
System.Text;
///
<summary>
///
Lib 的摘要说明
///
</summary>
namespace
MMSLib
{
public
class
MMessage
{
string
subject
=
"
测试
"
;
int
deliverTime
=
0
;
//
多少秒以后开始投递
ArrayList inlineFiles
=
new
ArrayList();
//
文件列表
ArrayList destinations
=
new
ArrayList();
//
目标号码
static
long
serialNumber
=
19700311L
;
//
流水号
FileInfo smilFile;
public
void
SetSubject(
string
subject)
{
this
.subject
=
subject;
}
public
void
SetDeliverTime(
int
deliverTime)
{
this
.deliverTime
=
deliverTime;
}
//
public
void
AddTo(
string
dest)
{
destinations.Add(dest);
}
public
void
AddFile(
string
file)
{
if
(file.ToLower().EndsWith(
"
.smil
"
))
{
if
(
this
.smilFile
!=
null
)
throw
new
Exception(
"
The smil file has existed!
"
);
this
.smilFile
=
new
FileInfo(file);
}
inlineFiles.Add(file);
}
public
void
ClearTo()
{
destinations.Clear();
}
//
得到二进制编码字节
public
byte
[] MakeMMSContent()
{
if
(
this
.smilFile
==
null
)
throw
new
Exception(
"
The smil file not found!
"
);
byte
[] MMSContent
=
new
byte
[
0
];
//
X-Mms-Message-Type
MMSContent
=
appendContent(
new
byte
[] {
0x8C
,
0x80
}, MMSContent);
//
X-Mms-Transaction-ID
MMSContent
=
appendContent(
new
byte
[] {
0x98
}, MMSContent);
MMSContent
=
appendContent(serialNumber.ToString(), MMSContent);
serialNumber
++
;
MMSContent
=
appendContent(
new
byte
[] {
0x0
}, MMSContent);
//
X-Mms-MMS-Version
MMSContent
=
appendContent(
new
byte
[] {
0x8D
,
0x90
}, MMSContent);
//
Date
MMSContent
=
appendContent(
new
byte
[] {
0x85
}, MMSContent);
TimeSpan ts
=
DateTime.Now
-
new
DateTime(
1970
,
1
,
1
,
0
,
0
,
0
);
int
sec
=
(
int
)ts.TotalSeconds;
byte
[] bySec
=
BitConverter.GetBytes(sec);
MMSContent
=
appendContent(
new
byte
[] { (
byte
)bySec.Length }, MMSContent);
Array.Reverse(bySec);
MMSContent
=
appendContent(bySec, MMSContent);
if
(deliverTime
>
0
)
{
MMSContent
=
appendContent(
new
byte
[] {
0x87
}, MMSContent);
byte
[] bfTime
=
BitConverter.GetBytes(deliverTime);
Array.Reverse(bfTime);
byte
[] bfTimeLen
=
new
byte
[
3
];
bfTimeLen[
0
]
=
(
byte
)(bfTime.Length
+
2
);
bfTimeLen[
1
]
=
0x81
;
//
相对时间格式
bfTimeLen[
2
]
=
(
byte
)bfTime.Length;
MMSContent
=
appendContent(bfTimeLen, MMSContent);
MMSContent
=
appendContent(bfTime, MMSContent);
}
//
From,Len = 0x01,一个以0x81为标记的占位符,发送时自动插入发送号码.
MMSContent
=
appendContent(
new
byte
[] {
0x89
,
0x01
,
0x81
}, MMSContent);
//
To
for
(
int
i
=
0
; i
<
destinations.Count; i
++
)
{
MMSContent
=
appendContent(
new
byte
[] {
0x97
}, MMSContent);
MMSContent
=
appendContent(
"
+86
"
+
(
string
)destinations[i]
+
"
/TYPE=PLMN
"
, MMSContent);
//
MMSContent = appendContent(new byte[] { 0x20, 0x20, 0x0 }, MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0x0
}, MMSContent);
}
//
subject
if
(subject.Length
>
0
)
//
使用Utf8编码
{
MMSContent
=
appendContent(
new
byte
[] {
0x96
}, MMSContent);
byte
[] byLen
=
new
byte
[
1
];
byLen[
0
]
=
(
byte
)(Encoding.UTF8.GetByteCount(subject)
+
2
);
MMSContent
=
appendContent(byLen, MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0xEA
}, MMSContent);
MMSContent
=
appendContent(Encoding.UTF8.GetBytes(subject), MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0x0
}, MMSContent);
}
MMSContent
=
appendContent(
new
byte
[] {
0x84
}, MMSContent);
int
ctLen
=
2
//
0xB3 ,0x89
+
"
application/smil
"
.Length
+
3
//
0x00 0x8A 0x3c
+
smilFile.Name.Length
+
2
;
//
0x3c,0x00
byte
[] cl
=
uintToBytes(ctLen);
if
(cl[
0
]
>=
0x1F
)
MMSContent
=
appendContent(
new
byte
[] {
0x1F
}, MMSContent);
MMSContent
=
appendContent(cl, MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0xB3
}, MMSContent);
//
0xB3 Content-Type:application/vnd.wap.multipart.related
MMSContent
=
appendContent(
new
byte
[] {
0x89
}, MMSContent);
MMSContent
=
appendContent(Encoding.ASCII.GetBytes(
"
application/smil
"
), MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0x0
}, MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0x8A
,
0x3C
}, MMSContent);
//
0x8A:Start,0x3C:<
MMSContent
=
appendContent(Encoding.ASCII.GetBytes( smilFile.Name ), MMSContent);
MMSContent
=
appendContent(
new
byte
[] {
0x3E
,
0x0
}, MMSContent);
//
0x3E:>
byte
[] byFileCount
=
new
byte
[
1
];
byFileCount[
0
]
=
(
byte
)inlineFiles.Count;
MMSContent
=
appendContent(byFileCount, MMSContent);
int
chLen
=
"
application/smil
"
.Length
+
4
//
0x00 0xc0 0x22 0x3c: cid,",<
+
smilFile.Name.Length
+
3
//
0x3E,0x00,0x8E
+
smilFile.Name.Length
+
1
;
//
0x00
for
(
int
j
=
0
; j
<
inlineFiles.Count; j
++
)
{
MMSContent
=
appendContent(GetFileContent(inlineFiles[j].ToString()), MMSContent);
}
return
MMSContent;
}
//
打包文件
private
byte
[] GetFileContent(
string
FileName)
{
byte
[] byHeaders
=
new
byte
[
0
];
//
ContentType和Headers组合
byte
[] byData
=
readFile(FileName);
string
FileID
=
getContentId(FileName);
if
(FileName.EndsWith(
"
.txt
"
))
{
byHeaders
=
new
byte
[
1
];
byHeaders[
0
]
=
(
byte
)(Encoding.ASCII.GetByteCount(FileID)
+
5
);
byHeaders
=
appendContent(
new
byte
[] {
0x83
,
0x85
}, byHeaders);
//
Utf-8
byHeaders
=
appendContent(Encoding.ASCII.GetBytes(FileID), byHeaders);
byHeaders
=
appendContent(
new
byte
[] {
0x00
}, byHeaders);
byHeaders
=
appendContent(
new
byte
[] {
0x81
,
0xEA
}, byHeaders);
}
else
if
(FileName.EndsWith(
"
.gif
"
))
{
byHeaders
=
new
byte
[] {
0x9D
};
}
else
if
(FileName.EndsWith(
"
.mid
"
)
||
FileName.EndsWith(
"
.midi
"
))
{
byHeaders
=
Encoding.ASCII.GetBytes(
"
audio/midi
"
);
byHeaders
=
appendContent(
new
byte
[] {
0x00
}, byHeaders);
}
else
if
(FileName.EndsWith(
"
.smil
"
))
{
byHeaders
=
Encoding.ASCII.GetBytes(
"
application/smil
"
);
byHeaders
=
appendContent(
new
byte
[] {
0x00
}, byHeaders);
}
//
加入Content-ID
byHeaders
=
appendContent(
new
byte
[] {
0xC0
,
0x22
,
0x3C
}, byHeaders);
byHeaders
=
appendContent(Encoding.ASCII.GetBytes(FileID), byHeaders);
byHeaders
=
appendContent(
new
byte
[] {
0x3E
,
0x00
}, byHeaders);
//
加入Content-Location
byHeaders
=
appendContent(
new
byte
[] {
0x8E
}, byHeaders);
byHeaders
=
appendContent(Encoding.ASCII.GetBytes(FileID), byHeaders);
byHeaders
=
appendContent(
new
byte
[] {
0x00
}, byHeaders);
byte
[] byHeaderLen
=
uintToBytes(byHeaders.Length);
byte
[] byDataLen
=
uintToBytes(byData.Length);
byte
[] byMmc
=
new
byte
[byHeaderLen.Length
+
byDataLen.Length
+
byHeaders.Length
+
byData.Length];
Array.Copy(byHeaderLen, byMmc, byHeaderLen.Length);
Array.Copy(byDataLen,
0
, byMmc, byHeaderLen.Length, byDataLen.Length);
Array.Copy(byHeaders,
0
, byMmc, byHeaderLen.Length
+
byDataLen.Length, byHeaders.Length);
Array.Copy(byData,
0
, byMmc, byHeaderLen.Length
+
byDataLen.Length
+
byHeaders.Length, byData.Length);
return
byMmc;
}
private
byte
[] uintToBytes(
int
n)
{
byte
[] buf
=
new
byte
[
8
];
int
l
=
0
;
while
(n
>=
128
)
{
byte
b
=
(
byte
)(n
&
0x7F
);
n
=
n
>>
7
;
buf[l
++
]
=
b;
}
buf[l
++
]
=
(
byte
)n;
byte
[] retBys
=
new
byte
[l];
for
(
int
i
=
0
; i
<
l;
++
i)
{
retBys[i]
=
(
byte
)(buf[l
-
i
-
1
]
|
0x80
);
}
retBys[l
-
1
]
&=
0x7F
;
return
retBys;
}
//
读取文件
private
byte
[] readFile(
string
FileName)
{
if
(FileName.EndsWith(
"
.txt
"
)) {
StreamReader sr
=
null
;
try
{
sr
=
new
StreamReader(FileName, Encoding.Default);
string
text
=
sr.ReadToEnd();
byte
[] bf
=
Encoding.UTF8.GetBytes(text);
return
bf;
}
catch
{
return
new
byte
[
0
];
}
finally
{
if
(sr
!=
null
) sr.Close();
}
}
FileStream fs
=
null
;
try
{
fs
=
new
FileStream(FileName, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
//
没有设定Buffsize
byte
[] bf
=
new
byte
[fs.Length];
fs.Read(bf,
0
, (
int
)fs.Length);
return
bf;
}
catch
{
return
new
byte
[
0
];
}
finally
{
if
(fs
!=
null
) fs.Close();
}
}
private
string
getContentId(
string
FileName)
{
int
at
=
FileName.LastIndexOf(
"
/
"
);
if
(at
<
0
)
at
=
FileName.LastIndexOf(
"
/
"
);
return
FileName.Substring(at
+
1
);
}
private
byte
[] appendContent(
byte
[] srcBys,
byte
[] destBys)
{
Array.Resize(
ref
destBys, srcBys.Length
+
destBys.Length);
Array.Copy(srcBys,
0
,destBys,destBys.Length
-
srcBys.Length,srcBys.Length);
return
destBys;
}
private
byte
[] appendContent(
string
sz,
byte
[] byDest)
{
return
appendContent(Encoding.Default.GetBytes(sz), byDest);
}
}
///
<summary>
///
MMSender 的摘要说明。
///
///
</summary>
public
class
MMSender
{
//
设置参数
string
sMmscUrl
=
"
http://mmsc.monternet.com
"
;
string
sProxyUrl
=
"
10.0.0.172:80
"
;
public
MMSender()
{
//
//
TODO: 在此处添加构造函数逻辑
//
}
public
void
SetMMSC(
string
szUrl)
{
sMmscUrl
=
szUrl;
}
public
void
SetProxy(
string
szUrl)
{
sProxyUrl
=
szUrl;
}
/*
发送MMS的过程
1> 创建消息发送接口
MMSender ms = new MMSender();
2> 设置参数属性
默认属性已经是中国移动参数,因此如果是中国移动用户,以下两个操作可以不需要
ms.SetMMSC("
http://mmsc.monternet.com
");
ms.SetProxy("10.0.0.172:80");
3> 创建消息
MMessage mm= new MMessage();
4> 设置消息内容
mm.SetSubject("标题"); // 设置标题
mm.AddTo("13810034500"); // 添加接收号码,调用一次添加一个接收号码
mm.AddFile("FileName"); // 添加发送文件,包含文件路径,调用一次添加一个发送文件
5> 发送消息
string szReult =ms.Send(mm);
6> 继续发送其他号码
mm.ClearTo();
mm.AddTo("13812345678");
ms.Send(mm);
*/
/*
避免协议冲突的设置
<configuration>
<system.net>
<settings>
<httpWebRequest useUnsafeHeaderParsing="true"/>
</settings>
</system.net>
</configuration>
*/
public
string
Send(MMessage mm)
{
try
{
byte
[] byMM
=
mm.MakeMMSContent();
if
(byMM.Length
>
50
*
1024
)
return
"
The package is too large!
"
;
//
验证参数有效性
//
FileStream fs = new FileStream("d:/aaa.mms", FileMode.Create);
//
fs.Write(byMM, 0, byMM.Length);
//
fs.Close();
//
return "OK";
WebRequest wReq
=
WebRequest.Create(sMmscUrl);
HttpWebRequest hReq
=
(HttpWebRequest)wReq;
wReq.Headers.Clear();
if
(sProxyUrl.Length
>
0
)
wReq.Proxy
=
new
WebProxy(sProxyUrl);
wReq.ContentType
=
"
application/vnd.wap.mms-message
"
;
hReq.Accept
=
"
application/vnd.wap.mms-message,text/plain,*/*
"
;
wReq.Method
=
"
POST
"
;
hReq.KeepAlive
=
false
;
hReq.UserAgent
=
"
Nokia6681/2.0 (4.00.15) SymbianOS/8.0 Series60/2.6 Profile/MIDP-2.0 Configuration/CLDC-1.1
"
;
//
Write Post Dat
hReq.ContentLength
=
byMM.Length;
Stream sReq
=
wReq.GetRequestStream();
sReq.Write(byMM,
0
, byMM.Length);
sReq.Close();
WebResponse wRes
=
wReq.GetResponse();
HttpWebResponse hRes
=
(HttpWebResponse)wRes;
if
(hRes.StatusCode
==
HttpStatusCode.OK)
{
Stream sRes
=
wRes.GetResponseStream();
StreamReader sr
=
new
StreamReader(sRes);
string
szResult
=
sr.ReadToEnd();
//
发送结果
sr.Close();
return
szResult;
}
}
catch
(Exception e)
{
throw
new
Exception(e.Message);
}
return
string
.Empty;
}
}
}