艾伟_转载: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;
        }

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

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

 

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

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

代码

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

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

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

        /// 
        /// 监听线程
        /// 
        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);
             }
        }

        /// 
        /// 接收一个Client
        /// 
        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

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

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

        /// 
        /// 增加一个连上来(未注册)的客户端
        /// 
        /// 
        public void AddClient(T client)
        {
            ......
        }

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

        /// 
        /// 当客户端登陆,加入列表后的操作
        /// 
        /// 
        protected virtual void OnAfterClientSignIn(T client)
        {
        }

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

        /// 
        /// 电召客户端登出
        /// 
        /// 
        public void ClientLogout(string userId)
        {
            if (_loginClientList.ContainsKey(userId))
            {
                RadioCallClientLogout(_loginClientList[userId]);
            }
        }

        /// 
        /// 电召客户端登出
        /// 
        /// 
        private void RadioCallClientLogout(T client)
        {
            client.DisConnect();
        }

        /// 
        /// 移除注册的客户端
        /// 
        /// 
        private void RemoveLoginClient(T client)
        {
            ......
        }

        /// 
        /// 移除客户端后的操作
        /// 
        /// 
        protected virtual void OnAfterClientLogout(T client)
        {
        }

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

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

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

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

        /// 
        /// 检查客户端连接
        /// 
        /// 
        private void CheckInvalidClient(Object stateInfo)
        {
            ......
        }

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

        /// 
        /// 增加一条客户端发送数据
        /// 
        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;
        }

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

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

        /// 
        /// 数据处理
        /// 
        /// 
        public virtual void PreDataHandle(PreData data)
        { 
        }

        /// 
        /// 开始数据处理线程
        /// 
        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>();

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

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

        /// 
        /// 数据处理
        /// 
        /// 
        public virtual void DataHandle(T data)
        {
        }

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

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

 

6、PreData、DataTypeKey、Sign

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

代码

    /// 
    /// 预处理的数据
    /// 
    public class PreData
    {
        private string _key;
        private string _content;
        private string _userId;

        public PreData()
        { 
        }

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

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

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

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

 

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

代码

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

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

    }

 

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

代码

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

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

代码

    /// 
    /// 测试数据的实体类信息
    /// 
    public class TestDataRequest
    {
        #region MyRegion

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

        #endregion

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

        /// 
        /// 转换Socket接收到的信息为对象信息
        /// 
        /// Socket接收到的信息
        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++];
            } 
        }

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

 

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

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


时间: 2024-09-19 23:54:50

艾伟_转载:Socket开发探秘--基类及公共类的定义的相关文章

艾伟_转载:Socket开发探秘--数据封包和拆包

在上篇<Socket开发探秘--基类及公共类的定义 >中介绍过,所有受到的数据包,经过系统的预处理后,都会得到一个PreData的数据实体,该实体包含了协议头.协议内容和所属用户的ID.PreData是定义了一个标准的协议数据格式,包含了协议关键字.协议内容.用户标识的内容.前面说了,我们数据是通过实体类作为载体的,我们知道,收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样,我们发送数

Socket开发探秘--基于Json格式的数据协议收发

前面发表过两篇随笔:<Socket开发探秘--基类及公共类的定义>和<Socket开发探秘-- 数据封包和拆包>,介绍了Socket方面的开发.本文继续探讨使用Json格式来作为Socket收 发协议方面的技术问题. 前面说到,收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用 的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样, 我们发送数据的时候,内容部分肯定是按照一定协议规则串联起来的数据,那么我们就需要 把实体转化为

Socket开发探秘--数据封包和拆包

在上篇<Socket开发探秘--基类及公共类的定义 >中介绍过,所有受到的数据包,经过系统的预处理后,都会得到一个PreData的数据实体,该实体包含了协议头.协议内容和所属用户的ID.PreData是定义了一个标准的协议数据格式,包含了协议关键字.协议内容.用户标识的内容.前面说了,我们数据是通过实体类作为载体的,我们知道,收到的Socket数据经过粗略的解析后,就是PreData类型的数据,这个是通用的数据格式,我们需要进一步处理才能转化为所能认识的数据对象(实体类对象),同样,我们发送数

