TCP解決粘包分包,簡單封裝

1.TCP粘包拆包原因

1.1TCP粘包原因

TCP粘包是指在數據傳輸過程中,短時間發(fā)送多個小數據包被合并成一個大的數據包,

包1
包2包3
包4

1.2TCP分包原因

TCP分包發(fā)送方一次發(fā)送大數據包被拆分成多個小的數據包,

包1_1
包1_2

1.3解決方法

1.包固定長度
2.包結尾特定字符分割如\n
3.消息頭包含包長度

2.具體實現(xiàn)

在這使用特殊字符分割,緩存數組,
1.在拿到多個分隔符時候,通過分隔符解析多條消息。
2.在沒有拿到分隔符時候,繼續(xù)緩存,直到拿到分隔符,保證完整包。

服務器代碼:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

class Server
{
    static string delimiter = "\r\n";
    static List<byte> leftoverData = new List<byte>();

    public void Start()
    {
        int port = 1234;
        TcpListener listener = new TcpListener(IPAddress.Any, port);
        listener.Start();
        Console.WriteLine("Server started. Waiting for connections...");

        TcpClient client = listener.AcceptTcpClient();
        Console.WriteLine("Client connected.");

        // 啟動發(fā)送消息的任務
        Task sendTask = Task.Run(() => SendMessages(client));

        // 啟動接收消息的任務
        Task receiveTask = Task.Run(() => ReceiveMessages(client));

        // 等待發(fā)送和接收任務完成
        Task.WaitAll(sendTask, receiveTask);

        client.Close();
        listener.Stop();
        Console.WriteLine("Server stopped.");
    }

    static void SendMessages(TcpClient client)
    {
        NetworkStream stream = client.GetStream();
        string[] messages = { "Hello", "以下是移除了文本中所有換行和空格的版本:當我們展望未來,科技的進步和創(chuàng)新無疑將引領著人類走向一個全新的世界。以下是一段關于未來科技的隨機文本,長約800字:未來科技的世界將是一個充滿奇跡和無限可能的地方。人工智能、生物技術、量子計算等前沿領域的突破將改變我們的生活方式、推動社會發(fā)展,并帶來前所未有的機遇和挑戰(zhàn)。人工智能將成為未來的核心技術。智能機器人將在各個領域發(fā)揮重要作用,從醫(yī)療保健到交通運輸,從教育到家庭生活,無所不在。智能機器人不僅能夠執(zhí)行各種任務,還能與人類進行交互和合作。他們將成為我們的助手、朋友和教師,為我們提供個性化的服務和支持。生物技術的進步將徹底改變醫(yī)學和生命科學。基因編輯技術的突破將使得人類能夠更精確地治療疾病,并可能解決一些遺傳性疾病的根本問題。仿生技術的發(fā)展將使得人類能夠模仿自然界的優(yōu)秀設計,創(chuàng)造出更強大、更靈活的機器和材料。生物打印技術將使得人體器官的定制化制造成為可能,解決器官移植的瓶頸問題。量子計算的崛起將徹底改變計算機科學和信息技術。量子計算機的運算速度將遠遠超過傳統(tǒng)計算機,解決那些傳統(tǒng)計算機無法應對的復雜問題。密碼學、材料科學、化學合成等領域將從量子計算的進步中獲益。同時,量子通信技術將實現(xiàn)絕對安全的通信,保護個人隱私和商業(yè)機密。能源技術的創(chuàng)新將推動可持續(xù)發(fā)展和應對氣候變化。太陽能、風能等可再生能源將成為主流,取代傳統(tǒng)的化石燃料。能源儲存技術的突破將解決可再生能源的不穩(wěn)定性問題,實現(xiàn)可持續(xù)能源的大規(guī)模應用。核聚變技術的實現(xiàn)將為人類提供清潔、高效、安全的能源來源。虛擬現(xiàn)實和增強現(xiàn)實技術將改變人們的感知和體驗。我們將進入一個全新的數字世界,與虛擬對象進行互動、探索虛擬環(huán)境、參與虛擬社交。", "12345" };

        foreach (string message in messages)
        {
            byte[] data = Encoding.UTF8.GetBytes(message + delimiter);
            stream.Write(data, 0, data.Length);
            Console.WriteLine("Sent message: " + message);

            Task.Delay(1000).Wait(); // 模擬發(fā)送間隔
        }

        // for (int i = 0; i < 5000; i++)
        // {
        //     byte[] data = Encoding.UTF8.GetBytes($"server back:{i}{delimiter}" );
        //     stream.Write(data, 0, data.Length);
        // }
    }

    static async Task ReceiveMessages(TcpClient client)
    {
        NetworkStream stream = client.GetStream();
        byte[] buffer = new byte[4096];
        int bytesRead = 0;

        while (true)
        {
            try
            {
                bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                if (bytesRead == 0)
                {
                    break;
                }

                ProcessReceivedData(buffer, bytesRead);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Error reading data: " + ex.Message);
                break;
            }
        }
    }

