做网站广告多少钱,营销网课,wordpress主题代码高亮,临沂建展示网站什么是粘包和拆包
.TCP 是面向连接的#xff0c;面向流的#xff0c;提供高可靠性服务。收发两端#xff08;客户端和服务器端#xff09;都要有一一成对的 socket#xff0c;因此#xff0c;发送端为了将多个发给接收端的包#xff0c;更有效的发给对方#xff0c;使…什么是粘包和拆包
.TCP 是面向连接的面向流的提供高可靠性服务。收发两端客户端和服务器端都要有一一成对的 socket因此发送端为了将多个发给接收端的包更有效的发给对方使用了优化方法Nagle 算法将多次间隔较小且数据量小的数据合并成一个大的数据块然后进行封包。这样做虽然提高了效率但是接收端就难于分辨出完整的数据包了因为面向流的通信是无消息保护边界的
粘包和拆包是网络编程中常遇到的问题主要是因为TCP协议的Nagle算法和接收方缓冲区的不足导致的。 粘包发送方发送的若干包数据到接收方接收时粘在一起导致数据不能正确分割。 拆包发送方发送的包数据在接收方解析时被拆开导致数据不能正确解析。 客户端
直接发消息
第一种方式发消息的时候 每一条消息的结束时候以特殊符合作为结束标志 例如#、\n等符号 // 其他端拿到数据之后 对数据进行特殊符号的分割
TcpClient client new TcpClient();
client.Connect(端口号, 8080);
NetworkStream stream client.GetStream();
Send(stream, 三十而立指的是拥有了30个重装合成旅才能在这个星球上拥有立足的资本);
Send(stream, 四十而不惑指的是拥有40个重装合成旅这个星球上就没有能让我们感到困惑的事);
Send(stream, 五十而知天命指的是拥有50个重装合成旅这个星球的其他国家便能知道我们所说的话便是他的天命);
第二种发送数据时候 出现粘包现象后台处粘包 //包头当中添加数据长度解决粘包
void Send(NetworkStream stream,string msg)
{//1定义一个字节数组获取发送数据字节流byte[] bs Encoding.UTF8.GetBytes(msg);//2 创建一个内存流存在内存当中可以理解为虚拟的文件流MemoryStream ms new MemoryStream();//3 创建一个二进制读写对象 写入内存流BinaryWriter bw new BinaryWriter(ms);//4写入数据的长度的bw.Write(bs.Length);//5 写入数据字节数组bw.Write(bs);//6 发送数据 把数据长度和数据内容同时发给服务器stream.Write(ms.ToArray(), 0, ms.ToArray().Length);bw.Close();ms.Close();}
服务器
Server封装
TcpListener listen;
public Server(IPAddress ip,int port)
{listen new TcpListener(ip, port);
}public void Start()
{listen.Start(100);StartConnect();
}
Dictionarystring,TcpClient clientDic new Dictionarystring,TcpClient();
public event ActionTcpClient 有客户端连入的事件;
void StartConnect()
{Task.Run(() {while (true) {TcpClient client listen.AcceptTcpClient();string ip client.Client.RemoteEndPoint.ToString(); clientDic.Add(ip, client);有客户端连入的事件?.Invoke(client);startRead(client);//读取粘包的数据在这个方法进行拆包处理}});
}
拆包
public void startRead(TcpClient t1)
{//接受客户端发来的数据NetworkStream stream t1.GetStream();string ip t1.Client.RemoteEndPoint.ToString();byte[] bs new byte[1024 * 1024];Task.Run(() {try{while (true){int count stream.Read(bs, 0, bs.Length);if (count 0){throw new Exception(客户端断了);}//接受数据byte[] body bs.Take(count).ToArray();string s Encoding.UTF8.GetString(body);Console.WriteLine(-------------------------------);Console.WriteLine(接收到消息为:s);Console.WriteLine(-------------------------------);if (上一个数据半包 ! null) //判断是否有半包 如果有把半包取出来和当前包合并{body 上一个数据半包.Concat(body).ToArray();//把俩个数组合成一个数组上一个数据半包 null; //清空半包}//开始拆包 封装一个拆包方法//参数1 当前要拆的数据//参数2 第几个位置开始拆//参数3 当前客户端ChaiBao(body, 0, t1);}}catch (Exception ex){clientDic.Remove(ip);}});
} byte[] 上一个数据半包 null;public void ChaiBao(byte[] bytes ,int startIndex, TcpClient client){if(startIndex4bytes.Length) {//如果开始位置加上4大于该数据包的长度时候 说明当前包是一个半包//跳过之前元素取出剩余的数据上一个数据半包 bytes.Skip(startIndex).ToArray();return;}//计算第一个包长度 获取从开始到startIndex之间的长度int len BitConverter.ToInt32 (bytes,startIndex);// 取出对应位置包的总体大小// 之前的包长度总和int abc len startIndex 4;//判断当前包是整包还是半包或者是多包if (abc bytes.Length) { //如果之前所有包的大小加上当前数据包大小等于全包的长度证明当前是一个整包byte[] bs1 bytes.Skip(startIndex4).ToArray();接受到消息的事件?.Invoke(client, bs1);}else if (abc bytes.Length){byte[] bs2 bytes.Skip(startIndex4).Take(len).ToArray();接受到消息的事件?.Invoke(client, bs2);//如果之前包加上当前包小于全包的长度说明当前包是多包。如果是多包再进行拆包ChaiBao(bytes, abc, client);}else{ //目前是一个半包上一个数据半包 bytes.Skip (startIndex).ToArray();}}
群发方法 public event Actionstring 客户端断开事件; public event ActionTcpClient, byte[] 接受到消息的事件;public Server(){}//群发方法 向所有的客户端发消息public void Send(string content){byte[] bs Encoding.UTF8.GetBytes(content);foreach (var item in clientDic) //遍历所有的客户端{item.Value.GetStream().Write(bs, 0, bs.Length);}}//指定给谁发public void Send(string content,string ip) {byte[] bs Encoding.UTF8.GetBytes(content);//根据ip取出客户端从字典取clientDic[ip].GetStream().Write(bs, 0, bs.Length);}//指定给哪些客户端发//send(你好, [192.,127])public void Send(string content, string[] ips){byte[] bs Encoding.UTF8.GetBytes(content);foreach (var item in clientDic) //所有客户端{//item.key 键 ip字符串//item.value 值 客户端对象if (ips.Contains(item.Key)){//如果ips数组包含目标客户端item.Value.GetStream().Write(bs, 0, bs.Length);}}}
Program
static Server s;static void Main(string[] args){s new Server(IPAddress.Any,8080);s.有客户端连入的事件 f1;s.接受到消息的事件 f2;s.Start();Console.ReadKey();}public static void f1(TcpClient t1){Console.WriteLine(t1.Client.RemoteEndPoint.ToString()连接到服务器);}public static void f2(TcpClient t2, byte[] bs){Console.WriteLine(t2.Client.RemoteEndPoint.ToString()发来的消息为: Encoding.UTF8.GetString(bs,0,bs.Length));}