(原创)TCPIP的二次封装,可同时多客户端收发数据



TCPIP多客户端收发数据

研究这个搞了好久,性能绝对没得说,我测试的同时接入200多个客户端内存大约增加90多MB,还是不错的,哈哈!废话不多说进入正文。


这里只讲解服务端,客户端很简单没啥可研究的,说白了懂了服务端,客户端自然明白了。

首先引用命名空间

<span style="font-size:14px;">using System.Net.Sockets;
using System.Net;</span><span style="font-size:14px;">
</span>

设置一个属性Port用于设置TCP的服务端口,代码如下:

<span style="font-size:14px;">        TcpListener objListerer;
        #region 侦听的端口号
        /// <summary>
        /// 侦听的端口号
        /// </summary>
        public int Port
        {
            set
            {
                objListerer = new TcpListener(IPAddress.Any, value);
            }
        }</span>

然后再设置一个方法Start(),方法实现了自动侦听客户端的功能和自动更新登陆信息的功能

<span style="font-size:14px;">        /// <summary>
        /// 启动多线程侦听
        /// </summary>
        public void Start()
        {
            objListerer.Start();
            Thread thread = new Thread(WaitNewConnection);
            thread.IsBackground = true;
            thread.Start(objListerer);
            System.Timers.Timer objTimer = new System.Timers.Timer();
            objTimer.Interval = 1000;
            objTimer.Elapsed += objTimer_Elapsed;
            objTimer.Start();
        }</span>

定时器翻转方法,主要用来更新登陆信息,踢掉超时的链接:

        void objTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            Connectioner[] objcon = objconn.ToArray();
            objconn.CopyTo(objcon, 0);
            Parallel.ForEach(objcon, (item, loop) =>
                {
                    item.登陆时间++;
                    if (item.登陆时间 > outTime)
                    {
                        var t = Task.Factory.StartNew(() => Abort(item)); //终止所有相关任务
                        var t1 = Task.Factory.StartNew(() => objconn.Remove(item));
                        var t2 = Task.Factory.StartNew(() => loop.Stop()); return;
                    }
                });
            ConntionInfo.BeginInvoke(objconn, SendDataOK, "OK");
        }

outTime用来设置超时自动题掉的阈值

        private int outTime = 100;
        /// <summary>
        /// 超时时间设置,默认为100S
        /// </summary>
        public int OutTime
        {
            get { return outTime; }
            set { outTime = value; }
        }

等待新的客户端接入的方法,接入后加入到线程列表中,线程列表有定时器控制,也可手动控制:

        private void WaitNewConnection(object tcpListener)
        {
            TcpListener Listener = tcpListener as TcpListener;
            try
            {
                while (true)
                {
                    TcpClient client = new TcpClient();
                    objListerer.Start();
                    client.Client = objListerer.AcceptSocket();
                    NetworkStream ns = client.GetStream();
                    Thread thead = new Thread(ReceiveData);
                    thead.IsBackground = true;
                    List<object> Info = new List<object>();
                    Info.Add(ns);
                    Info.Add(client);
                    thead.Start(Info);
                    if (!isContain(objconn, client.Client.ToString(), client.Client))//如果不存在则添加一个新的
                    {
                        objconn.Add(new Connectioner() { Key = client.Client.RemoteEndPoint.ToString(), netStream = ns, thread = thead, client = client, 登陆时间 = 0 });
                    }
                }
            }
            catch (Exception)//出错不报
            {
               
            }
        }

判断登陆对象是否存在的方法

