可扩展的SockBase设计和实现(1)

可扩展的SockBase设计和实现(1)

 

 

目录

   摘要

   基于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的网络编程开发.

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值