本文主要通过使用客户端发送的消息添加消息头,来标明该消息的字节数量
然后服务器端通过解析消息头长度,来分段解析消息内容的方式 解决粘包问题。
具体实现如下:
服务器端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Sockets; using System.Net; namespace TCP服务器端 { public class Program { static void Main(string[] args) { StartServer(); Console.ReadKey(); } #region 跟多个客户端异步接收 static void StartServer() { // 1. 创建服务器端Socket实例 Socket serverSocket = new Socket(AddressFamily.InterNetwork, // 局域网 SocketType.Stream, // 类型:流套接字 ProtocolType.Tcp); // 协议类型:TCP // 2. 指定服务器IP IPAddress ipAddress = IPAddress.Parse("192.168.3.43"); // 3. 规定开放哪些端口 IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888); // 4. 绑定开放的端口 serverSocket.Bind(ipEndPoint); // 绑定地址和端口号 // 5. 开始监听 serverSocket.Listen(0); // 监听端口号 0代表监听数量不限 // 6. 异步监听客户端连接 serverSocket.BeginAccept(AcceptCallBack, serverSocket); // 异步接收连接 } static SpliceByte message = new SpliceByte(); /// <summary> /// 接收连接 /// </summary> /// <param name="result"></param> static void AcceptCallBack(IAsyncResult result) { Socket serverSocket = result.AsyncState as Socket; Socket clientSocket = serverSocket.EndAccept(result); // 向客户端发送一条消息 string msg = "Hello! 你好!"; byte[] data = Encoding.UTF8.GetBytes(msg); // 修改编码为UTF-8 并将字符串转成字节数组 clientSocket.Send(data); serverSocket.BeginAccept(AcceptCallBack, serverSocket); // 异步接收连接 // 7. 异步接收客户端消息 clientSocket.BeginReceive(message.Data, message.UsedCount, message.RemainSize, SocketFlags.None, ReceiveCallBack, clientSocket); // 异步接收消息 } /// <summary> /// 接收消息内容 /// </summary> /// <param name="result"></param> static void ReceiveCallBack(IAsyncResult result) { Socket clientSocket = null; try { clientSocket = result.AsyncState as Socket; int count = clientSocket.EndReceive(result); // 如果接收消息长度为0,则关闭该客户端连接 if (count == 0) { clientSocket.Close(); return; } message.ReadData(count); // 解决粘包问题 clientSocket.BeginReceive(message.Data, message.UsedCount, message.RemainSize, SocketFlags.None, ReceiveCallBack, clientSocket); // 异步接收消息 } catch (System.Exception ex) { Console.WriteLine(ex); if (clientSocket != null) clientSocket.Close(); // 如果客户端非正常关闭,则关闭该客户端连接 } } #endregion } } |
服务器端分离粘包:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TCP服务器端 { class SpliceByte { private byte[] data = new byte[1024]; private int usedCount = 0; public byte[] Data { get { return data; } } public int UsedCount { get { return usedCount; } } public int RemainSize { get { return data.Length - usedCount; } } public void ReadData(int length) { usedCount += length; while (true) { if (usedCount <= 4) return; int count = BitConverter.ToInt32(data, 0); if ((usedCount - 4) >= count) { string s = Encoding.UTF8.GetString(data, 4, count); Console.WriteLine("解析出来一条数据:" + s); Array.Copy(data, count + 4, data, 0, usedCount - 4 - count); usedCount -= count + 4; } else return; } } } } |
客户端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Net.Sockets; using System.Net; namespace TCP客户端 { class Program { static void Main(string[] args) { // 1. 创建Socket实例 Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); // 2. 指定服务器端IP地址, 字符串转换为IP地址类 IPAddress ipAddress = IPAddress.Parse("192.168.3.43"); // 3. 创建IP实体类对象 IP地址+端口号 IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, 8888); // 4. 连接服务器端 clientSocket.Connect(ipEndPoint); // 5. 接收服务器消息 byte[] data = new byte[1024]; int count = clientSocket.Receive(data); string msg = Encoding.UTF8.GetString(data, 0, count); Console.WriteLine(msg); // 6. 向服务器发送消息 while (true) { string s = Console.ReadLine(); if(s == "c") { // 关闭连接 clientSocket.Close(); return; } if (s == "a") { // 发送消息 测试粘包 for (int i = 0; i < 100; i++) { clientSocket.Send(SpliceByte.GetBytes(i.ToString())); // 发送信息 该byte已经过添加头处理 } } } Console.ReadKey(); } } } |
客户端添加消息头:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TCP客户端 { class SpliceByte { /// <summary> /// 将传入的数据转换为byte数组,并在头部加入4字节数组长度 /// </summary> /// <param name="data"></param> /// <returns></returns> public static byte[] GetBytes(string data) { byte[] dataBytes = Encoding.UTF8.GetBytes(data); byte[] lengthBytes = BitConverter.GetBytes(dataBytes.Length); byte[] newBytes = lengthBytes.Concat(dataBytes).ToArray(); return newBytes; } } } |
项目使用版本:VS2015 GitHub下载地址:
https://github.com/654306663/Socket-Fixed-Packet-Splicing
- 本文固定链接: http://www.u3d8.com/?p=1365
- 转载请注明: 网虫虫 在 u3d8.com 发表过