粘包拆包服务器

服务器:

创建个控制台应用

创建Server.cs

       internal class Server
    {
        TcpListener listen;
        public Server(IPAddress ip,int port) 
        {
           listen = new TcpListener(ip, port);
        }
        public void Start()
        {
            listen.Start(100);
            StartConnect();  
        }
        Dictionary<string,TcpClient> clientDic = new Dictionary<string,TcpClient>();
        public event Action<TcpClient> 有客户端连入的事件; 
        void StartConnect()
        {
            Task.Run(() =>
            {
                while (true) 
                {
                 TcpClient client = listen.AcceptTcpClient();
                 string ip = client.Client.RemoteEndPoint.ToString(); 
                 clientDic.Add(ip, client);
                 有客户端连入的事件?.Invoke(client);
                 startRead(client);//读取粘包的数据,在这个方法进行拆包处理
                }
            });
        }
        //拆包
        public void startRead(TcpClient t1)
        {
            //接受客户端发来的数据
            NetworkStream stream = t1.GetStream();
            string ip = t1.Client.RemoteEndPoint.ToString();
            byte[] bs = new byte[1024 * 1024];
            Task.Run(() =>
            {
                try
                {
                    while (true)
                    {
                      int count=  stream.Read(bs, 0, bs.Length);
                     if (count== 0)
                     {
                            throw new Exception("客户端断了");
                     }
                        //接受数据
                        byte[] body = bs.Take(count).ToArray();
                        string s = Encoding.UTF8.GetString(body);
                        Console.WriteLine("-------------------------------");
                        Console.WriteLine("接收到消息为:"+s);
                        Console.WriteLine("-------------------------------");
                        //进行拆包1 按照特殊符号进行分割,然后遍历,
                        //对应发数据的代码  buffer = Encoding.UTF8.GetBytes("张三你真牛逼了#");
                        //for (int i = 0; i < s.Split('#').Length-1; i++)
                        //{
                        //    Console.WriteLine("接收到消息为:" + s.Split('#')[i]);
                        //    Send(s.Split('#')[i]);
                        //}
                        if (上一个数据半包 != null) //判断是否有半包 如果有,把半包取出来和当前包合并
                        {
                            body =  上一个数据半包.Concat(body).ToArray();//把俩个数组合成一个数组
                            上一个数据半包 = null; //清空半包
                        }
                        //开始拆包 封装一个拆包方法
                        //参数1  当前要拆的数据
                        //参数2 第几个位置开始拆
                        //参数3 当前客户端
                        ChaiBao(body, 0, t1);
                    }
                }
                catch (Exception ex)
                {
                    clientDic.Remove(ip);
                }
            });
        }
        byte[] 上一个数据半包 = null;
        public void ChaiBao(byte[] bytes ,int startIndex, TcpClient client)
        {
            if(startIndex+4>bytes.Length) 
            {
                //如果开始位置加上4大于该数据包的长度时候 说明当前包是一个半包
                //跳过之前元素,取出剩余的数据
                上一个数据半包 = bytes.Skip(startIndex).ToArray();
                return;
            }
            //计算第一个包长度 获取从开始到startIndex之间的长度
            int len = BitConverter.ToInt32 (bytes,startIndex);

            // 取出对应位置包的总体大小
            // 之前的包长度总和
            int abc = len + startIndex + 4;

            //判断当前包是整包还是半包或者是多包
            if (abc == bytes.Length) 
            { //如果之前所有包的大小加上当前数据包大小等于全包的长度,证明当前是一个整包
                 byte[] bs1  = bytes.Skip(startIndex+4).ToArray();
                接受到消息的事件?.Invoke(client, bs1);
               
            }else if (abc < bytes.Length)
            {
                 byte[] bs2 =   bytes.Skip(startIndex+4).Take(len).ToArray();
                接受到消息的事件?.Invoke(client, bs2);
                //如果之前包加上当前包小于全包的长度,说明当前包是多包。如果是多包,再进行拆包
                ChaiBao(bytes, abc, client);
            }
            else
            { //目前是一个半包
                上一个数据半包 = bytes.Skip (startIndex).ToArray();
            }
        }
        public event Action<string> 客户端断开事件; 
        public event Action<TcpClient, byte[]> 接受到消息的事件;
        public Server()
        {
        }
        //群发方法 向所有的客户端发消息
        public void Send(string content)
        {
            byte[] bs = Encoding.UTF8.GetBytes(content);
            foreach (var item in clientDic) //遍历所有的客户端
            {
                item.Value.GetStream().Write(bs, 0, bs.Length);
            }
        }
        //指定给谁发
        public void Send(string content,string ip) {
            byte[] bs = Encoding.UTF8.GetBytes(content);
            //根据ip取出客户端,从字典取
            clientDic[ip].GetStream().Write(bs, 0, bs.Length);
        }
        //指定给哪些客户端发
        //send("你好", ["192.","127"])
        public void Send(string content, string[] ips)
        {
            byte[] bs = Encoding.UTF8.GetBytes(content);
            foreach (var item in clientDic) //所有客户端
            {
                //item.key 键 ip字符串
                //item.value 值 客户端对象
                if (ips.Contains(item.Key))
                {
                    //如果ips数组包含目标客户端
                    item.Value.GetStream().Write(bs, 0, bs.Length);
                }
            }
        }
    }

