2025年Unity丛林战争学习02_TCP_解决分包和黏包的问题

Unity丛林战争学习02_TCP_解决分包和黏包的问题1 分包和黏包 分包 当发送的消息过长时会出现分包的情况 即一个消息分多次接收 黏包 当发送的消息过短时会出现黏包的情况 即多个消息合在一起接收 2 导致的问题 分包 UTF 8 使用一至四个字节为每个字符编码 比如有一个字符有三个自己 它的一个字节被分包到了上一个包中

大家好,我是讯享网,很高兴认识大家。

1.分包和黏包
分包:当发送的消息过长时会出现分包的情况,即一个消息分多次接收。
黏包:当发送的消息过短时会出现黏包的情况,即多个消息合在一起接收。

2.导致的问题
分包:UTF-8 使用一至四个字节为每个字符编码,比如有一个字符有三个自己,它的一个字节被分包到了上一个包中,另外两个字节被分配到下一个包中,这样每接收一个包后使用System.Text.Encoding.UTF8.ToString之后,就会出现上一个包结尾出现乱码,下个包开头出现乱码的情况
黏包:接收方不知道一次提取多少个字节。

3.改进思路
思路:发送方每次发送消息的时候在消息前面加上一个int类型的数据,表示该消息的长度,接收方就可以根据该int数据进行提取后面对应长度,转换为字符串。

4.发送方(客户端)

定义下面这样一个类,给每一条消息前面加上长度信息(int):

 class Message { 
    public static byte[] PackageData(string message) { 
    byte[] originalByte = Encoding.UTF8.GetBytes(message); byte[] sizeByte = BitConverter.GetBytes(originalByte.Length); return (sizeByte.Concat(originalByte).ToArray()); } } 

讯享网

发送的时候就可以发送处理好的消息,即:

讯享网clientSocket.Send(Message.PackageData(sendMessage)); 

整个客户端代码

Message类(处理消息:给消息前加上长度)


讯享网

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace clientProject { 
    class Message { 
    public static byte[] PackageData(string message) { 
    byte[] originalByte = Encoding.UTF8.GetBytes(message); byte[] sizeByte = BitConverter.GetBytes(originalByte.Length); return (sizeByte.Concat(originalByte).ToArray()); } } } 

Program.cs(对处理好的消息进行发送)

讯享网using System; using System.Net.Sockets; using System.Net; namespace clientProject { 
    class Program { 
    static void Main(string[] args) { 
    //创建客户端套接字 Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //连接服务器 clientSocket.Connect(new IPEndPoint(IPAddress.Parse("192.168.137.1"), 13001)); //接收服务器的一条消息 byte[] messageRecieve = new byte[1024]; int size = clientSocket.Receive(messageRecieve); string messageRecieveStr = System.Text.Encoding.UTF8.GetString(messageRecieve,0,size); Console.WriteLine(messageRecieveStr); //连续发送短包 string sendMessage = null; for (int i=0;i<100;i++) { 
    sendMessage = i.ToString(); clientSocket.Send(Message.PackageData(sendMessage)); } Console.ReadKey(); clientSocket.Close(); } } } 

5.接收方(服务器端)

定义一个类,对接收的消息进行存储和解析:

 class Message { 
    //存储接受到的数据 byte[] data = new byte[1024]; public byte[] Data { 
    get { 
    return data; } //set { data = value; } } //下一次应该从哪存放 int startIndex = 0; public int StartIndex { 
    get { 
    return startIndex; } //set { startIndex = value; } } public void AddStartIndex(int num) { 
    startIndex += num; } /// <summary> /// 返回剩余存储长度 /// </summary> /// <returns></returns> public int GetRestLength() { 
    return (data.Length - startIndex); } /// <summary> /// 解析消息,解析到一条完整的消息就输出,然后把后面的数据前移覆盖已输出的消息 /// </summary> public void ParsingData() { 
    while (true) { 
    //data内容没有四个字节(标记的长度) if (startIndex <= 4) break; // 具体消息的内容长度 int count = BitConverter.ToInt32(data, 0); //data内容包含一次发送的内容 if (startIndex - 4 >= count) { 
    //输出消息内容 string str = Encoding.UTF8.GetString(data, 4, count); Console.WriteLine("从客户端发来:" + str); //把未输出的内容前移,计算startIndex Array.Copy(data, 4 + count, data, 0, startIndex - count); startIndex = startIndex - count - 4; } //data内容不及一次发送的内容 else { 
    break; } } } 