    static void ProcessReceivedData(byte[] receivedBytes, int bytesRead)
    {
        leftoverData.AddRange(receivedBytes.Take(bytesRead));

        while (true)
        {
            int delimiterIndex = FindDelimiterIndex(leftoverData, delimiter);

            if (delimiterIndex == -1)
            {
                break; // 沒有找到完整的消息
            }

            byte[] messageBytes = leftoverData.GetRange(0, delimiterIndex).ToArray();
            string message = Encoding.UTF8.GetString(messageBytes);
            Console.WriteLine("Received message: " + message);

            // 在這里進行消息處理邏輯
            // ...

            leftoverData.RemoveRange(0, delimiterIndex + delimiter.Length);
        }
    }

    static int FindDelimiterIndex(List<byte> data)
    {
        byte[] delimiterBytes = Encoding.UTF8.GetBytes(delimiter);
        int delimiterLength = delimiterBytes.Length;

        for (int i = 0; i <= data.Count - delimiterLength; i++)
        {
            bool isDelimiter = true;
            for (int j = 0; j < delimiterLength; j++)
            {
                if (data[i + j] != delimiterBytes[j])
                {
                    isDelimiter = false;
                    break;
                }
            }

            if (isDelimiter)
            {
                return i;
            }
        }

        return -1;
    }

    #region 高效kmp算法

    private static int FindDelimiterIndex(List<byte> data, string delimiter)
    {
        if (data.Count < delimiter.Length)
            return -1;

        int[] prefixTable = CalculatePrefixTable(delimiter);
        int i = 0; // 指向 data 的索引
        int j = 0; // 指向 delimiter 的索引

        while (i < data.Count)
        {
            if (data[i] == delimiter[j])
            {
                i++;
                j++;

                if (j == delimiter.Length)
                    return i - j; // 找到完整的消息,返回起始索引
            }
            else if (j > 0)
            {
                j = prefixTable[j - 1]; // 利用前綴表回退到前一個匹配位置
            }
            else
            {
                i++;
            }
        }

        return -1; // 沒有找到完整的消息
    }

    private static int[] CalculatePrefixTable(string pattern)
    {
        int[] prefixTable = new int[pattern.Length];
        int length = 0;
        int i = 1;

        prefixTable[0] = 0; // 第一個元素的前綴長度為0

        while (i < pattern.Length)
        {
            if (pattern[i] == pattern[length])
            {
                length++;
                prefixTable[i] = length;
                i++;
            }
            else
            {
                if (length != 0)
                {
                    length = prefixTable[length - 1];
                }
                else
                {
                    prefixTable[i] = 0;
                    i++;
                }
            }
        }

        return prefixTable;
    }

    #endregion
}

客戶端代碼

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Unity.VisualScripting;
using UnityEngine;

namespace NFramework.MyTCP
{
    public class MyClientPeer
    {
        static string delimiter = "\r\n"; // 分隔符
        static List<byte> leftoverData = new List<byte>();
        string serverIP = "127.0.0.1";
        int port = 1234;

        private TcpClient client;
        private NetworkStream stream;

        private bool isConnected = false;
        private int maxReconnectAttempts = 7;
        private int currentReconnectAttempt = 0;


        private readonly SemaphoreSlim semaphore = new SemaphoreSlim(1, 1);

        public void InitConnect(string ip, int portT)
        {
            serverIP = ip;
            port = portT;

            isConnected = false;
            currentReconnectAttempt = 0;

            while (!isConnected && currentReconnectAttempt < maxReconnectAttempts)
            {
                try
                {
                    client = new TcpClient(serverIP, port);
                    stream = client.GetStream();
                    isConnected = true;

                    Debug.Log($"Connected to server. serverIP:{serverIP}//port:{port}");

                    // 啟動接收消息的線程
                    Task task = Task.Run(() => ReceiveMessagesAsync(client));
                    //ReceiveMessages();
                }
                catch (Exception ex)
                {
                    Debug.LogError($"Error connecting to server: {ex.Message}. Reconnecting...");
                    Close();

                    // 等待一段時間后嘗試重新連接
                    Task.Delay(TimeSpan.FromSeconds(2));
                    currentReconnectAttempt++;
                }
            }

            if (!isConnected)
            {
                Debug.LogError($"Failed to connect after {maxReconnectAttempts} attempts.");
            }

            Debug.LogError($"connectEnd");
        }


        public void Close()
        {
            isConnected = false;
            if (client != null)
            {
                client.Close();
                Debug.Log("Disconnected from server.");
            }

            if (stream != null)
            {
                stream.Close();
            }
        }

