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