在Program.cs里面,接收时,我们对消息进行存储:

讯享网clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.GetRestLength(), SocketFlags.None, RecieveMessage, clientSocket); 

注:存储到Message类的对象的data里,StartIndex表示我应该存放的位置,也就是data存放内容的长度。

然后解析数据,对StartIndex进行更新:

msg.AddStartIndex(size); msg.ParsingData(); 

整个代码:
Message类(对消息进行存储,根据标记好的长度输出消息,并用后面未输出的消息覆盖掉已输出的消息)

讯享网using System; using System.Collections.Generic; using System.Text; namespace SeverProject { 
    class Message { 
    //存储接受到的数据 byte[] data = new byte[1024]; public byte[] Data { 
    get { 
    return data; } //set { data = value; } } //下一次应该从哪存放 int startIndex = 0; public int StartIndex { 
    get { 
    return startIndex; } //set { startIndex = value; } } public void AddStartIndex(int num) { 
    startIndex += num; } /// <summary> /// 返回剩余存储长度 /// </summary> /// <returns></returns> public int GetRestLength() { 
    return (data.Length - startIndex); } /// <summary> /// 解析消息,解析到一条完整的消息就输出,然后把后面的数据前移覆盖已输出的消息 /// </summary> public void ParsingData() { 
    while (true) { 
    //data内容没有四个字节(标记的长度) if (startIndex <= 4) break; // 具体消息的内容长度 int count = BitConverter.ToInt32(data, 0); //data内容包含一次发送的内容 if (startIndex - 4 >= count) { 
    //输出消息内容 string str = Encoding.UTF8.GetString(data, 4, count); Console.WriteLine("从客户端发来:" + str); //把未输出的内容前移,计算startIndex Array.Copy(data, 4 + count, data, 0, startIndex - count); startIndex = startIndex - count - 4; } //data内容不及一次发送的内容 else { 
    break; } } } } } 

Program.cs(接收消息后,存放到Message.data后面空白区域,即StartIndex索引处,调用解析方法,最后更新StarIndex)

using System; using System.Net.Sockets; using System.Net; namespace SeverProject { 
    class Program { 
    static Message msg = new Message(); static byte[] messageRecieve = new byte[1024]; static void Main(string[] args) { 
    //创建服务器端套接字 Socket severSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //绑定套接字 IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Parse("192.168.137.1"), 13001); severSocket.Bind(iPEndPoint); //监听连接 severSocket.Listen(0); //异步接受连接,返回客户端套接字 //Socket clientSocket = severSocket.Accept(); severSocket.BeginAccept(AcceptCallBack, severSocket); //暂停主线程 Console.ReadKey(); } static void AcceptCallBack(IAsyncResult ar) { 
    Socket severSocket = (Socket)ar.AsyncState; Socket clientSocket = severSocket.EndAccept(ar); //向客户端发送一条消息 string message = "Hello Client"; clientSocket.Send(System.Text.Encoding.UTF8.GetBytes(message)); //异步接收消息 clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.GetRestLength(), SocketFlags.None, RecieveMessage, clientSocket); //异步连接其他客户端 severSocket.BeginAccept(AcceptCallBack, severSocket); } static void RecieveMessage(IAsyncResult ar) { 
    Socket clientSocket = (Socket)ar.AsyncState; try { 
    int size = clientSocket.EndReceive(ar); string recieveStr = System.Text.Encoding.UTF8.GetString(msg.Data, 0, size); if (recieveStr.Length == 0) return; msg.AddStartIndex(size); msg.ParsingData(); clientSocket.BeginReceive(msg.Data, msg.StartIndex, msg.GetRestLength(), SocketFlags.None, RecieveMessage, clientSocket); } catch (Exception e) { 
    Console.WriteLine(e); clientSocket.Close(); } } } } 
小讯
上一篇 2025-01-05 17:02
下一篇 2025-03-04 16:24

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/48509.html