Socket解决粘包问题2

在AsynServer中对接收函数增加接收判断,如果收到客户端发送的请求信息,则发送10个测试包给发送端,否则继续接收,修改后的接收代码如下:

        private void AsynReceive()
        {
            byte[] data = new byte[1024];//接收缓存
            string receiveStr;
            string[] sendArr = PackageBuilder.BuildPackage(10);//生成发送数组,10个包
            socket.BeginReceive(data, 0, data.Length, SocketFlags.None, asyncResult => {
                int length = socket.EndReceive(asyncResult);

                receiveStr = Encoding.ASCII.GetString(data, 0, length);//获取缓存中的信息
                                                                       //    Console.WriteLine(receiveStr);

                if (receiveStr == "1")      //标志字符‘1‘,如果收到1,则发送测试包给客户端,如果不是1,继续接受
                {
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine("第{0}次发送:",i);
                        AsynSend(sendArr[i]);
                   //     Thread.Sleep(200);
                    }

                }
                else
                    AsynReceive();
            }, null);

        }

View Cosde

其中if (receiveStr == "1")是接收判断,如果收到客户端发来的1,则发送测试包给客户端,如果不是1,继续接收。因为是异步发送,所以Console.WriteLine("第{0}次发送:",i);显示可能和发送数据不同步,但肯定是发送了10次。

测试一下,发送10次,接收端收到结果如下:

发了10次,而只收了8次,从上图中我们可以发现第6次和第7次发生了粘包,两个包被当作1个包接收了,如果你编写解包程序时不考虑粘包,那么解包循环在第7次解包时会抛异常。当然一种快捷的避免粘包方式是在发送函数的 AsynSend(sendArr[i]);下面加上Thread.Sleep(200);减少发送频率,但这不是长久之计,接下来我们进入客户端来处理粘包问题。

客户端主要是修改了SyncReceive方法,使用StringBuilder来做接收,因为StringBuilder较于string而言,增加字符串,删除字符串的效率比较高,代码如下:

 string[] receiveArr = new string[10];//用于存储接收到的数据
        int arri=0;//数组位序
        public virtual void SyncReceive()
        {
            //StringBuilder sb = new StringBuilder(1024*1024);
            StringBuilder receiveSb = new StringBuilder();      //接收字符串buffer
            string receiveStr;            //解包过程中用于 中间处理
            int index;       //位序,用于解包
            int dataLength; //存储接收包中的数据长度
            int i = 0;
            Thread th = new Thread(() =>
            {
                while (receiveFlag)
                {
                    byte[] buffer = new byte[1024];
                    int r = socket.Receive(buffer);
                    string str = Encoding.ASCII.GetString(buffer, 0, r);    //只是用来显示
                    Console.WriteLine("第{0}次收到数据:{1}",i++,str);
                    Console.WriteLine();

                    receiveSb.Append(str);              //存储接收字符串,可能存多个包
                    receiveStr = receiveSb.ToString();
                    index = receiveStr.IndexOf("data:");//可能有多个"data"

                    while (index > 1)
                    {
                        dataLength = int.Parse(receiveStr.Substring(index - 2, 2));//数据长度规定为2个字节
                        receiveArr[arri] = receiveStr.Substring(index, dataLength);//保存数据到数组中
                        Console.WriteLine("保存的数据数组[{0}]:{1}",arri,receiveArr[arri++]);
                        receiveSb.Remove(0, 10 + dataLength);//包头+数据长度字节共10个字节"HEAD|H1|38",后面是数据"data:xxxxxxxxxxxxxxxxxxxx"
                        receiveStr = receiveSb.ToString();
                        index = receiveStr.IndexOf("data:");//寻找下一个"data",如果没有跳出循环
                    }

                        //sb.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, r));
                    }
            });
            th.Start();

        }

方法上面加了两个全局变量,receiveArr是一个数组,用于保存接收到的实时数据,这些数据可以用于前台展示,也可以直接保存到数据库;arri是数组位序,运行结果如下:

如图所示,客户端接收了7次数据,第一次有4个包粘在了一起,但我们通过合适的解包,依然将10次数据分开保存在字符串数组中。

客户端要先发送"1"标志给客户端才能执行上面的程序,     SynSend("1");

程序的源码下载地址:

链接:http://pan.baidu.com/s/1nvfa8lF 密码:zjoa

粘包的处理是比较简单的,更麻烦的是分包,虽然不常见,但也要考虑,后面考虑做一下分包的处理,程序写的比较毛糙,如有不足之处希望大家指出。

时间: 2024-08-09 08:48:49

Socket解决粘包问题2的相关文章

Socket解决粘包问题1

