這是2005年6月云南移動短信網關升級到3.0時寫的,在SP那穩定運行了很長時間的。因為SP倒閉了,貼出來給有興趣的朋友參考。
優點:支持多線程、滑動窗口、異步發送、全事件模式、自動識別ASCII、GBK、UCS-2
缺點:不支持長短信自動分頁、不支持PROVISION接口(偶的PROVISION接口是用WEB SERVICE實現的)
using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using System.Net.Sockets;
using System.Security.Cryptography;
namespace Tiray.SMS
{
?/// <summary>
?/// CMPP30 的摘要說明。
?/// </summary>
?
?public class CMPP30
?{
??#region Constants
??public const Byte CMPP_VERSION_30???=0x30;
??public const Byte CMPP_VERSION_21???=0x20;
??public const UInt32 CMD_ERROR????=0xFFFFFFFF;
??public const UInt32 CMD_CONNECT????=0x00000001;???
??public const UInt32 CMD_CONNECT_RESP??=0x80000001;
??public const UInt32 CMD_TERMINATE???=0x00000002;?//?終止連接
??public const UInt32 CMD_TERMINATE_RESP??=0x80000002;?//?終止連接應答
??public const UInt32 CMD_SUBMIT????=0x00000004;?//?提交短信
??public const UInt32 CMD_SUBMIT_RESP???=0x80000004;?//?提交短信應答
??public const UInt32 CMD_DELIVER????=0x00000005;?//?短信下發
??public const UInt32 CMD_DELIVER_RESP??=0x80000005;?//?下發短信應答
??public const UInt32 CMD_QUERY????=0x00000006;?//?短信狀態查詢
??public const UInt32 CMD_QUERY_RESP???=0x80000006;?//?短信狀態查詢應答
??public const UInt32 CMD_CANCEL????=0x00000007;?//?刪除短信
??public const UInt32 CMD_CANCEL_RESP???=0x80000007;?//?刪除短信應答
??public const UInt32 CMD_ACTIVE_TEST???=0x00000008;?//?激活測試
??public const UInt32 CMD_ACTIVE_TEST_RESP?=0x80000008;?//?激活測試應答
??#endregion
??#region Protected Member Variables;
??protected string m_strSPID;//SP企業代碼;
??protected string m_strPassword;//SP密碼;
??protected string m_strAddress;//短信網關地址
??protected int m_iPort;//短信網關端口號;
??protected static UInt32 m_iSeqID=0;//命令的序號
??protected int m_iSlidingWindowSize=16;//滑動窗口大小(W)
??protected int m_iActiveTestSpan=150;//ACTIVETEST的時間間隔(C,以秒為單位),標準為180
??protected DateTime m_dtLastTransferTime;//最近一次網絡傳輸時間
??protected int m_iTimeOut=60;//響應超時時間(T,以秒為單位)
??protected int m_iSendCount=3;//最大發送次數(N)
??protected DATA_PACKAGE[] SlidingWindow=null;
??protected TcpClient m_TcpClient=null;
??protected NetworkStream m_NetworkStream=null;
??protected Queue m_MessageQueue=null;//消息隊列,用于保存所有待發送數據
??protected int m_iTcpClientTimeout=5;//TcpClient接收和發送超時(以秒為單位)
??protected int m_iSendSpan=10;//發送間隔,以毫秒為單位
??#endregion
??#region Worker Thread
??protected System.Threading.Thread m_SendThread=null;
??protected System.Threading.Thread m_ReceiveThread=null;
??
??protected AutoResetEvent m_eventSendExit=new AutoResetEvent(false);
??protected AutoResetEvent m_eventReceiveExit=new AutoResetEvent(false);
??protected AutoResetEvent m_eventConnect=new AutoResetEvent(false);
??protected AutoResetEvent m_eventDisconnect=new AutoResetEvent(false);
??protected ManualResetEvent m_eventSend=new ManualResetEvent(false);
??protected ManualResetEvent m_eventReceive=new ManualResetEvent(false);
??protected void SendThreadProc()
??{
???while(true)
???{
????if(m_eventSendExit.WaitOne(TimeSpan.FromMilliseconds(0),false))?
????{
?????Disconnect();
?????break;
????}
????if(m_eventConnect.WaitOne(TimeSpan.FromMilliseconds(0),false))//連接
????{
?????if(Connect())//連接上,開始發送和接收
?????{
??????m_eventSend.Set();
??????m_eventReceive.Set();
?????}
?????else
?????{
??????Close();
??????Thread.Sleep(5000);
??????m_eventConnect.Set();
?????}
????}
????if(m_eventDisconnect.WaitOne(TimeSpan.FromMilliseconds(0),false))//拆除連接
????{
?????m_eventSend.Reset();
?????m_eventReceive.Reset();
?????Disconnect();
?????Thread.Sleep(5000);
?????m_eventConnect.Set();
????}
????if((m_eventSend.WaitOne(TimeSpan.FromMilliseconds(0),false))&&(m_NetworkStream!=null))
????{
?????bool bOK=true;
?????ActiveTest();????
?????Monitor.Enter(SlidingWindow);
?????for(int i=0;i<m_iSlidingWindowSize;i++)//首先用消息隊列中的數據填充滑動窗口
?????{
??????if(SlidingWindow[i].Status==0)
??????{
???????DATA_PACKAGE dp=new DATA_PACKAGE();
???????dp.Data=null;
???????Monitor.Enter(m_MessageQueue);
???????if(m_MessageQueue.Count>0)
???????{
????????dp =(DATA_PACKAGE)m_MessageQueue.Dequeue();
????????SlidingWindow[i]=dp;
???????}
???????Monitor.Exit(m_MessageQueue);
??????}
?????}
?????for(int i=0;i<m_iSlidingWindowSize;i++)
?????{
??????DATA_PACKAGE dp =SlidingWindow[i];
??????if((dp.Status==1)&&(dp.SendCount==0))//第一次發送
??????{
???????bOK=Send(dp);
???????if((bOK)&&(dp.Command>0x80000000))//發送的是Response類的消息,不需等待Response
???????{
????????SlidingWindow[i].Status=0;//清空窗口
???????}
???????else if((bOK)&&(dp.Command<0x80000000))//發送的是需要等待Response的消息
???????{
????????
????????SlidingWindow[i].SendTime=DateTime.Now;
????????SlidingWindow[i].SendCount++;
???????}
???????else
???????{
????????bOK=false;
????????break;//網絡出錯
???????}
???????
??????}
??????else if((dp.Status==1)&&(dp.SendCount>0))//第N次發送
??????{
???????if(dp.SendCount>m_iSendCount-1)//已發送m_iSendCount次,丟棄數據包
???????{
????????SlidingWindow[i].Status=0;//清空窗口
????????if(dp.Command==CMPP30.CMD_ACTIVE_TEST)//是ActiveTest
????????{
?????????bOK=false;
?????????break;//ActiveTest出錯
????????}
????????
???????}
???????else
???????{
????????TimeSpan ts=DateTime.Now-dp.SendTime;
????????if(ts.TotalSeconds>=m_iTimeOut)//超時后未收到回應包
????????{
?????????bOK=Send(dp);//再次發送
?????????if(bOK)
?????????{
??????????SlidingWindow[i].SendTime=DateTime.Now;
??????????SlidingWindow[i].SendCount++;
?????????}
?????????else
?????????{
??????????bOK=false;
??????????break;//網絡出錯
?????????}
????????}
???????}
??????}
?????}
?????Monitor.Exit(SlidingWindow);
?????
?????if(!bOK)
?????{
??????Close();//關閉連接
??????Thread.Sleep(5000);//等待5秒
??????m_eventSend.Reset();
??????m_eventConnect.Set();
?????}
????}
???}
??}
??protected void ReceiveThreadProc()
??{
???while(true)
???{
????if(m_eventReceiveExit.WaitOne(TimeSpan.FromMilliseconds(0),false))?
????{
?????break;
????}
????if((m_eventReceive.WaitOne(TimeSpan.FromMilliseconds(0),false)&&(m_NetworkStream!=null)))
????{
?????CMPP_HEAD Head=ReadHead();
?????if(Head.CommandID==CMPP30.CMD_SUBMIT_RESP)
?????{
??????ReadSubmitResp(Head);
?????}
?????else if(Head.CommandID==CMPP30.CMD_ACTIVE_TEST)
?????{
??????ActiveTestResponse(Head.SequenceID);
?????}
?????else if(Head.CommandID==CMPP30.CMD_ACTIVE_TEST_RESP)
?????{
??????ReadActiveTestResponse(Head);
?????}
?????else if(Head.CommandID==CMPP30.CMD_DELIVER)
?????{
??????ReadDeliver(Head);
?????}
?????else if(Head.CommandID==CMPP30.CMD_ERROR)//網絡故障
?????{
??????m_eventReceive.Reset();
??????m_eventDisconnect.Set();
?????}
????}
???}
??}
??#endregion
??#region Constructor
??public CMPP30(string SPID,string Password,string Address,int Port)
??{
???m_strSPID=SPID;
???m_strPassword=Password;
???m_strAddress=Address;
???m_iPort=Port;
???SlidingWindow=new DATA_PACKAGE[m_iSlidingWindowSize];//初始化滑動窗口
???for(int i=0;i<m_iSlidingWindowSize;i++)
????SlidingWindow[i]=new DATA_PACKAGE();
???m_MessageQueue=new Queue();
??}
??#endregion
??#region SMSEvents
??public event Tiray.SMS.SMSEventHandler SMSStateChanged;
??protected? void RaiseEvent(SMS_STATE State,Object Data)
??{
???if(null!=SMSStateChanged)
???{
????SMSEventArgs e=new SMSEventArgs();
????e.Time=DateTime.Now;
????e.State=State;
????e.Data=Data;
????SMSStateChanged(this,e);
???}
??}
??#endregion
??#region Protected Methods
??protected UInt32 TimeStamp(DateTime dt)
??{
???string str=String.Format("{0:MMddhhmmss}",dt);
???return Convert.ToUInt32(str);
??}
??protected UInt32 CreateID()
??{
???UInt32 id=m_iSeqID;
???m_iSeqID++;
???if(m_iSeqID>UInt32.MaxValue)
????m_iSeqID=0;
???return id;
??}
??protected Byte[] CreateDigest(DateTime dt)
??{
???int iLength=25+m_strPassword.Length;
???Byte[] btContent=new Byte[iLength];
???Array.Clear(btContent,0,iLength);
???int iPos=0;
???foreach(char ch in m_strSPID)
???{
????btContent[iPos]=(Byte)ch;
????iPos++;
???}
???iPos+=9;
???foreach(char ch in m_strPassword)
???{
????btContent[iPos]=(Byte)ch;
????iPos++;
???}
???string strTimeStamp=String.Format("{0:MMddhhmmss}",dt);
???foreach(char ch in strTimeStamp)
???{
????btContent[iPos]=(Byte)ch;
????iPos++;
???}
???MD5 md5 = new MD5CryptoServiceProvider();
???return md5.ComputeHash(btContent);
??}
??protected bool Close()
??{
???if(m_NetworkStream!=null)
????m_NetworkStream.Close();
???if(m_TcpClient!=null)
????m_TcpClient.Close();
???
???m_TcpClient=null;
???m_NetworkStream=null;
???return true;
??}
??protected bool Connect()
??{
???bool bOK=true;
???string strError=string.Empty;
???CMPP_CONNECT_RESP resp=new CMPP_CONNECT_RESP();
???try
???{
????m_TcpClient=new TcpClient();
????m_TcpClient.ReceiveTimeout=m_TcpClient.SendTimeout=m_iTcpClientTimeout*1000;
????m_TcpClient.Connect(m_strAddress,m_iPort);
????m_NetworkStream=m_TcpClient.GetStream();
????
????DateTime dt=DateTime.Now;
????CMPP_CONNECT conn=new CMPP_CONNECT();
????conn.Head=new CMPP_HEAD();
????conn.Head.CommandID=CMPP30.CMD_CONNECT;
????conn.Head.SequenceID=CreateID();
????conn.SourceAddress=m_strSPID;
????conn.TimeStamp=TimeStamp(dt);
????conn.AuthenticatorSource=CreateDigest(dt);
????conn.Version=CMPP_VERSION_30;
????Byte[] buffer=conn.GetBuffer();
????m_NetworkStream.Write(buffer,0,(Int32)conn.Head.TotalLength);
????int iSpan=0;
????bool bTimeOut=false;
????while(!m_NetworkStream.DataAvailable)//等待RESPONSE 5秒
????{
?????Thread.Sleep(10);
?????iSpan++;
?????if(iSpan>500)
?????{
??????bTimeOut=true;
??????break;
?????}
????}
????if(!bTimeOut)
????{
?????CMPP_HEAD Head=ReadHead();
?????if(Head.CommandID==CMD_CONNECT_RESP)
?????{
??????resp=ReadConnectResp(Head);
??????if(resp.Status==0)
???????bOK=true;
??????else
??????{
???????bOK=false;
???????strError="未正確接收CONNECT_RESP";
??????}
?????}
????}
????else
????{
?????bOK=false;
?????strError="等待CONNECT_RESP超時";
????}
???}
???catch(Exception e)
???{
????strError=e.Message;
????bOK=false;
???}
???if(bOK)
????RaiseEvent(SMS_STATE.SP_CONNECT,resp);
???else
????RaiseEvent(SMS_STATE.SP_CONNECT_ERROR,strError);
???return bOK;
??}
??protected bool Disconnect()
??{
???bool bOK=true;
???string strError=string.Empty;
???try
???{
????DateTime dt=DateTime.Now;
????CMPP_HEAD Head=new CMPP_HEAD();
????Head.CommandID=CMPP30.CMD_TERMINATE;
????Head.SequenceID=CreateID();
????Head.TotalLength=(UInt32)Marshal.SizeOf(Head);
????Byte[] buffer=Head.GetBuffer();
????m_NetworkStream.Write(buffer,0,(Int32)Head.TotalLength);
????int iSpan=0;
????bool bTimeOut=false;
????while(!m_NetworkStream.DataAvailable)//等待RESPONSE 5秒
????{
?????Thread.Sleep(10);
?????iSpan++;
?????if(iSpan>500)
?????{
??????bTimeOut=true;
??????break;
?????}
????}
????if(!bTimeOut)
????{
?????Head=ReadHead();
?????if(Head.CommandID==CMD_TERMINATE_RESP)
??????bOK=true;
?????else
?????{
??????bOK=false;
??????strError="未正確接收TERMINATE_RESP";
?????}
????}
????else
????{
?????bOK=false;
?????strError="等待TERMINATE_RESP超時";
????}
???}
???catch (Exception ex)
???{
????bOK=false;
????strError=ex.Message;
???}
???if(m_NetworkStream!=null)
????m_NetworkStream.Close();
???if(m_TcpClient!=null)
????m_TcpClient.Close();
???m_TcpClient=null;
???m_NetworkStream=null;
???if(bOK)
????RaiseEvent(SMS_STATE.SP_DISCONNECT,null);
???else
????RaiseEvent(SMS_STATE.SP_DISCONNECT_ERROR,strError);
???return bOK;
??}
??protected bool Send(DATA_PACKAGE dp)
??{
???bool bOK=true;
???string strError=string.Empty;
???SMS_STATE state=SMS_STATE.UNKNOW_ERROR;
???try
???{
????Thread.Sleep(m_iSendSpan);
????Byte[] btData=null;
????if(dp.Command==CMD_ACTIVE_TEST)
????{
?????btData=((CMPP_HEAD)dp.Data).GetBuffer();
?????state=SMS_STATE.ACTIVE_TEST;
????}
????else if(dp.Command==CMD_ACTIVE_TEST_RESP)
????{
?????btData=((CMPP_ACTIVE_TEST_RESP)dp.Data).GetBuffer();
?????state=SMS_STATE.ACTIVE_TEST_RESPONSE;
????}
????else if(dp.Command==CMD_DELIVER_RESP)
????{
?????btData=((CMPP_DELIVER_RESP)dp.Data).GetBuffer();
?????state=SMS_STATE.DELIVER_RESPONSE;
????}
????else if(dp.Command==CMD_SUBMIT)
????{
?????btData=((CMPP_SUBMIT)dp.Data).GetBuffer();
?????state=SMS_STATE.SUBMIT;
????}
????m_NetworkStream.Write(btData,0,btData.Length);
????m_dtLastTransferTime=DateTime.Now;
???}
???catch(Exception ex)
???{
????
????bOK=false;
????strError=ex.Message;
???}
???if(bOK)
???{
????RaiseEvent(state,dp.Data);
???}
???else
???{
????if(state==SMS_STATE.ACTIVE_TEST)
?????state=SMS_STATE.ACTIVE_TEST_ERROR;
????else if(state==SMS_STATE.ACTIVE_TEST_RESPONSE)
?????state=SMS_STATE.ACTIVE_TEST_RESPONSE_ERROR;
????else if(state==SMS_STATE.DELIVER_RESPONSE)
?????state=SMS_STATE.DELIVER_RESPONSE_ERROR;
????else if(state==SMS_STATE.SUBMIT)
?????state=SMS_STATE.SUBMIT_ERROR;
????RaiseEvent(state,strError);
???}
???return bOK;
??}