        public void SendMessages(string dataMsg)
        {
            if (client == null || !client.Connected)
            {
                Debug.LogError("Client is not connected. Reconnecting...");
                Reconnect();
                return;
            }

            try
            {
                Debug.LogError($"dataMsg:{dataMsg}");
                byte[] data = Encoding.UTF8.GetBytes(dataMsg + delimiter);
                stream.Write(data, 0, data.Length);
            }
            catch (Exception e)
            {
                Debug.LogError("Error sending data: " + e.Message);
                Close();
            }
        }

        public async Task SendMessagesAsync(string dataMsg)
        {
            if (client == null || !client.Connected)
            {
                Debug.LogError("Client is not connected. Reconnecting...");
                Reconnect();
                return;
            }

            Debug.LogError($"dataMsg:{dataMsg}");
            byte[] data = Encoding.UTF8.GetBytes(dataMsg + delimiter);

            try
            {
                await semaphore.WaitAsync();
                await stream.WriteAsync(data, 0, data.Length);
            }
            catch (Exception ex)
            {
                Debug.LogError("Error sending data: " + ex.Message);
                Close();
            }
            finally
            {
                semaphore.Release();
            }
        }

        private void Reconnect()
        {
            Close();
            InitConnect(serverIP, port);
        }


        async Task ReceiveMessagesAsync(TcpClient client)
        {
            byte[] buffer = new byte[40960];
            int bytesRead = 0;

            while (true)
            {
                try
                {
                    bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        break;
                    }

                    ProcessReceivedData(buffer, bytesRead);
                }
                catch (Exception ex)
                {
                    Debug.LogError("Error reading data: " + ex.Message);
                    Close();
                    break;
                }
            }
        }

        private void ReceiveMessages()
        {
            byte[] buffer = new byte[40960];
            int bytesRead = 0;

            while (true)
            {
                try
                {
                    bytesRead = stream.Read(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        continue;
                    }

                    ProcessReceivedData(buffer, bytesRead);
                }
                catch (Exception ex)
                {
                    Debug.LogError("Error reading data: " + ex.Message);
                    Close();
                    break;
                }
            }
        }

        void ProcessReceivedData(byte[] receivedBytes, int bytesRead)
        {
            List<byte> localLeftoverData = new List<byte>(leftoverData);
            localLeftoverData.AddRange(receivedBytes.Take(bytesRead));

            while (true)
            {
                int delimiterIndex = FindDelimiterIndex(localLeftoverData);
                if (delimiterIndex == -1)
                {
                    break; // 沒有找到完整的消息
                }

                byte[] messageBytes = localLeftoverData.GetRange(0, delimiterIndex).ToArray();
                string message = Encoding.UTF8.GetString(messageBytes);
                Debug.LogError("Received message: " + message);

                // 在這里進行消息處理邏輯
                // ...

                localLeftoverData.RemoveRange(0, delimiterIndex + delimiter.Length);
            }
        }

        int FindDelimiterIndex(List<byte> data)
        {
            byte[] delimiterBytes = Encoding.UTF8.GetBytes(delimiter);
            int delimiterLength = delimiterBytes.Length;

            for (int i = 0; i <= data.Count - delimiterLength; i++)
            {
                bool isDelimiter = true;
                for (int j = 0; j < delimiterLength; j++)
                {
                    if (data[i + j] != delimiterBytes[j])
                    {
                        isDelimiter = false;
                        break;
                    }
                }

                if (isDelimiter)
                {
                    return i;
                }
            }

            return -1;
        }

        #region 高效kmp算法

        //https://www.bilibili.com/video/BV1Qb411h7U6/?vd_source=47e0483e4e2ecd647a3ec667685eb918
        private static int FindDelimiterIndex(List<byte> data, string delimiter)
        {
            if (data.Count < delimiter.Length)
                return -1;

            int[] prefixTable = CalculatePrefixTable(delimiter);
            int i = 0; // 指向 data 的索引
            int j = 0; // 指向 delimiter 的索引

            while (i < data.Count)
            {
                if (data[i] == delimiter[j])
                {
                    i++;
                    j++;

                    if (j == delimiter.Length)
                        return i - j; // 找到完整的消息,返回起始索引
                }
                else if (j > 0)
                {
                    j = prefixTable[j - 1]; // 利用前綴表回退到前一個匹配位置
                }
                else
                {
                    i++;
                }
            }

            return -1; // 沒有找到完整的消息
        }

        private static int[] CalculatePrefixTable(string pattern)
        {
            int[] prefixTable = new int[pattern.Length];
            int length = 0;
            int i = 1;

            prefixTable[0] = 0; // 第一個元素的前綴長度為0

            while (i < pattern.Length)
            {
                if (pattern[i] == pattern[length])
                {
                    length++;
                    prefixTable[i] = length;
                    i++;
                }
                else
                {
                    if (length != 0)
                    {
                        length = prefixTable[length - 1];
                    }
                    else
                    {
                        prefixTable[i] = 0;
                        i++;
                    }
                }
            }

            return prefixTable;
        }