粘包是指发送端发送的包速度过快,到接收端那边多包并成一个包的现象,比如发送端连续10次发送1个字符'a',因为发送的速度很快,接收端可能一次就收到了10个字符'aaaaaaaaaa',这就是接收端的粘包. 可能我们在平时练习时没觉的粘包有什么危害,或者通过把发送端发送的速率调慢来解决粘包,但在实时通信中,发送端常常是单片机或者其他系统的信息采集机,它们的发送速率是无法控制的,如果不解决接收端的粘包问题,我们无法获得正常的信息. 就以我自己正在做的项目来说,接收端是一台单频指标测量仪,它会把当前测

c# socket 解决粘包,半包

处理原理: 半包:即一条消息底层分几次发送,先有个头包读取整条消息的长度,当不满足长度时,将消息临时缓存起来,直到满足长度再解码 粘包:两条完整/不完整消息粘在一起,一般是解码完上一条消息,然后再判断是否有剩余字节,有的话缓存起来,循环半包处理 客户端接收代码: private void callReceived(object sender, SocketAsyncEventArgs args) { var socket = sender as Socket; var bb = args.Use

socket tcp 粘包解决

何为粘包: 先看代码 session=socket.socket(socket.AF_INET,socket.SOCK_STREAM) 在定义socket对象的时候 有两个参数 一个是   socket地址家族,另一个是处理类型socket.SOCK_STREAM,注意是  'stream':流 那既然是流处理类型,理解上就是 水流式  处理数据. 这个时候数据是没有边界(也就是没有从头开始,到哪里)的概念就像下图 现在执行命令很正常: 执行了一个cat /etc/passwd   , 也能显示

粘包产生的原因 socket 基于tcp实现远程执行命令(解决粘包)low

# 粘包产生的原因 # 粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的. # 基于tcp协议的套接字会有粘包现象,而基于udp协议的套接字不会产生粘包现象 # tcp是基于数据流的,于是收发的消息不能为空,这就需要在客户端和服务端都添加空消息的处理机制,防止程序卡住:而udp是基于数据报的,即使你输入的是空内容,那也不是空消息,udp协议会帮你封装上消息头(ip+端口的方式),这样就有了消息办界 # 两种情况下会发生粘包 # 1.发送端需要等缓冲区满才发送

socket大文件传输(解决粘包)

解决粘包 模块struct struct.pack(type,num) type:是num的类型 num :是一个数字 r = struct.pack 把一个数字打包成一个四字节的bytes struct.unpack(type,r) 功能:解包,把r解成原数字,结果是一个元组,原数字在元组的下标位0的位置 #解决粘包:原理是在服务器接收到字典长度后,根据字典长度去recv字典中的内容,就不会造成recv最后一次接收完后剩下的空间留给部分文件内容所造成字典内容和文件内容黏在一起 #实现文件的上传

NetworkComms c#通信框架与Java的Netty框架通信 解决粘包问题

上次写了一篇文章  基于networkcomms V3通信框架的c#服务器与java客户端进行通信之Protobuf探讨 其中没有解决粘包问题,抛砖引玉,文章得到了失足程序员 老师的点评,并给出了解决方案:[最多评论]java netty socket库和自定义C#socket库利用protobuf进行通信完整实例(10/591) » 于是马上开始学习,并把c#服务器端换成了我比较熟悉的networkcomms v3 c#通信框架(商业版,本文并不提供) ,以方便与已经存在的系统进行整合. 客户

10.python网络编程(解决粘包问题 part 2)

一.什么时候会产生粘包现象. 只有在使用tcp协议的情况下才会产生粘包现象!udp协议永远不会! 发送端可以1k1k的把数据发送出去,接收端,可以2k2k的的去接收数据,一次可能会接收3k,也有可能1次接收6k. TCP协议是面向流的协议,这也是容易出现粘包问题的原因.而UDP是面向消息的协议,每个UDP段都是一条消息,应用程序必须以消息为单位提取数据,不能一次提取任意字节的数据,这一点和TCP是很不同的.怎样定义消息呢?可以认为对方一次性write/send的数据为一个消息,需要明白的是当对方

Python-socket发送文件并解决粘包问题

服务器端要先根据客户端要下载的文件进行判断是否存在,还要根据文件大小来进行传送,最后还要比对文件的md5值来判断传送的文件是否正确,通过判断剩余字节来解决粘包问题 服务器端 # -*- coding:utf-8 -*- __author__ = "MuT6 Sch01aR" import socket import os import hashlib server = socket.socket() server.bind(('127.0.0.1',8080)) server.list

峰哥解决粘包的方式

峰哥解决粘包的方法为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据 struct模块 该模块可以把一个类型,如数字,转成固定长度的bytes >> > struct.pack('i', 1111111111111) ......... struct.error: 'i'formatrequires - 2147483648 <= number <= 2147483647 # 这个是范围 im