Program.cs

 internal class Program
 {
    static Server s;
     static void Main(string[] args)
     {
          s = new Server(IPAddress.Any,8080);
         s.有客户端连入的事件 += f1;
         s.接受到消息的事件 += f2;
         s.Start();
         Console.ReadKey();
     }
     public static void f1(TcpClient t1)
     {
         Console.WriteLine(t1.Client.RemoteEndPoint.ToString()+"连接到服务器");
     }
     public static void f2(TcpClient t2, byte[] bs)
     {
         Console.WriteLine(t2.Client.RemoteEndPoint.ToString()+"发来的消息为+++++++++++:"+ Encoding.UTF8.GetString(bs,0,bs.Length));
        
     }
 }

战报拆包开客户端:

窗体使用Button方法

 private void button1_Click(object sender, EventArgs e)
 {
     TcpClient  client = new TcpClient();
     client.Connect("192.168.107.83", 8080);
     NetworkStream stream = client.GetStream();
    

     //发消息
     // 第一种方式发消息的时候 每一条消息的结束时候以特殊符合作为结束标志 例如#、\n等符号
     // 其他端拿到数据之后 对数据进行特殊符号的分割, 

     //byte[] buffer = Encoding.UTF8.GetBytes("我是如来佛祖玉皇大帝观音菩萨指定取西经特派使者花果山水帘洞美猴王齐天大圣孙悟空啊!帅到掉渣!!#");
     //stream.Write(buffer,0,buffer.Length);

     //buffer = Encoding.UTF8.GetBytes("张三你真牛逼了#");
     //stream.Write(buffer, 0, buffer.Length);

     //buffer = Encoding.UTF8.GetBytes("李四你真牛逼了#");
     //stream.Write(buffer, 0, buffer.Length);

     //buffer = Encoding.UTF8.GetBytes("王五你真牛逼了#");
     //stream.Write(buffer, 0, buffer.Length);

     //buffer = Encoding.UTF8.GetBytes("马六你真牛逼了#");
     //stream.Write(buffer, 0, buffer.Length);

     //调用send方法
     Send(stream, "世界");
     Send(stream, "你好埃及文物已到达中国上海");
     Send(stream, "1111");
     Send(stream, "222222222");
 }
      
 //第二种发送数据时候 出现粘包现象,后台处粘包
 //包头当中添加数据长度解决粘包
 //Send(stream,"123")
 void Send(NetworkStream stream,string msg)
 {
     //1定义一个字节数组获取发送数据字节流
     byte[] bs = Encoding.UTF8.GetBytes(msg);

     //2 创建一个内存流,存在内存当中,可以理解为虚拟的文件流
     MemoryStream ms = new MemoryStream();

     //3 创建一个二进制读写对象 写入内存流
     BinaryWriter bw = new BinaryWriter(ms);

     //4写入数据的长度的
     bw.Write(bs.Length);

     //5 写入数据字节数组
     bw.Write(bs);

     //6 发送数据 把数据长度和数据内容同时发给服务器
     stream.Write(ms.ToArray(), 0, ms.ToArray().Length);

     bw.Close();
     ms.Close();
        

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值