艾伟_转载:WCF安全之EndPointIdentity

最近在做一个项目,应用了WCF进行分布式开发,中间还涉及到消息路由器等,好在有WCF提供了强大的基础支持,当然,本身也作了不少的扩展,实际,我 最关心的是WCF的安全问题,网上不少朋友介绍的WCF的安全也是少得可怜,微软发布的WCF Security GUID好像讲得也只是入门级别的教程,离真正应用到项目中还是有很大的距离,这也让我萌发了分享的想法,今天先放出来占个位置吧,有反对的朋友砖头轻 点,呵~,可以告诉你,WCF的安全里,有很多的小秘密,当然还是要告诉你,并且有此小秘密是要自己去体验后才

艾伟_转载:.NET设计模式:工厂方法模式(Factory Method)

概述 在软件系统中,经常面临着"某个对象"的创建工作,由于需求的变化,这个对象的具体实现经常面临着剧烈的变化,但是它却拥有比较稳定的接口.如何应对这种变化?提供一种封装机制来隔离出"这个易变对象"的变化,从而保持系统中"其它依赖该对象的对象"不随着需求的改变而改变?这就是要说的Factory Method模式了. 意图 定义一个用户创建对象的接口,让子类决定实例化哪一个类.Factory Method使一个类的实例化延迟到其子类. 结构图 生活中

艾伟_转载:C# Design Patterns (3) - Decorator

Decorator Pattern (装饰模式) 装饰模式可「动态」地给一个对象添加一些额外的职责,提供有别于「继承」的另一种选择.就扩展功能而言,Decorator Pattern 透过 Aggregation (聚合) 的特殊应用,降低了类与类之间的耦合度,会比单独使用「继承」生成子类更为灵活. 一般用「继承」来设计子类的做法,会让程序变得较僵硬,其对象的行为,是在「编译」时期就已经「静态」决定的,而且所有的子类,都会继承到相同的行为:然而,若用「装饰模式」以及 UML 的 Aggregat

艾伟_转载:C# Design Patterns (5) - Prototype

本帖介绍 Prototype Pattern (原型模式),并以一个「人事招聘程序」作为示例来说明. --------------------------------------------------------本帖的示例下载点:http://files.cnblogs.com/WizardWu/090713.zip第一个示例为 Console Mode (控制台应用程序) 项目,第二个示例为 ASP.NET 网站项目.执行示例需要 Visual Studio 2008 或 IIS + .NE

艾伟_转载:用C#设计Windows应用程序模板

通常windows应用程序都有相似的特征:控件.菜单.工具条.状态栏等等.每次我们开始作一个新的windows应用程序时都是以相同的事情开始:建立项目,添加控件和事件处理器.如果我们有一个模板,那么我们就可以节约大量的时间了. 在介绍如何建立模板的过程中,将涉及大量的微软.net framework类库的基本知识.如果你没有使用集成开发环境那么本文介绍的模板对你将非常有用,如果你使用了visual studio.net这样的集成开发环境你也可以从中了解控件的工作方式,这对你也是很有用的. 写一个

艾伟_转载:揭示同步块索引(上):从lock开始

大家都知道引用类型对象除实例字段的开销外,还有两个字段的开销:类型指针和同步块索引(SyncBlockIndex).同步块索引这个东西比起它的兄弟类型指针更少受人关注,显得有点冷落,其实此兄功力非凡,在CLR里可谓叱咤风云,很多功能都要借助它来实现. 接下来我会用三篇来介绍同步块索引在.NET中的所作所为. 既然本章副标题是从lock开始,那我就举几个lock的示例: 代码1 1: public class Singleton 2: { 3: private static object lock