Socket开发探秘--基类及公共类的定义 (转)

Socket开发是属于通信底层的开发,.NET也提供了非常丰富的类来实现Socket的开发工作,本篇不是介绍这些基础类的操作,而是从一个大的架构方面阐述Socket的快速开发工作,本篇以TCP模式进行程序的开发介绍,以期达到抛砖引玉的目的。

要掌握或者了解Socket开发,必须了解下面所述的场景及知识。

1、TCP客户端,连接服务器端,进行数据通信

2、TCP服务器端,负责侦听客户端连接

3、连接客户端的管理,如登陆,注销等,使用独立线程处理

4、数据接收管理,负责数据的接受,并处理队列的分发,使用独立线程处理,简单处理后叫给“数据处理线程”

5、数据处理线程,对特定的数据,采用独立的线程进行数据处理

6、数据的封包和解包,按照一定的协议进行数据的封装和解包

 

针对以上内容,可以封装以下功能的操作类作为共用基类:

1、BaseSocketClient,客户端基类

2、BaseSocketServer,TCP服务器管理基类

3、BaseClientManager,连接客户端管理类

4、BaseReceiver,数据接收处理类

5、ThreadHandler,数据独立线程处理类

6、PreData、DataTypeKey、Sign分别是定义数据的基础格式、协议标识、分隔符号等,另外我们定义需要发送的实体类信息,发送和接收通过实体类进行数据转换和解析。

 

以上类是基类,不能直接使用,在服务器端和客户端都要继承相应的类来完成所需要的工作。

BaseSocketClient只要负责客户端的链接、断开、发送、接收等操作,大致的定义如下:

代码
     public   class  BaseSocketClient
    {       
        
public  BaseSocketClient()
        {
            _Name 
=   this .GetType().Name;
        }

        
public  BaseSocketClient(Socket socket) :  this ()
        {
            _socket 
=  socket;
            IPEndPoint ipAndPort 
=  (IPEndPoint)socket.RemoteEndPoint;
            _IP 
=  ipAndPort.Address.ToString();
            _port 
=  ipAndPort.Port;
        }

        
///   <summary>
        
///  断开连接
        
///   </summary>
         public   virtual   void  DisConnect()
        {
            .........
        }

        
///   <summary>
        
///  主动连接
        
///   </summary>
         public   virtual   void  Connect( string  ip,  int  port)
        {
            ........
        }
        
        
///   <summary>
        
///  开始异步接收
        
///   </summary>
         public   void  BeginReceive()
        {
            .........
        }
        
         
///   <summary>
         
///  开始同步接收
         
///   </summary>                
          public   void  StartReceive()
         {
              .........
         }
         
        
///   <summary>
        
/// 异步发送
        
///   </summary>
         public   void  BeginSend(SendStateObject sendState)
        {
            ........
        }
        
        
///   <summary>
        
///  同步发送。直接返回成功失败状态
        
///   </summary>
         public   bool  SendTo( string  data)
        {
            .........
        }
        
///   <summary>
        
///  主动检查连接
        
///   </summary>
         public   virtual   void  CheckConnect()
        {
            .............
        }
        
        
protected   virtual   void  OnRead(PreData data)
        {
        }
    }

  

2、BaseSocketServer,TCP服务器管理基类

该类负责在独立的线程中侦听指定的端口,如果有客户端连接进来,则进行相应的处理,重载处理函数可以实现独立的处理。大致的定义如下。

