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);



}
相关推荐
星辰徐哥41 分钟前
C++网络编程:TCP服务器与客户端的实现
网络·c++·tcp/ip
初九之潜龙勿用42 分钟前
C# 解决“因为算法不同,客户端和服务器无法通信”的问题
服务器·开发语言·网络协议·网络安全·c#
星辰徐哥1 小时前
C语言网络编程:TCP/IP协议栈、套接字、服务器/客户端通信深度解析
c语言·网络·tcp/ip
net3m333 小时前
C#插件化架构(Plugin Architecture)或 可插拔架构,根据产品类型编码的不同自动路由到目标函数,而无需为每个产品都编码相应的代码!!
重构·c#
水深00安东尼4 小时前
C#猜数字小游戏
开发语言·c#
雨声不在6 小时前
IP路由表(ip rule)修改
网络·网络协议·tcp/ip
无风听海6 小时前
.NET10之C# Extension Members深入分析
大数据·c#·.net·extensionmember
唐青枫7 小时前
C#.NET 分布式事务 深入解析:TCC、Saga、Outbox 与落地取舍
c#·.net
yuweiade7 小时前
如何查询SQL Server数据库服务器的IP地址
服务器·数据库·tcp/ip
人工智能AI技术7 小时前
ML.NET + 1-bit LLM:在 C# 上位机实现仅 1GB 内存的本地 AI 推理
人工智能·c#