SMS短信PDU编码
目前,發(fā)送短消息常用Text和PDU(Protocol Data Unit,協(xié)議數(shù)據(jù)單元)模式。使用Text模式收發(fā)短信代碼簡單,實(shí)現(xiàn)起來十分容易,但最大的缺點(diǎn)是不能收發(fā)中文短信;而PDU模式不僅支持中文短信,也能發(fā)送英文短信。PDU模式收發(fā)短信可以使用3種編碼:7-bit、8-bit和UCS2編碼。7-bit編碼用于發(fā)送普通的ASCII字符,8-bit編碼通常用于發(fā)送數(shù)據(jù)消息,UCS2編碼用于發(fā)送Unicode字符。一般的PDU編碼由A B C D E F G H I J K L M十三項(xiàng)組成。
A:短信息中心地址長度,2位十六進(jìn)制數(shù)(1字節(jié))。
B:短信息中心號碼類型,2位十六進(jìn)制數(shù)。
C:短信息中心號碼,B+C的長度將由A中的數(shù)據(jù)決定。
D:文件頭字節(jié),2位十六進(jìn)制數(shù)。
E:信息類型,2位十六進(jìn)制數(shù)。
F:被叫號碼長度,2位十六進(jìn)制數(shù)。
G:被叫號碼類型,2位十六進(jìn)制數(shù),取值同B。
H:被叫號碼,長度由F中的數(shù)據(jù)決定。
I:協(xié)議標(biāo)識,2位十六進(jìn)制數(shù)。
J:數(shù)據(jù)編碼方案,2位十六進(jìn)制數(shù)。
K:有效期,2位十六進(jìn)制數(shù)。
L:用戶數(shù)據(jù)長度,2位十六進(jìn)制數(shù)。
M:用戶數(shù)據(jù),其長度由L中的數(shù)據(jù)決定。J中設(shè)定采用UCS2編碼,這里是中英文的Unicode字符。
PDU編碼協(xié)議簡單說明
例1 發(fā)送:SMSC號碼是+8613800250500,對方號碼是13693092030,消息內(nèi)容是“Hello!”。從手機(jī)發(fā)出的PDU串可以是
08 91 68 31 08 20 05 05 F0 11 00 0D 91 68 31 96 03 29 30 F0 00 00 00 06 C8 32 9B FD 0E 01
對照規(guī)范,具體分析:
分段 含義 說明
08 SMSC地址信息的長度 共8個(gè)八位字節(jié)(包括91) 91 SMSC地址格式(TON/NPI) 用國際格式號碼(在前面加‘+’) 68 31 08 20 05 05 F0 SMSC地址 8613800250500,補(bǔ)‘F’湊成偶數(shù)個(gè) 11 基本參數(shù)(TP-MTI/VFP) 發(fā)送,TP-VP用相對格式 00 消息基準(zhǔn)值(TP-MR) 0 0D 目標(biāo)地址數(shù)字個(gè)數(shù) 共13個(gè)十進(jìn)制數(shù)(不包括91和‘F’) 91 目標(biāo)地址格式(TON/NPI) 用國際格式號碼(在前面加‘+’) 68 31 96 03 29 30 F0 目標(biāo)地址(TP-DA) 8613693092030,補(bǔ)‘F’湊成偶數(shù)個(gè) 00 協(xié)議標(biāo)識(TP-PID) 是普通GSM類型,點(diǎn)到點(diǎn)方式 00 用戶信息編碼方式(TP-DCS) 7-bit編碼 00 有效期(TP-VP) 5分鐘 06 用戶信息長度(TP-UDL) 實(shí)際長度6個(gè)字節(jié) C8 32 9B FD 0E 01 用戶信息(TP-UD) “Hello!”
例2 接收:SMSC號碼是+8613800250500,對方號碼是13693092030,消息內(nèi)容是“你好!”。手機(jī)接收到的PDU串可以是
08 91 68 31 08 20 05 05 F0 84 0D 91 68 31 96 03 29 30 F0 00 08 30 30 21 80 63 54 80 06 4F 60 59 7D 00 21
對照規(guī)范,具體分析:
分段 含義 說明
08 地址信息的長度 個(gè)八位字節(jié)(包括91) 91 SMSC地址格式(TON/NPI) 用國際格式號碼(在前面加‘+’) 68 31 08 20 05 05 F0 SMSC地址 8613800250500,補(bǔ)‘F’湊成偶數(shù)個(gè) 84 基本參數(shù)(TP-MTI/MMS/RP) 接收,無更多消息,有回復(fù)地址 0D 回復(fù)地址數(shù)字個(gè)數(shù) 共13個(gè)十進(jìn)制數(shù)(不包括91和‘F’) 91 回復(fù)地址格式(TON/NPI) 用國際格式號碼(在前面加‘+’) 68 31 96 03 29 30 F0 回復(fù)地址(TP-RA) 8613693092030,補(bǔ)‘F’湊成偶數(shù)個(gè) 00 協(xié)議標(biāo)識(TP-PID) 是普通GSM類型,點(diǎn)到點(diǎn)方式 08 用戶信息編碼方式(TP-DCS) UCS2編碼 30 30 21 80 63 54 80 時(shí)間戳(TP-SCTS) 2003-3-12 08:36:45 +8時(shí)區(qū) 06 用戶信息長度(TP-UDL) 實(shí)際長度6個(gè)字節(jié) 4F 60 59 7D 00 21 用戶信息(TP-UD) “你好!”
若基本參數(shù)的最高位(TP-RP)為0,則沒有回復(fù)地址的三個(gè)段。從Internet上發(fā)出的短消息常常是這種情形。
注意號碼和時(shí)間的表示方法,不是按正常順序順著來的,而且要以‘F’將奇數(shù)補(bǔ)成偶數(shù)。
在PDU Mode中,可以采用三種編碼方式來對發(fā)送的內(nèi)容進(jìn)行編碼,它們是7-bit、8-bit和UCS2編碼。7-bit編碼用于發(fā)送普通的ASCII字符,它將一串7-bit的字符(最高位為0)編碼成8-bit的數(shù)據(jù),每8個(gè)字符可“壓縮”成7個(gè);8-bit編碼通常用于發(fā)送數(shù)據(jù)消息,比如圖片和鈴聲等;而UCS2編碼用于發(fā)送Unicode字符。PDU串的用戶信息(TP-UD)段最大容量是140字節(jié),所以在這三種編碼方式下,可以發(fā)送的短消息的最大字符數(shù)分別是160、140和70。這里,將一個(gè)英文字母、一個(gè)漢字和一個(gè)數(shù)據(jù)字節(jié)都視為一個(gè)字符。
需要注意的是,PDU串的用戶信息長度(TP-UDL),在各種編碼方式下意義有所不同。7-bit編碼時(shí),指原始短消息的字符個(gè)數(shù),而不是編碼后的字節(jié)數(shù)。8-bit編碼時(shí),就是字節(jié)數(shù)。UCS2編碼時(shí),也是字節(jié)數(shù),等于原始短消息的字符數(shù)的兩倍。如果用戶信息(TP-UD)中存在一個(gè)頭(基本參數(shù)的TP-UDHI為1),在所有編碼方式下,用戶信息長度(TP-UDL)都等于頭長度與編碼后字節(jié)數(shù)之和。如果采用GSM 03.42所建議的壓縮算法(TP-DCS的高3位為001),則該長度也是壓縮編碼后字節(jié)數(shù)或頭長度與壓縮編碼后字節(jié)數(shù)之和。
參見詳細(xì)英文說明:http://www.dreamfabric.com/sms/
將源串每8個(gè)字符分為一組(這個(gè)例子中不滿8個(gè))進(jìn)行編碼,在組內(nèi)字符間壓縮,但每組之間是沒有什么聯(lián)系的。
用C實(shí)現(xiàn)7-bit編碼和解碼的算法如下:
// 7-bit編碼
// pSrc: 源字符串指針
// pDst: 目標(biāo)編碼串指針
// nSrcLength: 源字符串長度
// 返回: 目標(biāo)編碼串長度
int gsmEncode7bit(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
int nSrc; // 源字符串的計(jì)數(shù)值
int nDst; // 目標(biāo)編碼串的計(jì)數(shù)值
int nChar; // 當(dāng)前正在處理的組內(nèi)字符字節(jié)的序號,范圍是0-7
unsigned char nLeft; // 上一字節(jié)殘余的數(shù)據(jù)
// 計(jì)數(shù)值初始化
nSrc = 0;
nDst = 0;
// 將源串每8個(gè)字節(jié)分為一組,壓縮成7個(gè)字節(jié)
// 循環(huán)該處理過程,直至源串被處理完
// 如果分組不到8字節(jié),也能正確處理
while(nSrc<nSrcLength)
{
// 取源字符串的計(jì)數(shù)值的最低3位
nChar = nSrc & 7;
// 處理源串的每個(gè)字節(jié)
if(nChar == 0)
{
// 組內(nèi)第一個(gè)字節(jié),只是保存起來,待處理下一個(gè)字節(jié)時(shí)使用
nLeft = *pSrc;
}
else
{
// 組內(nèi)其它字節(jié),將其右邊部分與殘余數(shù)據(jù)相加,得到一個(gè)目標(biāo)編碼字節(jié)
*pDst = (*pSrc << (8-nChar)) | nLeft;
// 將該字節(jié)剩下的左邊部分,作為殘余數(shù)據(jù)保存起來
nLeft = *pSrc >> nChar;
// 修改目標(biāo)串的指針和計(jì)數(shù)值 pDst++;
nDst++;
}
// 修改源串的指針和計(jì)數(shù)值
pSrc++; nSrc++;
}
// 返回目標(biāo)串長度
return nDst;
}
// 7-bit解碼
// pSrc: 源編碼串指針
// pDst: 目標(biāo)字符串指針
// nSrcLength: 源編碼串長度
// 返回: 目標(biāo)字符串長度
int gsmDecode7bit(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
int nSrc; // 源字符串的計(jì)數(shù)值
int nDst; // 目標(biāo)解碼串的計(jì)數(shù)值
int nByte; // 當(dāng)前正在處理的組內(nèi)字節(jié)的序號,范圍是0-6
unsigned char nLeft; // 上一字節(jié)殘余的數(shù)據(jù)
// 計(jì)數(shù)值初始化
nSrc = 0;
nDst = 0;
// 組內(nèi)字節(jié)序號和殘余數(shù)據(jù)初始化
nByte = 0;
nLeft = 0;
// 將源數(shù)據(jù)每7個(gè)字節(jié)分為一組,解壓縮成8個(gè)字節(jié)
// 循環(huán)該處理過程,直至源數(shù)據(jù)被處理完
// 如果分組不到7字節(jié),也能正確處理
while(nSrc<nSrcLength)
{
// 將源字節(jié)右邊部分與殘余數(shù)據(jù)相加,去掉最高位,得到一個(gè)目標(biāo)解碼字節(jié)
*pDst = ((*pSrc << nByte) | nLeft) & 0x7f;
// 將該字節(jié)剩下的左邊部分,作為殘余數(shù)據(jù)保存起來
nLeft = *pSrc >> (7-nByte);
// 修改目標(biāo)串的指針和計(jì)數(shù)值
pDst++;
nDst++;
// 修改字節(jié)計(jì)數(shù)值
nByte++;
// 到了一組的最后一個(gè)字節(jié)
if(nByte == 7)
{
// 額外得到一個(gè)目標(biāo)解碼字節(jié)
*pDst = nLeft;
// 修改目標(biāo)串的指針和計(jì)數(shù)值
pDst++;
nDst++;
// 組內(nèi)字節(jié)序號和殘余數(shù)據(jù)初始化
nByte = 0;
nLeft = 0;
}
// 修改源串的指針和計(jì)數(shù)值
pSrc++;
nSrc++;
}
*pDst = 0;
// 返回目標(biāo)串長度
return nDst;
}
需要指出的是,7-bit的字符集與ANSI標(biāo)準(zhǔn)字符集不完全一致,在0x20以下也排布了一些可打印字符,但英文字母、阿拉伯?dāng)?shù)字和常用符號的位置兩者是一樣的。用上面介紹的算法收發(fā)純英文短消息,一般情況應(yīng)該是夠用了。如果是法語、德語、西班牙語等,含有 “?”、 “é”這一類字符,則要按上面編碼的輸出去查表,請參閱GSM 03.38的規(guī)定。
8-bit編碼其實(shí)沒有規(guī)定什么具體的算法,不需要介紹。
UCS2編碼是將每個(gè)字符(1-2個(gè)字節(jié))按照ISO/IEC106的規(guī)定,轉(zhuǎn)變?yōu)?6位的Unicode寬字符。在Windows系統(tǒng)中,特別是在2000/XP中,可以簡單地調(diào)用API 函數(shù)實(shí)現(xiàn)編碼和解碼。如果沒有系統(tǒng)的支持,比如用單片機(jī)控制手機(jī)模塊收發(fā)短消息,只好用查表法解決了。
Windows環(huán)境下,用C實(shí)現(xiàn)UCS2編碼和解碼的算法如下:
// UCS2編碼
// pSrc: 源字符串指針
// pDst: 目標(biāo)編碼串指針
// nSrcLength: 源字符串長度
// 返回: 目標(biāo)編碼串長度
int gsmEncodeUcs2(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
int nDstLength; // UNICODE寬字符數(shù)目
WCHAR wchar[128]; // UNICODE串緩沖區(qū)
// 字符串-->UNICODE串
nDstLength = ::MultiByteToWideChar(CP_ACP, 0, pSrc, nSrcLength, wchar, 128);
// 高低字節(jié)對調(diào),輸出
for(int i=0; i<nDstLength; i++)
{
// 先輸出高位字節(jié)
*pDst++ = wchar[i] >> 8;
// 后輸出低位字節(jié)
*pDst++ = wchar[i] & 0xff;
}
// 返回目標(biāo)編碼串長度
return nDstLength * 2;
}
// UCS2解碼
// pSrc: 源編碼串指針
// pDst: 目標(biāo)字符串指針
// nSrcLength: 源編碼串長度
// 返回: 目標(biāo)字符串長度
int gsmDecodeUcs2(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
int nDstLength; // UNICODE寬字符數(shù)目
WCHAR wchar[128]; // UNICODE串緩沖區(qū)
// 高低字節(jié)對調(diào),拼成UNICODE
for(int i=0; i<nSrcLength/2; i++)
{
// 先高位字節(jié)
wchar[i] = *pSrc++ << 8;
// 后低位字節(jié)
wchar[i] |= *pSrc++;
}
// UNICODE串-->字符串
nDstLength = ::WideCharToMultiByte(CP_ACP, 0, wchar, nSrcLength/2, pDst, 160, NULL, NULL);
// 輸出字符串加個(gè)結(jié)束符
pDst[nDstLength] = '';
// 返回目標(biāo)字符串長度
return nDstLength;
}
用以上編碼和解碼模塊,還不能將短消息字符串編碼為PDU串需要的格式,也不能直接將PDU串中的用戶信息解碼為短消息字符串,因?yàn)檫€差一個(gè)在可打印字符串和字節(jié)數(shù)據(jù)之間相互轉(zhuǎn)換的環(huán)節(jié)??梢匝h(huán)調(diào)用sscanf和sprintf函數(shù)實(shí)現(xiàn)這種變換。下面提供不用這些函數(shù)的算法,它們也適用于單片機(jī)、DSP編程環(huán)境。
// 可打印字符串轉(zhuǎn)換為字節(jié)數(shù)據(jù)
// 如:"C8329BFD0E01" --> {0xC8, 0x32, 0x9B, 0xFD, 0x0E, 0x01}
// pSrc: 源字符串指針
// pDst: 目標(biāo)數(shù)據(jù)指針
// nSrcLength: 源字符串長度
// 返回: 目標(biāo)數(shù)據(jù)長度
int gsmString2Bytes(const char* pSrc, unsigned char* pDst, int nSrcLength)
{
for(int i=0; i<nSrcLength; i+=2)
{
// 輸出高4位
if(*pSrc>='0' && *pSrc<='9')
{
*pDst = (*pSrc - '0') << 4;
}
else
{
*pDst = (*pSrc - 'A' + 10) << 4;
}
pSrc++;
// 輸出低4位
if(*pSrc>='0' && *pSrc<='9')
{
*pDst |= *pSrc - '0';
}
else
{
*pDst |= *pSrc - 'A' + 10;
}
pSrc++;
pDst++;
}
// 返回目標(biāo)數(shù)據(jù)長度
returnnSrcLength / 2;
}
// 字節(jié)數(shù)據(jù)轉(zhuǎn)換為可打印字符串
// 如:{0xC8, 0x32, 0x9B, 0xFD, 0x0E, 0x01} --> "C8329BFD0E01"
// pSrc: 源數(shù)據(jù)指針
// pDst: 目標(biāo)字符串指針
// nSrcLength: 源數(shù)據(jù)長度
// 返回: 目標(biāo)字符串長度
int gsmBytes2String(const unsigned char* pSrc, char* pDst, int nSrcLength)
{
const char tab[]="0123456789ABCDEF"; // 0x0-0xf的字符查找表
for(int i=0; i<nSrcLength; i++)
{
// 輸出低4位
*pDst++ = tab[*pSrc >> 4];
// 輸出高4位
*pDst++ = tab[*pSrc & 0x0f];
pSrc++;
}
// 輸出字符串加個(gè)結(jié)束符
*pDst = '';
// 返回目標(biāo)字符串長度
return nSrcLength * 2;
}
相關(guān)鏈接:手機(jī)短信編碼解碼 在線PDU格式編碼/解碼
總結(jié)
以上是生活随笔為你收集整理的SMS短信PDU编码的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。