代码
     public   class  BaseSocketServer
    {
        
public  BaseSocketServer()
        {
            
this ._SocketName  =   this .GetType().Name;
        }

        
///   <summary>
        
///  启动监听线程
        
///   </summary>
         public   void  StartListen( string  ip,  int  port)
        {
            _IP 
=  ip;
            _port 
=  port;
            
if  (_listenThread  ==   null )
            {
                _listenThread 
=   new  Thread(Listen);
                _listenThread.IsBackground 
=   true ;
                _listenThread.Start();
            }
        }

        
///   <summary>
        
///  检查监听线程
        
///   </summary>
         public   void  CheckListen()
        {
            
if  (_listenThread  ==   null   ||  ( ! _listenThread.IsAlive))
            {
                _listenThread 
=   new  Thread(Listen);
                _listenThread.IsBackground 
=   true ;
                _listenThread.Start();
            }
        }

        
///   <summary>
        
///  监听线程
        
///   </summary>
         protected   virtual   void  Listen()
        {
            IPEndPoint ipAndPort 
=   new  IPEndPoint(IPAddress.Parse(IP), Port);
            TcpListener tcpListener 
=   new  TcpListener(ipAndPort);
            tcpListener.Start(
50 ); // 配置

            
while  ( true )
            {
                Socket socket 
=  tcpListener.AcceptSocket();
                AcceptClient(socket);
             }
        }

        
///   <summary>
        
///  接收一个Client
        
///   </summary>
         protected   virtual   void  AcceptClient(Socket socket)
        {
        }

  

3、BaseClientManager,连接客户端管理类

由于考虑性能的影响,客户端对象的管理交给一个独立的线程进行处理,一则处理思路清晰,二则充分利用线程的性能。该类主要负责客户端登录超时处理,连接上来的客户端维护,经过登陆验证的客户端维护,客户端登陆验证接口,客户端发送数据处理等功能。

 

代码
     public   class  BaseClientManager < T >   where  T : BaseSocketClient
    {
        
#region  登陆管理

        
protected   string  _Name  =   " BaseClientManager " ;
        
private   int  _SessionId  =   0 ;
        
private   object  _LockSession  =   new   object ();

        
private  System.Threading.Timer _CheckInvalidClientTimer  =   null ; //  检查客户端连接timer
         private  System.Threading.Timer _SendTimer  =   null ; //  发送数据调用timer

        
///   <summary>
        
///  已经注册的客户端 关键字userid
        
///   </summary>
         protected  SortedList < string , T >  _loginClientList  =   new  SortedList < string , T > ();
        
///   <summary>
        
///  连上来的客户端 未注册 关键字Session
        
///   </summary>
         protected  SortedList < string , T >  _tempClientList  =   new  SortedList < string , T > ();
        
        
///   <summary>
        
///  构造函数
        
///   </summary>
         public  BaseClientManager()
        {
            
this ._Name  =   this .GetType().Name;
        }

        
///   <summary>
        
///  已经注册的客户端 关键字userid
        
///   </summary>
         public  SortedList < string , T >  LoginClientList
        {
            
get  {  return  _loginClientList; }
            
set  { _loginClientList  =  value; }
        }

        
///   <summary>
        
///  增加一个连上来(未注册)的客户端
        
///   </summary>
        
///   <param name="client"></param>
         public   void  AddClient(T client)
        {
            ......
        }

        
///   <summary>
        
///  增加一个已登录的客户端
        
///   </summary>
         public   void  AddLoginClient(T client)
        {
            ......
        }

        
///   <summary>
        
///  当客户端登陆,加入列表后的操作
        
///   </summary>
        
///   <param name="client"></param>
         protected   virtual   void  OnAfterClientSignIn(T client)
        {
        }

        
///   <summary>
        
///  验证登录
        
///   </summary>
         public   virtual   bool  CheckClientLogin( string  userId,  string  psw,  ref   string  memo)
        {
            
return   false ;
        }

        
///   <summary>
        
///  电召客户端登出
        
///   </summary>
        
///   <param name="userId"></param>
         public   void  ClientLogout( string  userId)
        {
            
if  (_loginClientList.ContainsKey(userId))
            {
                RadioCallClientLogout(_loginClientList[userId]);
            }
        }

        
///   <summary>
        
///  电召客户端登出
        
///   </summary>
        
///   <param name="client"></param>
         private   void  RadioCallClientLogout(T client)
        {
            client.DisConnect();
        }

        
///   <summary>
        
///  移除注册的客户端
        
///   </summary>
        
///   <param name="client"></param>
         private   void  RemoveLoginClient(T client)
        {
            ......
        }

        
///   <summary>
        
///  移除客户端后的操作
        
///   </summary>
        
///   <param name="client"></param>
         protected   virtual   void  OnAfterClientLogout(T client)
        {
        }

        
///   <summary>
        
///  在连接的列表中移除客户端对象
        
///   </summary>
        
///   <param name="client"></param>
         public   virtual   void  RemoveClient(T client)
        {
            RemoveLoginClient(client);
            RemoveTempClient(client);
        }
        
        
#endregion

        
///   <summary>
        
///  开始客户端连接处理
        
///   </summary>
         public   void  Start()
        {
            StartSendTimer();
            StartCheckInvalidClientTimer();
        }

        
///   <summary>
        
///  启动客户端发送数据线程
        
///   </summary>
         public   void  StartSendTimer()
        {
            ......
        }

        
///   <summary>
        
///  启动检查客户端连接timer
        
///   </summary>
         public   void  StartCheckInvalidClientTimer()
        {
            ......
        }

        
///   <summary>
        
///  检查客户端连接
        
///   </summary>
        
///   <param name="stateInfo"></param>
         private   void  CheckInvalidClient(Object stateInfo)
        {
            ......
        }

        
public   virtual   void  RemoveInvalidClient()
        {
            ......
        }

        
///   <summary>
        
///  增加一条客户端发送数据
        
///   </summary>
         public   void  AddSend( string  userid,  string  send,  bool  isFirst)
        {
            ......
        }
    }

 

 

4、BaseReceiver,数据接收处理类

该基类是所有接受数据的处理类,负责维护数据的队列关系,并进一步进行处理。

代码
     public   class  BaseReceiver
    {
        
protected   string  _Name  =   " BaseReceiver " ;
        
protected  Thread _PreDataHandlehread  =   null ; //  处理数据线程
         protected  Fifo < PreData >  _preDataFifo  =   new  Fifo < PreData > ( 50000 );

        
public  BaseReceiver()
        {
            _Name 
=   this .GetType().Name;
        }

        
///   <summary>
        
///  接收处理数据
        
///   </summary>
         public   void  AppendPreData(PreData data)
        {
            _preDataFifo.Append(data);
        }

        
///   <summary>
        
///  数据处理
        
///   </summary>
         protected   virtual   void  PreDataHandle()
        {
            ......
        }

        
///   <summary>
        
///  数据处理
        
///   </summary>
        
///   <param name="data"></param>
         public   virtual   void  PreDataHandle(PreData data)
        { 
        }

        
///   <summary>
        
///  开始数据处理线程
        
///   </summary>
         public   virtual   void  Start()
        {
            
if  (_PreDataHandlehread  ==   null )
            {
                _PreDataHandlehread 
=   new  Thread( new  ThreadStart(PreDataHandle));
                _PreDataHandlehread.IsBackground 
=   true ;
                _PreDataHandlehread.Start();
            }
        }
    }

 

 

5、ThreadHandler,数据独立线程处理类

对每个不同类型的数据(不同的协议类型),可以用独立的线程进行处理,这里封装了一个基类,用于进行数据独立线程的处理。

代码
     public   class  ThreadHandler < T >
    {
        Thread _Handlehread 
=   null ; //  处理数据线程
         private   string  _ThreadName  =   "" ;
        
private  Fifo < T >  _DataFifo  =   new  Fifo < T > ();

        
///   <summary>
        
///  接收处理数据
        
///   </summary>
         public   virtual   void  AppendData(T data)
        {
            
if  (data  !=   null )
                _DataFifo.Append(data);
        }

        
///   <summary>
        
///  数据处理
        
///   </summary>
         protected   virtual   void  DataThreadHandle()
        {
            
while  ( true )
            {
                    T data 
=  _DataFifo.Pop();
                DataHandle(data);
            }
        }

        
///   <summary>
        
///  数据处理
        
///   </summary>
        
///   <param name="data"></param>
         public   virtual   void  DataHandle(T data)
        {
        }

        
///   <summary>
        
///  检查数据处理线程
        
///   </summary>
         public   virtual   void  Check()
        {
            ......
        }

        
///   <summary>
        
///  开始数据处理线程
        
///   </summary>
         public   virtual   void  StartHandleThread()
        {
            ......
        }
    }

 

 

6、PreData、DataTypeKey、Sign

 PreData是定义了一个标准的协议数据格式,包含了协议关键字、协议内容、用户标识的内容,代码如下。

 

代码
     ///   <summary>
    
///  预处理的数据
    
///   </summary>
     public   class  PreData
    {
        
private   string  _key;
        
private   string  _content;
        
private   string  _userId;

        
public  PreData()
        { 
        }

        
public  PreData( string  key, string  data)
        {
            _key 
=  key;
            _content 
=  data;
        }

        
///   <summary>
        
///  协议关键字
        
///   </summary>
         public   string  Key
        {
            
get  {  return  _key; }
            
set  { _key  =  value; }
        }

        
///   <summary>
        
///  数据内容
        
///   </summary>
         public   string  Content
        {
            
get  {  return  _content; }
            
set  { _content  =  value; }
        }

        
///   <summary>
        
///  客户端过来为用户帐号,或者指定的名称
        
///   </summary>
         public   string  UserId
        {
            
get  {  return  _userId; }
            
set  { _userId  =  value; }
        }
    }

 

 

其中的DataTypeKey和Sign定义了一系列的协议头关键字和数据分隔符等信息。

代码
     public   class  DataTypeKey
    {
        
///   <summary>
        
///  认证请求 AUTHR C->S
        
///   </summary>
         public   const   string  AuthenticationRequest  =   " AUTHR " ;
        
///   <summary>
        
///  认证请求应答AUTHA S->C
        
///   </summary>
         public   const   string  AuthenticationAnswer  =   " AUTHA " ;

        
///   <summary>
        
///  测试数据TESTR C->S
        
///   </summary>
         public   const   string  TestDataRequest  =   " TESTR " ;
        
///   <summary>
        
///  测试数据TESTA S->C
        
///   </summary>
         public   const   string  TestDataAnswer  =   " TESTA " ;
        
        .........

    }

 

 

 下面是数据分割符号,定义了数据包的开始符号、结束符号,分隔符号和数据分隔符等。

代码
     public   class  Sign
    {
        
///   <summary>
        
///  开始符
        
///   </summary>
         public   const   string  Start  =   " ~ " ;
        
///   <summary>
        
///  开始符比特
        
///   </summary>
         public   const   byte  StartByte  =   0x7E ;
        
///   <summary>
        
///  结束符
        
///   </summary>
         public   const   string  End  =   " # " ;
        
///   <summary>
        
///  结束符比特
        
///   </summary>
         public   const   byte  EndByte  =   0x23 ;
        
///   <summary>
        
///  分隔符
        
///   </summary>
         public   const   string  Separator  =   " & " ;
        
///   <summary>
        
///  分隔符比特
        
///   </summary>
         public   const   byte  SeparatorByte  =   0x26 ;
        
///   <summary>
        
///  数据分隔符
        
///   </summary>
         public   const   string  DataSeparator  =   " | " ;
        
///   <summary>
        
///  数据分隔符比特
        
///   </summary>
         public   const   byte  DataSeparatorByte  =   0x7C ;
    }

 另外,前面说了,我们数据是通过实体类作为载体的,我们知道,收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样,我们发送数据的时候,内容部分肯定是按照一定协议规则串联起来的数据,那么我们就需要把实体转化为发送的数据格式。综上所述,我们通过实体类,必须实现数据的发送和读取的转换。

 

代码
     ///   <summary>
    
///  测试数据的实体类信息
    
///   </summary>
     public   class  TestDataRequest
    {
        
#region  MyRegion

        
///   <summary>
        
///  请求序列
        
///   </summary>
         public   string  seq;
        
///   <summary>
        
///  用户帐号
        
///   </summary>
         public   string  userid;
        
///   <summary>
        
///  用户密码
        
///   </summary>
         public   string  psw;

        
#endregion

        
public  TestDataRequest( string  seq,  string  userid,  string  psw)
        {
            
this .seq  =  seq;
            
this .userid  =  userid;
            
this .psw  =  psw;
        }
        
public  TestDataRequest()
        {
        }

        
///   <summary>
        
///  转换Socket接收到的信息为对象信息
        
///   </summary>
        
///   <param name="data"> Socket接收到的信息 </param>
         public  TestDataRequest( string  data)
        {
            
string [] dataArray  =   null ;
            dataArray 
=  NetStringUtil.UnPack(data);
            
if  (dataArray  !=   null   &&  dataArray.Length  >   0 )
            {
                TestDataRequest newAnswerData 
=   new  TestDataRequest();
                
int  i  =   0 ;
                
this .seq  =  dataArray[i ++ ];
                
this .userid  =  dataArray[i ++ ];
                
this .psw  =  dataArray[i ++ ];
            } 
        }

        
///   <summary>
        
///  转换对象为Socket发送格式的字符串
        
///   </summary>
        
///   <returns></returns>
         public   override   string  ToString()
        {
            
string  data  =   "" ;
            data 
=   this .seq  +   " | "   +   this .userid  +   " | "   +   this .psw.ToString();
            data 
=  NetStringUtil.PackSend(DataTypeKey.TestDataRequest, data);
            
return  data;
        }

  

在接下来的工作中,就需要继承以上的基类,完成相关的对象和数据的处理了。

本人是实际中,编写了一个测试的例子,大致的基类使用情况如下所示。




 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
类是一种特殊的继承方式,用于解决多继承中的菱形继承问题。在虚类中,类的成员变量只会被派生类的最顶层的类所共享,从而避免了菱形继承中的二义性和数据冗余问题。 在人与教师学生的例子中,可以用虚类来解决多继承带来的问题。首先,定义一个人类作为类,然后定义一个教师类和一个学生类,它们都继承自人类。由于教师和学生都是人,因此它们都有一些共同的属性和方法,比如姓名、年龄、性别等等。 现在假设我们需要定义一个既是教师又是学生的类,即一个人既可以教书又可以学习。如果采用普通的多继承方式,会出现两个人类的实例被创建,从而导致数据冗余和方法二义性的问题。为了解决这个问题,我们可以将人类定义为虚类,这样在派生类中只会创建一个人类的实例,从而避免了数据冗余和方法二义性的问题。 下面是一个示例代码: ```c++ #include <iostream> #include <string> using namespace std; class Person { public: Person(string name, int age, string gender) : m_name(name), m_age(age), m_gender(gender) { } void showInfo() { cout << "姓名:" << m_name << endl; cout << "年龄:" << m_age << endl; cout << "性别:" << m_gender << endl; } protected: string m_name; int m_age; string m_gender; }; class Teacher : virtual public Person { public: Teacher(string name, int age, string gender, string course) : Person(name, age, gender), m_course(course) { } void teach() { cout << m_name << "正在教授" << m_course << "课程" << endl; } protected: string m_course; }; class Student : virtual public Person { public: Student(string name, int age, string gender, string major) : Person(name, age, gender), m_major(major) { } void study() { cout << m_name << "正在学习" << m_major << "专业" << endl; } protected: string m_major; }; class TeacherStudent : public Teacher, public Student { public: TeacherStudent(string name, int age, string gender, string course, string major) : Person(name, age, gender), Teacher(name, age, gender, course), Student(name, age, gender, major) { } void showInfo() { cout << "教师学生的信息如下:" << endl; Person::showInfo(); cout << "授课科目:" << m_course << endl; cout << "所学专业:" << m_major << endl; } }; int main() { Teacher teacher("张三", 35, "男", "数学"); teacher.showInfo(); teacher.teach(); cout << endl; Student student("李四", 20, "女", "计算机科学"); student.showInfo(); student.study(); cout << endl; TeacherStudent ts("王五", 28, "男", "英语", "经济管理"); ts.showInfo(); ts.teach(); ts.study(); return 0; } ``` 在这个示例代码中,Person类被定义为虚类,同时Teacher和Student类都继承自Person类,并且都采用虚继承方式。在TeacherStudent类中,通过调用Person、Teacher和Student类的构造函数来初始化类成员变量。需要注意的是,由于Person类是虚类,因此在构造函数中不需要对其进行初始化,该工作由最顶层的派生类(即TeacherStudent类)来完成。同时,在TeacherStudent类中,需要调用Person类的showInfo()函数来显示人的信息,其他的函数则通过调用Teacher和Student类的函数来实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值