        #endregion
    }
}

客戶端使用

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using NFramework.MyTCP;
using UnityEngine;

public class MyClient : MonoBehaviour
{
    private MyClientPeer myClientPeer;

    // Start is called before the first frame update
    void Start()
    {
        int port = 1234;
        string serverIP = "127.0.0.1";
        myClientPeer = new MyClientPeer();
        myClientPeer.InitConnect(serverIP, port);
        Debug.LogError("1111111");
    }


    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.A))
        {
            Send();
        }
    }

    private async void Send()
    {
        string msg =
            @"以下是移除了文本中所有換行和空格的版本:當我們展望未來,科技的進步和創(chuàng)新無疑將引領著人類走向一個全新的世界。以下是一段關于未來科技的隨機文本,長約800字:未來科技的世界將是一個充滿奇跡和無限可能的地方。人工智能、生物技術、量子計算等前沿領域的突破將改變我們的生活方式、推動社會發(fā)展,并帶來前所未有的機遇和挑戰(zhàn)。人工智能將成為未來的核心技術。智能機器人將在各個領域發(fā)揮重要作用,從醫(yī)療保健到交通運輸,從教育到家庭生活,無所不在。智能機器人不僅能夠執(zhí)行各種任務,還能與人類進行交互和合作。他們將成為我們的助手、朋友和教師,為我們提供個性化的服務和支持。生物技術的進步將徹底改變醫(yī)學和生命科學。基因編輯技術的突破將使得人類能夠更精確地治療疾病,并可能解決一些遺傳性疾病的根本問題。仿生技術的發(fā)展將使得人類能夠模仿自然界的優(yōu)秀設計,創(chuàng)造出更強大、更靈活的機器和材料。生物打印技術將使得人體器官的定制化制造成為可能,解決器官移植的瓶頸問題。量子計算的崛起將徹底改變計算機科學和信息技術。量子計算機的運算速度將遠遠超過傳統(tǒng)計算機,解決那些傳統(tǒng)計算機無法應對的復雜問題。密碼學、材料科學、化學合成等領域將從量子計算的進步中獲益。同時,量子通信技術將實現(xiàn)絕對安全的通信,保護個人隱私和商業(yè)機密。能源技術的創(chuàng)新將推動可持續(xù)發(fā)展和應對氣候變化。太陽能、風能等可再生能源將成為主流,取代傳統(tǒng)的化石燃料。能源儲存技術的突破將解決可再生能源的不穩(wěn)定性問題,實現(xiàn)可持續(xù)能源的大規(guī)模應用。核聚變技術的實現(xiàn)將為人類提供清潔、高效、安全的能源來源。虛擬現(xiàn)實和增強現(xiàn)實技術將改變人們的感知和體驗。我們將進入一個全新的數字世界,與虛擬對象進行互動、探索虛擬環(huán)境、參與虛擬社交。增強現(xiàn)實技術將把數字信息與現(xiàn)實世界相結合,為我們提供更豐富的信息和體驗。未來的城市將變得更加智能和可持續(xù)。智能交通系統(tǒng)將實現(xiàn)無縫連接和高效管理,減少擁堵和排放。智能家居將為我們提供舒適、安全、節(jié)能的居住環(huán)境。智能城市規(guī)劃將通過科技手段優(yōu)化資源分配和供應鏈管理,提高城市的可持續(xù)性和生活質量。這只是未來科技世界的一小部分可能性,科技的創(chuàng)新和進步將繼續(xù)超出我們的想抱歉,由于限制,我無法移除所有換行和空格,因為這將使文本變得無法閱讀。我可以刪除多余的空格和換行,以提高文本的緊湊性,如果您愿意的話。請確認是否需要進行此操作。當我們展望未來,科技的進步和創(chuàng)新無疑將引領著人類走向一個全新的世界。以下是一段關于未來科技的隨機文本,長約800字:未來科技的世界將是一個充滿奇跡和無限可能的地方。人工智能、生物技術、量子計算等前沿領域的突破將改變我們的生活方式、推動社會發(fā)展,并帶來前所未有的機遇和挑戰(zhàn)。人工智能將成為未來的核心技術。智能機器人將在各個領域發(fā)揮重要作用,從醫(yī)療保健到交通運輸,從教育到家庭生活";


        for (int i = 0; i < 9000; i++)
        {
            await myClientPeer.SendMessagesAsync($"你好:==>{i}");
        }


        await myClientPeer.SendMessagesAsync(msg);
    }

    private void OnDestroy()
    {
        myClientPeer.Close();
    }
}
image.png
image.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容