可扩展的SockBase设计和实现(1)
目錄<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
?? 摘要
?? 基于Sockets網(wǎng)絡(luò)編程存在的問題
?? 可擴展的SockBase設(shè)計
?? SockBase的編程實現(xiàn)
?? 從SockBase繼承及其使用方法
?
???
摘要
??? System.Net 命名空間為當前網(wǎng)絡(luò)上使用的多種協(xié)議提供了簡單的編程接口,如果需要底層控制更多的編程而言,開發(fā)人員就需要使用System.Net.Sockets 命名空間了。System.Net.Sockets為需要嚴密控制網(wǎng)絡(luò)訪問的開發(fā)人員提供了 Windows Sockets (Winsock) 接口的托管實現(xiàn)。 但是Sockets編程既煩雜,又毫無可擴展性,需要開發(fā)人員自己控制消息的接受和發(fā)送以及處理,這些與業(yè)務(wù)邏輯有關(guān)的工作在編程之時就需要寫入代碼,一旦需求發(fā)生變化,又得改寫Sockets的接受消息列表和處理。同時對于命令字符串的構(gòu)造都需要底層的程序員去控制,不僅容易出錯,而且不易改變。面對復雜多變的業(yè)務(wù)邏輯,這樣的架構(gòu)毫無可重用而言,同時給程序員提出了很高的要求,很大程度的工作量放在了做底層的重復性的勞動上。因此,為了提供一種易于擴展的Sockets編程架構(gòu),使得開發(fā)人員將注意力放在業(yè)務(wù)邏輯上,我們提出了設(shè)計可擴展的SockBase思路,同時實現(xiàn)了這個架構(gòu),經(jīng)驗表明,不僅解決了上述存在的問題,而且取得了非常好的效果。
?
?
基于Sockets網(wǎng)絡(luò)編程存在的問題
??? 在一般的基于Sockets網(wǎng)絡(luò)編程中,不難出現(xiàn)以下代碼:
?while(sock != null){
?????? temp=ReadMsg(); //調(diào)用sock.Recevie(..)函數(shù),把byte[]轉(zhuǎn)成字符串
??? if( temp.Trim() == "Login")
?????? {
??????? // do some thing…
??????? sock.Send( TransMsg("OK"));
??? }
??? else if (temp.Trim() == "show")
?????? {
??????? // do some thing…
??????? sock.Send( TransMsg(ips));
?????? }
?????? else if (temp.Trim() == "Upload"){
??????? // do some thing…
??????? sock.Send(TransMsg("OK"));
??????? // do some thing…
????????????? sock.Send(TransMsg("OK"));
?????? }
?????? else if (temp.Trim() == "list"){
??????? // do some thing…
??????? sock.Send(TransMsg(files));
?????? }
?????? else if (temp.Trim() == "Get"){
??????? // do some thing…
??????? sock.Send(TransMsg("OK"));
????????????? temp = ReadMsg().Trim();
??????? // do some thing…
??? }
}
?
從上面的代碼中,我們可以注意到,對于所有的從客戶端發(fā)來的消息,都是統(tǒng)一在這個while()循環(huán)中進行處理的…
??? 對于消息命令的接收,顯然一般都是和上面的代碼方式類似,統(tǒng)一放到一個地方來執(zhí)行.但是上述代碼對于消息的派發(fā)是使用的Switch case的結(jié)構(gòu).這樣就帶來一個問題.Switch Case是在程序代碼編寫階段寫的,也就是所謂的硬性編入.即在程序運行過程中不可修改.這樣就使得程序不能在運行過程中對用戶不同的輸入/不同的條件發(fā)送不同的消息,或是用戶自定義的,或是程序完成后新加入的擴展的命令..同時,也還是由于Switch case結(jié)構(gòu),使得對于消息的處理也固定下來了,同樣也不能動態(tài)的去修改消息處理函數(shù).這樣使得程序的擴展性很差,而且對于底層的如上述代碼,對于Socket的操作,完全不能直接使用到別的軟件中.(因為消息命令,處理函數(shù)不一定是完全一樣的).
?????? 也就是說,在通常的Sockets的網(wǎng)絡(luò)開發(fā)中,開發(fā)人員自己控制消息的接受和發(fā)送以及處理,這些與業(yè)務(wù)邏輯有關(guān)的工作在編程之時就需要寫入代碼,一旦需求發(fā)生變化,又得改寫Sockets的接受消息列表和處理。同時對于命令字符串的構(gòu)造都需要底層的程序員去控制,不僅容易出錯,而且不易改變。面對復雜多變的業(yè)務(wù)邏輯,這樣的架構(gòu)毫無可重用而言,同時給程序員提出了很高的要求,很大程度的工作量放在了做底層的重復性的勞動上。
?
?
可擴展的SockBase設(shè)計
??? 針對上面的問題,我們提出了可擴展的SockBase.可擴展性主要在于能接收任意的消息,而且能對同一個消息在不同的情況下面有不同的處理函數(shù)..
我們想到了Windows的消息處理機制.當我們要處理一個系統(tǒng)消息的時候,或是處理我們自定義的消息的時候,首先,我們把自定義消息加入到程序的消息列表中去,同時通過Windows編程中的消息映射的方式,運行增加對處理此消息的函數(shù).使操作系統(tǒng)在收到這個消息后,能夠找到我們對其進行綁定的消息處理函數(shù),進而調(diào)用..
回到Sockets中來,我們先做出一個類似Windows消息映射表樣的東西.其中有兩個元素,一個就是收到的消息命令,另一個就是收到此消息后的處理函數(shù),在程序開發(fā)者開發(fā)過程中,只要在具體的消息接收前先對消息映射表進行初始化,就夠了.SockBase會自動的調(diào)用相應(yīng)的消息處理函數(shù).
?
?
SockBase的編程實現(xiàn)
??? 上面的部分都是理論.現(xiàn)在我們開始完成SockBase的實現(xiàn)代碼.
?
1.定義消息映射表
根據(jù)上面所提到的,需要有一個類似消息映射表的東西.這里,我們使用Hashtable來存儲消息和處理函數(shù)的數(shù)據(jù)..由于Hashtable是一種鍵/值型的集合,所以我們把消息命令做為鍵,對應(yīng)的消息處理函數(shù)做為值.由于消息有很多種,而且我們希望對于所有的消息,都能在一個地方去調(diào)用相應(yīng)的處理函數(shù).所以我們使用了.NET的委托做為Hashtable中的值.
定義的委托如下:
Public delegate Command(string args);
?
使用方法如下:
Hashtable Commands = new Hashtable();
Commands.Add( /*消息命令*/, new Command( /*具體的處理函數(shù)*/));
調(diào)用的時候只要
((Command)Commands[/*消息命令*/])(/*參數(shù)*/);
就可以了~
?
2,SockBase的具體實現(xiàn)
好,現(xiàn)在,關(guān)于消息映射表的準備工作已完成了.現(xiàn)在開始SockBase的實現(xiàn):P
?????? (1) 構(gòu)造函數(shù)以及變量的聲明,實現(xiàn)
public class SocketBase:IDisposable{
???????? //待處理的命令處理集合
???????? protected Hashtable m_CommandHandlerList;
???????? protected NetworkStream readStream;
???????? protected NetworkStream writeStream;
???????? protected Socket m_sock;
???????? //通過構(gòu)造函數(shù)將Socket的實例傳進來.
???????? public SocketBase(Socket sock){
m_sock = sock;
readStream = new NetworkStream(m_sock);
writeStream = new NetworkStream(m_sock);
}
?
???????? public void Dispose()
???????? {
????????????? // 關(guān)閉本地套節(jié)子?????
????????????? try
????????????? {
?????????????????? if (m_sock!= null)
?????????????????? {
?????????????????????? if(m_sock.Connected)
?????????????????????? {
??????????????????????????? m_sock.Shutdown(SocketShutdown.Both);
??????????????????????????? m_sock.Close();
?????????????????????? }
?????????????????????? m_sock = null;
?????????????????? }???
????????????? }
????????????? catch(Exception ex)
????????????? {
????????????? }
}
}
?
??? (2) 發(fā)送和接收函數(shù)
準備工作已完成了.現(xiàn)在就是我們開始對m_sock進行消息接收,以及對消息進行派發(fā)了.
首先是消息發(fā)送和接收.由于Socket的不確定性,所以很容易出現(xiàn)發(fā)送的多個消息在接收的時候混在一起了,所以我們決定每發(fā)一個消息就發(fā)送固定大小的包,接收時了接收相應(yīng)大小的包.
在SockBase中定義一個包的固定大小:
private static int???? DefaulteBufferSize =5120;
public int BufferSize
{
get{
????????????? if(m_BufferSize!=0)
?????????????????? return m_BufferSize;
????????????? else
?????????????????? return DefaulteBufferSize;
???????? }
???? set{m_BufferSize=value;}
}
再就是發(fā)送,接收函數(shù)
??????? public string? ReceiveMsg()
????????????? {
??????????????????????????? byte[] Recs=new byte[BufferSize];
??????????????????????????? int count = 0;
??????????????????????????? int num;
??????????????????????????? do {
?????????????????????????????????? num = ReadStream.Read(Recs,count,BufferSize-count);
?????????????????????????????????? if( num == 0){
??????????????????????? throw new Exception("客戶端不正常關(guān)閉");
??????????????????? }
??????????????????? count += num;
??????????????????????????? } while( count < BufferSize);
return System.Text.Encoding.GetEncoding(Encoding).GetString(Recs).Replace("\0","");
}
?
??????? public void Send(string msg)
????????????? {
???????????????????? byte[] sender = new Byte[BufferSize];
???????????????????? byte[] temp =? System.Text.Encoding.Unicode.GetBytes(msg) ;
???????????????????? Array.Copy( temp,sender,temp.Length);
???????????????????? WriteStream.Write(sender,0,sender.Length);
???????????????????? WriteStream.Flush();
????????????? }
??? (3) 消息派發(fā)函數(shù)
好了,下面就是對消息進行派發(fā)的函數(shù)了:
??????? public void CmdHandler(string ClientMessage)
????????????? {
??????????? //解析出命令
??????????? string[] cmdList=ClientMessage.Split(‘;’);
??????????? string cmdText = "";
???????????????????? for(int i=1;i<cmdList.Length;i++)
???????????????????? {
??????????????????????????? if(i==cmdList.Length-1)
??????????????????????????? {
?????????????????????????????????? cmdText += cmdList[i];
??????????????????????????? }
??????????????????????????? else
??????????????????????????? {
?????????????????????????????????? cmdText += cmdList[i]+":";
??????????????????????????? }
???????????????????? }
??????????? //尋找合適的匹配處理
???????????
if(m_CommandHandlerList.ContainsKey( cmdList[0] ) ) {
??????????????? ( ( Command ) m_ConnamdHandlerList[ cmdList[0] ) ( cmdText);
}
}
我們通過對m_CommandHandlerList中所有的鍵(即注冊的消息命令)進行判斷,如果和接收到的消息的命令是相同的,就直接去調(diào)用存在此Hashtable中對應(yīng)的值(即Command委托)..
(4) SockBase運行的起點
最后的部分,整個SockBase運行的起點:
??????? public void ListenSocket()
????????????? {
???????????????????? try
???????????????????? {
??????????????????????????? while(m_sock!=null&&m_sock.Connected)
??????????????????????????? {
??????????????????? //截獲消息,并作出相應(yīng)的處理
??????????????????? CmdHandler(ReceiveMsg());
??????????????????????????? }
???????????????????? }
????????????? }
現(xiàn)在我們只要直接在m_CommandHandlerList中加入我們要處理的消息的命令和處理函數(shù),再運行ListenSocket(),就可以對接收到的消息進行相應(yīng)的處理了..
?
?
從SockBase繼承及其使用方法
??? 上面實現(xiàn)了SockBase的基本的構(gòu)架.對于大部分的Sockets網(wǎng)絡(luò)編程,都可適用.下面就是使用的方法..
這里,我們從SockBase直接繼承而來一個Client_ListenThread.在此類中,我們通過構(gòu)造函數(shù),將相應(yīng)的Socket的實例傳給m_sock.再對消息映射表進行初始化,用一個線程專門運行ListenSockt來對接收到的消息進行派發(fā),調(diào)用其處理函數(shù).
??? public class Client_ListenThread : SocketBase????
?????? {
??????? #region 所有字段包含命令字段
??????? #endregion
?
??????? #region 所有方法
??????? public Client_ListenThread(Socket Client_socket) : base(socket)
??????? {
??????????? LoadCommandHandlerList();
????????????? }
?????????????
??????? //裝載所有的命令處理隊列
???????
??????? public void LoadCommandHandlerList()
????????????? {
???????????????????? CommandHandlerItem.Add(“GetFile” , new Command(GetFileHandler);
???????????????????? CommandHandlerItem.Add(“FileOK”, Command(FileOKHandler);
?
??????? }
?
??????? //以下為所有命令處理函數(shù)
??????? private void GetFileHandler(string cmdText)
????????????? {
??????????? //檢查文件是否存在
??????????? if((new FileManager()).CheckFileExist(cmdTxt))
???????????????????? {
??????????????????????????? Send(“OK”);
??????????? }
??????????? else
???????????????????? {
??????????????????????????? Send(“Failure”);
??????????? }
??????? }
??????? private void FileOKHandler(string cmdText)
????????????? {
???????????????????? Dispose();
????????????? }
????????????? #endregion
?????? }
?
通過下面這個函數(shù)將其運行:
listen_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
…
listen_socket.Listen(-1);
while(true){
Client_ListenThread clientthread=new Client_ListenThread(listen_socket.Accept());
?????????????????????? if ( clientthread.Sock.Connected )
?????????????????????? {
??????????????????????????? Thread fileThread = new Thread(new ThreadStart(clientthread.ListenSocket));
??????????????????????????? fileThread.IsBackground=true;
??????????????????????????? fileThread.Start();
?????????????????????? }
}
?
總結(jié)
??? 通過上述的SockBase,我們可以在不改變SockBase的前提下,對消息映射表進行動態(tài)的修改.這樣使得開發(fā)人員將注意力放在業(yè)務(wù)邏輯上,極大的方便了基于Sockets的網(wǎng)絡(luò)編程開發(fā).
轉(zhuǎn)載于:https://www.cnblogs.com/zhouxiancai0128/archive/2006/08/05/468503.html
總結(jié)
以上是生活随笔為你收集整理的可扩展的SockBase设计和实现(1)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 通过结束进程来关闭程序
- 下一篇: 我是一个硬盘[转]