解决TCP传输黏包的问题(以C#为例)

因为TCP是基于Stream的方式传输,所以会有封包沾黏或读取不完全的问题
解决方式就是在封包最前面附带资料长度的资讯
这个解决方法是不管什么程式语言都能适用

在这边我将以C#举例,Python例子在这

开启Visual Studio,建立两个专案,分别是Server和Client,由Client不断地传送讯息给Server
我这边使用.NET 6.0做示範

Server端

#pragma warning disable CS8618 //关掉编译器的警告资讯using System.Net;using System.Net.Sockets;using System.Text;//设定封包格式。不一定要用struct,这边选它只是为了让资料结构更好看public class PacketProtocol{//https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-typespublic int Length;//4 Bytespublic byte[] Data;//因为Length使用int型态,所以最大允许的资料数是2,147,483,647}public class Server{//使用委派和事件把接收到的资料传递出去public delegate void DReceive(string Message);public event DReceive Receive;private Socket server;//建立Server端的开口private Socket socket;//一旦有目标连到server的开口,就分配给这个物件private Thread threadAccept;//因为程式会在等待连线中停住,所以需要使用Thread等非同步方式去处理private Thread threadReceive;private bool FLAG_RECEIVE = true;public Server(int port, int allowNumber){//InterNetwork: IPv4//Stream: 因为TCP是串流方式传输,所以这边要选择Stream//TCP: 建立TCP连线server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);server.Bind(new IPEndPoint(IPAddress.Any, port));//绑定连线参数server.Listen(allowNumber);//传入的参数代表允许多少个目标连入threadAccept = new Thread(Accept);threadAccept.Start();}~Server(){Close();}private void Accept(){try{Console.WriteLine("Wait Connect...");socket = server.Accept();server.Close();//若不需要再接收其他连线则可以关掉server这个socket物件Console.WriteLine("Connect SUCCESS");//準备接收资料threadReceive = new Thread(ReceiveMessage);threadReceive.Start();}catch (SocketException se){Console.WriteLine(se.ToString());}}public void Close(){server.Close();socket.Close();FLAG_RECEIVE = false;}public bool IsConnected{get{if (socket == null) return false;else return socket.Connected;}}private void ReceiveMessage(){while(FLAG_RECEIVE){if (Receive == null) continue;//只有设定Receive事件后才开始处理接收事件PacketProtocol data = new PacketProtocol();//先接收资料长度资讯byte[] length = new byte[4];//因为int是4bytes大小socket.Receive(length);data.Length = BitConverter.ToInt32(length, 0);//接收数据资料data.Data = new byte[data.Length];socket.Receive(data.Data);//将接收的资料转为字串后,透过事件发送出去Receive(Encoding.UTF8.GetString(data.Data));}}}public class Process{public static void Main(){Server server = new Server(8080, 1);server.Receive += ReceiveMessage;Console.ReadKey();//pauseserver.Receive -= ReceiveMessage;server.Close();}private static void ReceiveMessage(string Message){Console.WriteLine(Message);}}

Client端

#pragma warning disable CS8618 //关掉警告资讯using System.Net;using System.Net.Sockets;using System.Text;//设定封包格式。不一定要用struct,这边选它只是为了让资料结构更好看public class PacketProtocol{//https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-typespublic int Length;//4 Bytespublic byte[] Data;//因为Length使用int型态,所以最大允许的资料数是2,147,483,647}public class Client{private Socket socket;public Client(string ip, int port){//InterNetwork: IPv4//Stream: 因为TCP是串流方式传输,所以这边要选择Stream//TCP: 建立TCP连线socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);Console.WriteLine("Wait Connect...");socket.Connect(ip, port);//这里只考虑Server先开启,再开Client的状况Console.WriteLine("Connect SUCCESS");}~Client(){Close();}public void Close(){socket.Close();}public bool IsConnected{get{if (socket == null) return false;else return socket.Connected;}}public void Send(string message){//只有连线成功才开始传递资料if (socket.Connected){PacketProtocol packet = new PacketProtocol();packet.Data = Encoding.UTF8.GetBytes(message);packet.Length = packet.Data.Length;byte[] data = PacketProtocolToBytes(packet);socket.Send(data);}}private byte[] PacketProtocolToBytes(PacketProtocol data){//将封包资料转换为Byte阵列byte[] bytes;using (MemoryStream ms = new MemoryStream()){using (BinaryWriter bw = new BinaryWriter(ms)){//先写入资料长度,再写入资料//写入顺序错误会导致解析失败bw.Write(data.Length);bw.Write(data.Data);bytes = ms.ToArray();}}return bytes;}}public class Process{public static void Main(){Client client = new Client("127.0.0.1", 8080);while(client.IsConnected){client.Send("Hello Server!");}Console.ReadKey();//pauseclient.Close();}}

关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章