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