• 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;
            }

      

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

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





    主要研究技术:代码生成工具、会员管理系统、客户关系管理软件、病人资料管理软件、Visio二次开发、酒店管理系统、仓库管理系统等共享软件开发
    专注于Winform开发框架/混合式开发框架Web开发框架Bootstrap开发框架微信门户开发框架的研究及应用
      转载请注明出处:
    撰写人:伍华聪  http://www.iqidi.com 
        
  • 相关阅读:
    Eclipse中的快捷键
    xml文件头文件生成策略以及导入约束条件
    HTTP协议状态代码和错误状态含义的解释
    水了一个前端面试 记下问的东西
    整理的一些PHP面试题目
    Magic Index 寻找数组中A[i]=i的位置(原题转自微信号待字闺中)
    【经典算法】寻找最长01字串(转自待字闺中)
    PHP中不用第三个变量交换两个变量的值
    已知一个数组a[N]来构造数组b[N]的有趣算法题
    MySQL安装后默认自带数据库的作用
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/1620416.html
Copyright © 2020-2023  润新知