tcp 客户端进行拆包

心跳机制服务器

Server
cs 复制代码
 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("-------------------------------");
                 Send(s);// 群发
             }
         }
         catch (Exception ex)
         {
             clientDic.Remove(ip);
         }
     });
 }
 

 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 复制代码
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));
    
 }

客户端

cs 复制代码
TcpClient client;
 public Form1()
 {
     InitializeComponent();
     client = new TcpClient();
     client.Connect("192.168.107.60",8080);
     // 开始接收数据
     startReceive(client);
     
 }
 void startReceive(TcpClient t1)
 {
     byte[] bs = new byte[1024]; // 缓存区
     Task.Run(() =>
     {
         try
         {
             while (true)
             {
                 int count = t1.GetStream().Read(bs, 0, bs.Length);
                 if(count == 0)
                 {
                     Console.WriteLine("服务器断开");
                     break;
                 }
                 // 去掉数据最后一位占位0
                 byte[] data = bs.Take(count).ToArray();
                 // 需不需要拆包
                 Console.WriteLine(Encoding.UTF8.GetString(data));
                 if(lastPack != null)
                 {
                     data = lastPack.Concat(data).ToArray();
                     lastPack = null;
                 }
                 ChaiBao(data, 0, t1);
             }
         }
         catch (Exception ex)
         {
             Console.WriteLine("客户端断开");
         }
     });
 }
 byte[] lastPack = null;// 上一个半包
 public void ChaiBao(byte[] bs,int startIndex,TcpClient t1)
 {
     if (startIndex + 4 > bs.Length)
     {
         lastPack = bs.Skip(startIndex).ToArray(); // 跳过开始位置取出后面的
         return;
     }

     // 记录第一个包的长度
     int len = BitConverter.ToInt32(bs, startIndex);
     // 取出之前所有包的长度
     int abc = len + startIndex + 4;
     Console.WriteLine(abc + "?????" + bs.Length);
     if(abc == bs.Length)
     {
         byte[] bs1 = bs.Skip(startIndex + 4).ToArray();
         richTextBox1.Invoke((Action)(() =>
         {
             richTextBox1.AppendText(Encoding.UTF8.GetString(bs1) + "\n");
         }));

     }else if (abc < bs.Length)
     {
         byte[] bs2 = bs.Skip(startIndex + 4).Take(len).ToArray();
         richTextBox1.Invoke((Action)(() =>
         {
             richTextBox1.AppendText(Encoding.UTF8.GetString(bs2) + "\n");
             richTextBox1.SelectionStart = richTextBox1.Text.Length;
             richTextBox1.ScrollToCaret();
         }));
         // 继续拆包
         ChaiBao(bs, abc, t1);
     }
     else
     {
         lastPack = bs.Skip(startIndex).ToArray();
     }

 }

直接发消息

调用MemoryStream进行添加数据长度的方法

cs 复制代码
Send(client.GetStream(), "大帅的几种形态");
Send(client.GetStream(), "精致大帅");
Send(client.GetStream(), "油腻大帅");
Send(client.GetStream(), "虚币大帅");
Send(client.GetStream(), "变态大帅");
Send(client.GetStream(), "人妻大帅");

没有使用MemoryStream进行添加数据长度

cs 复制代码
 Send("罗罗诺亚·索隆");
 Send("蒙奇·D·路飞");
 Send("文斯莫克·山治");
 Send("托尼托尼·乔巴");
 Send("妮可·罗宾");

MemoeySteream内存流把数据长度和数据内容写在一个包里

cs 复制代码
void Send(NetworkStream stream, string msg)
{
    byte[] bs = Encoding.UTF8.GetBytes(msg); // 先转成字符节数组
    MemoryStream ms = new MemoryStream();// 创建内存流
    BinaryWriter bw = new BinaryWriter(ms); // 创建内存流
    bw.Write(bs.Length);// 写入长度
    bw.Write(bs);// 写入内容
    stream.Write(ms.ToArray(), 0, ms.ToArray().Length);// 发送内容流
    bw.Close();// 关闭写入对象
    ms.Close();// 关闭内存流
}

void Send(string msg)
{
    byte[] bs = Encoding.UTF8.GetBytes(msg); // 把发的消息转成字节数组
    // BitConverter 转成位数据
    // 把字节数组长度转成位格式数据
    byte[] len = BitConverter.GetBytes(bs.Length);

    byte[] body = len.Concat(bs).ToArray(); // 把数据长度和内容存在一个包里面    
    client.GetStream().Write(body, 0, body.Length);



}
相关推荐
yaoxin5211238 分钟前
第二十九章 TCP 客户端 服务器通信 - 记录的拼接
服务器·网络·tcp/ip
冷眼Σ(-᷅_-᷄๑)25 分钟前
WPF异步UI交互功能的实现方法
c#·wpf
xxtzaaa3 小时前
住宅IP怎么在指纹浏览器设置运营矩阵账号
网络·网络协议·tcp/ip
okmacong3 小时前
3、集线器、交换机、路由器、ip的关系。
网络·tcp/ip·智能路由器
添砖java_8573 小时前
TCP流套接字编程
网络·网络协议·tcp/ip
TimberWill3 小时前
字符串-07-判断两个IP是否属于同一子网
java·网络协议·tcp/ip
ZachOn1y3 小时前
计算机网络:运输层 —— TCP 的超时重传机制
网络·网络协议·tcp/ip·计算机网络·tcp·超时重传
CV大法好4 小时前
刘铁猛C#入门 027 抽象和开闭原则
开发语言·c#
程序员洲洲5 小时前
使用青果代理IP爬取豆瓣TOP250电影数据
网络·网络协议·tcp/ip