<span style="font-size:18px;">        /// <summary>
        /// 判断是否包含该链接
        /// </summary>
        /// <param name="objCOn"></param>
        /// <param name="key"></param>
        /// <param name="socket"></param>
        /// <returns></returns>
        private bool isContain(List<Connectioner> objCOn, string key, Socket socket)
        {
            int number = 0;
            foreach (var item in objCOn)
            {
                if (item.Key == socket.RemoteEndPoint.ToString())
                {
                    number++;
                }
            }
            if (number > 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }</span>


由于在软件中增加了自动识别客户端的功能所以增加了如下属性

此属性用来设置正则表达式,

       public IDFilterClass[] IDFilter
        {
            get;
            set;
        }

正则表达式验证登陆信息类

    /// <summary>
    /// 正则获取ID表达式类
    /// </summary>
    public class IDFilterClass
    {
        /// <summary>
        /// 正则匹配表达式
        /// </summary>
        public string 标准正则 { get; set; }
        /// <summary>
        /// 正则要获取的信息
        /// </summary>
        public string 获取正则 { get; set; }
        /// <summary>
        /// 截取的位置
        /// </summary>
        public int index { get; set; }
        /// <summary>
        /// 要截取的长度
        /// </summary>
        public int length { get; set; }
    }


连接信息类,应该放在前面吧,哈哈

   public class Connectioner
    {
        public string Key { get; set; }
        internal Thread thread { get; set; }
        internal NetworkStream netStream { get; set; }
        internal TcpClient client { get; set; }
        public int 登陆时间 { get; set; }
        public string ID { get; set; }
    }

登陆信息验证方法

        private string Filter(string data)
        {
            if (IDFilter.Count()>0)
            {
                try
                {
                    for (int i = 0; i < IDFilter.Length; i++)
                    {
                        if (Regex.Match(data, IDFilter[i].标准正则).Success)
                        {
                            string regex = Regex.Match(Regex.Match(data, IDFilter[i].标准正则).Value.ToString(), IDFilter[i].获取正则).Value;
                            if (regex != string.Empty)
                            {
                                return regex.Substring(IDFilter[i].index, IDFilter[i].length);
                            }
                        }
                    }
                }
                catch (Exception err)
                {
                    
                    throw new Exception(err.Message);
                }

            }
            return string.Empty;
        }

接收数据方法,此方法会自动通知调用方

       private void ReceiveData(object Stream)
        {
            TcpClient client;
            client = new TcpClient();
            NetworkStream ns = null;
            List<object> objList = Stream as List<object>;
            Parallel.ForEach(objList, item =>
                {
                    if (item is TcpClient)
                    {
                        client = (TcpClient)item;
                    }
                    if (item is NetworkStream)
                    {
                        ns = (NetworkStream)item;
                    }
                });
            try
            {
                while (true)
                {
                    byte[] bytes;
                    bytes = new byte[2048];
                    int length = ns.Read(bytes, 0, bytes.Count());
                    if (length==0)
                    {
                        Thread.Sleep(200);
                        continue;
                    }
                    byte[] NewBytes = new byte[length];
                    Array.Copy(bytes, NewBytes, length);
                    if (length > 0)
                    {
                        Parallel.ForEach(objconn, item =>
                            {
                                if (item.Key == client.Client.RemoteEndPoint.ToString())
                                {
                                    if (Filter(Encoding.ASCII.GetString(NewBytes)) != string.Empty && Filter(Encoding.ASCII.GetString(NewBytes)) != null)
                                    {
                                        item.ID = Convert.ToInt32(高低字节对调(Filter(Encoding.ASCII.GetString(NewBytes))), 16).ToString().PadLeft(10, '0');
                                        NewConnterID.Invoke(item.ID);
                                    }
                                }
                            });
                        MessageByte.BeginInvoke(NewBytes, Encoding.ASCII.GetString(NewBytes), client.Client.RemoteEndPoint.ToString(), ReceiveOK, "OK");
                        RefreshTime(client.Client.RemoteEndPoint.ToString());
                    }
                }
            }
            catch (Exception )
            {
                
            }
        }

如果收到新数据自动更新超时时间默认设置为0


    private void RefreshTime(string key)
        {
            Parallel.ForEach(objconn, item =>
                {
                    if (item.Key == key)
                    {
                        item.登陆时间 = 0;
                    }
                });
        }

下面的代码用于终止线程

     /// <summary>
        /// 终止某个线程
        /// </summary>
        /// <param name="key"></param>
        public void ThreadStop(string key)
        {
            int num = objconn.Count;
                for (int i = 0; i < num; i++)
                {
                    if (objconn[i].Key == key)
                    {
                        Abort(objconn[i]);
                        objconn.Remove(objconn[i]);
                        num--;
                    }
                }
        }
        /// <summary>
        /// 终止所有线程
        /// </summary>
        public void Stop()
        {
            if (objconn.Count!=0)
            {
                int num = objconn.Count;
                for (int i = 0; i < num; i++)
                {
                    Abort(objconn[i]);
                    objconn.Remove(objconn[i]);
                    num--;
                }
                objListerer.Stop();
            }
           
        }


发送消息方法,支持Byte,或者HexByte(十六进制字符串形式的Byte)

       public void SendData(string key, byte[] dataByte = null, string dataHex = null)
        {
            if (dataByte == null && dataHex == null)
            {
                return;
            }
            foreach (var item in objconn)
            {
                if (item.Key == key)
                {
                    if (dataByte != null)
                    {
                        try
                        {
                            item.netStream.BeginWrite(dataByte, 0, dataByte.Length, SendDataOK, "OK");
                        }
                        catch (Exception)
                        {
                            return;
                        }
                    }
                    else
                    {
                        dataByte = HexToHexByte(dataHex).ToArray();
                        try
                        {
                            item.netStream.BeginWrite(dataByte, 0, dataByte.Length, SendDataOK, "OK");
                        }
                        catch (Exception)
                        {
                            return;
                        }
                    }
                }
            }

        }

这个方法是别人写的,比较二逼,不过功能还是可以得

  private List<byte> HexToHexByte(string convertString) // 
        {
            //【格式转换】将文本框的string转换为byte[]类型(ASCII)!
            byte[] bytes_input = Encoding.Default.GetBytes(convertString);
            uint bytes_input_len = (uint)Encoding.Default.GetByteCount(convertString);   //字符个数


            //【过滤无效字符】仅提取符合0~9,A~F,a~f的ASCII值
            byte[] bytes_text_fiter = new byte[65536];
            uint filter_len = 0; for (uint i = 0; i < bytes_input_len; i++)
            {
                if ((bytes_input[i] >= 48 && bytes_input[i] <= 57) || (bytes_input[i] >= 65 && bytes_input[i] <= 70) || (bytes_input[i] >= 97 && bytes_input[i] <= 102))
                { bytes_text_fiter[filter_len++] = bytes_input[i]; }
            }


            //【ASCII 转16进制】
            for (uint i = 0; i < filter_len + 1; i++)
            {
                if (bytes_text_fiter[i] == '0') { bytes_text_fiter[i] = 0x00; }
                else if (bytes_text_fiter[i] == '1') { bytes_text_fiter[i] = 0x01; }
                else if (bytes_text_fiter[i] == '2') { bytes_text_fiter[i] = 0x02; }
                else if (bytes_text_fiter[i] == '3') { bytes_text_fiter[i] = 0x03; }
                else if (bytes_text_fiter[i] == '4') { bytes_text_fiter[i] = 0x04; }
                else if (bytes_text_fiter[i] == '5') { bytes_text_fiter[i] = 0x05; }
                else if (bytes_text_fiter[i] == '6') { bytes_text_fiter[i] = 0x06; }
                else if (bytes_text_fiter[i] == '7') { bytes_text_fiter[i] = 0x07; }
                else if (bytes_text_fiter[i] == '8') { bytes_text_fiter[i] = 0x08; }
                else if (bytes_text_fiter[i] == '9') { bytes_text_fiter[i] = 0x09; }
                else if (bytes_text_fiter[i] == 'a' || bytes_text_fiter[i] == 'A') { bytes_text_fiter[i] = 0x0A; }
                else if (bytes_text_fiter[i] == 'b' || bytes_text_fiter[i] == 'B') { bytes_text_fiter[i] = 0x0B; }
                else if (bytes_text_fiter[i] == 'c' || bytes_text_fiter[i] == 'C') { bytes_text_fiter[i] = 0x0C; }
                else if (bytes_text_fiter[i] == 'd' || bytes_text_fiter[i] == 'D') { bytes_text_fiter[i] = 0x0D; }
                else if (bytes_text_fiter[i] == 'e' || bytes_text_fiter[i] == 'E') { bytes_text_fiter[i] = 0x0E; }
                else if (bytes_text_fiter[i] == 'f' || bytes_text_fiter[i] == 'F') { bytes_text_fiter[i] = 0x0F; }
            }


            //最终有效的16进制格式数据
            byte[] bytes_hex = new byte[65536];
            uint bytes_hex_len = 0;

            //合并前后两个16进制字节存储在byte[]中
            for (int i = 0; i < (filter_len + 1) / 2; i++)
            {
                int sw_tmp = 0;
                sw_tmp = bytes_text_fiter[i * 2];
                sw_tmp <<= 4;
                bytes_text_fiter[i * 2] = (byte)sw_tmp;

                bytes_hex[i] = (byte)(bytes_text_fiter[i * 2] | bytes_text_fiter[i * 2 + 1]);
                bytes_hex_len++;
            }

            //最后一个字节是否需要右移4位
            if (filter_len % 2 == 1)        //查看合并字节前有效字节个数是否是奇数
            {
                uint sw_tmp = 0;
                sw_tmp = bytes_hex[bytes_hex_len - 1];  //将合并后的最有一个字节右移4位
                sw_tmp >>= 4;
                bytes_hex[bytes_hex_len - 1] = (byte)sw_tmp;
            }


            List<byte> list_res = new List<byte>();
            for (uint i = 0; i < (bytes_hex_len); i++)
            {
                list_res.Add(bytes_hex[i]);
            }
            return list_res;
        }

添加事件

  public event NewID NewConnterID;
       /// <summary>
        /// 刷新登陆信息
        /// </summary>
       public event Refresg ConntionInfo;
       /// <summary>
        /// 收到新的消息
        /// </summary>
       public event NewMessage MessageByte;
        TcpListener objListerer;

<span style="font-size:18px;color:#ff0000;">注意下面的代码要写在类的外面</span>
   public delegate void NewMessage(byte[] msgByte,string msgASCII,string key);
    /// <summary>
    /// 有的新的集中器ID
    /// </summary>
    /// <param name="RemoteEndPoint"></param>
    public delegate void NewID(string ID);
    /// <summary>
    /// 有客户端关闭
    /// </summary>
    /// <param name="RemoteEndPoint"></param>
    public delegate void SocketCLose(string RemoteEndPoint);
    /// <summary>
    /// 更新登陆列表
    /// </summary>
    /// <param name="objList"></param>
    public delegate void Refresg(List<Connectioner> objList);
    /// <summary>
    /// TCPIP服务端
    /// </summary>

封装后的方法列表
登陆信息

惊恐500多行的代码,没法讲太细。。。(-。-;)

效果图



使用方法

objStream.Port=this.objStringIP.Port;
                        List<IDFilterClass> objCLass = new List<IDFilterClass>();
                        objCLass.Add(new IDFilterClass { 标准正则 = "[D][E][V][:][0-9a-fA-F]{8}", 获取正则 = "[D][E][V][:][0-9a-fA-F]{8}", index = 4, length = 8 });
                        objCLass.Add(new IDFilterClass { 标准正则 = "[[][E][N][N][P][I][N][G][]][[][L][S][D][0-9a-fA-F]{8}[]]", 获取正则 = "[L][S][D][0-9a-fA-F]{8}", index = 3, length = 8 });
                        objStream.IDFilter = objCLass.ToArray();
                        objStream.Start();

觉得麻烦或者看不懂还是下DLL吧!这个实在

DLL下载地址:http://download.csdn.net/detail/hotmee/9575341




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值