C# Socket TCP 数据合包问题

SomethingJack 2015-03-07 12:45:31
设备媒50MS发送一次数据,一次的数据可能不是完整的数据包.
开头以FF FF FF FF CA CB CC CD 开始,以EA EB EC ED结束 这位一个完整的数据包.

如果 50MS过来一次数据 但是数据分了3次或者N次

第一次 FF FF FF FF CA CB CC CD **
第二次 ** ** **
第三次 EA EB EC ED

如何去合成这个完整的包 根据开始和结束判断。

希望给出伪代码。谢谢各位!
...全文
507 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
小么呀小儿郎 2015-04-11
  • 打赏
  • 举报
回复
引用 14 楼 SomethingJack 的回复:
[quote=引用 13 楼 wyd1520 的回复:] 看这里 有DEMO http://blog.csdn.net/wyd1520/article/details/44153543 这是缓冲区代码


/// <summary>  
    /// 字节缓冲器  
    /// </summary>  
    public class ByteQueue  
    {  
        private List<byte> m_buffer = new List<byte>();  
        private byte[] headBuffer = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, 0xCB, 0xCC, 0xCD }; //包头开始部份  
        private byte[] tailBuffer = new byte[] { 0xEA, 0xEB, 0xEC, 0xED };//包结束部份  
  
        private int IndexOf(List<byte> source, byte[] seach, int startIndex)  
        {  
            string baseStr = BitConverter.ToString(source.ToArray());  
            string searchStr=BitConverter.ToString(seach);  
            return baseStr.IndexOf(searchStr, startIndex) / 3;  
        }  
  
         
        public bool Find()  
        {  
            if (m_buffer.Count == 0)  
                return false;  
            int HeadIndex = IndexOf(m_buffer, headBuffer,0);//找开头的位置  
            if (HeadIndex == -1)  
            {  
                m_buffer.Clear();  
                return false; //没找到  
            }  
            else if (HeadIndex != 0) //不为开头移掉之前的字节  
            {  
                if (HeadIndex > 1)  
                {  
                    m_buffer.RemoveRange(0, HeadIndex);  
                    HeadIndex = 0;  
                }  
            }  
  
  
  
            int TailIndex = IndexOf(m_buffer, tailBuffer, headBuffer.Length); //查找结尾的位置  
  
            if (TailIndex == -1)  
            {  
                //这一步为防止连发一个开头的包后,没发结尾,而又发了一个包头  
                int head = IndexOf(m_buffer, headBuffer, 0);  
                if (head > -1)  
                {  
                    m_buffer.RemoveRange(0, head);  
                }  
                return false;  
            }  
            int packLength = TailIndex - HeadIndex;  
            if (packLength<0) //计算包尾是否与包长度相等  
            {  
                m_buffer.RemoveRange(0, TailIndex + tailBuffer.Length);  
                return false;  
            }  
            return true;  
        }  
  
  
  
  
         
  
        /// <summary>  
        /// 包长度  
        /// </summary>  
        /// <returns></returns>  
        private int GetLength()  
        {  
            int len = IndexOf(m_buffer, tailBuffer, headBuffer.Length) + tailBuffer.Length; //查找结尾的位置  
            return len;  
        }  
        /// <summary>  
        /// 提取数据  
        /// </summary>  
        public byte[]  Dequeue()  
        {  
            int length = GetLength();  
            List<byte> result = m_buffer.Take(length).ToList();//提取数据包  
            result.RemoveRange(0, headBuffer.Length);//移掉开头部份 只保留内容  
            result.RemoveRange(result.Count - tailBuffer.Length, tailBuffer.Length); //移掉结尾 只保留内容  
            m_buffer.RemoveRange(0, length);//缓冲区内容移除  
            return result.ToArray();  
            
        }  
  
        /// <summary>  
        /// 队列数据  
        /// </summary>  
        /// <param name="buffer"></param>  
        public void Enqueue(byte[] buffer)  
        {  
            m_buffer.AddRange(buffer);  
        }  
    }  

非常感谢 最后还有一点不明白 你在串口例子中调用的时候 有两个地方是对应Socket的哪部分?同步或者异步都可以说一下。 这里

private ByteQueue queue = new ByteQueue();  
private void socket_DataReceived(object sender, SerialDataReceivedEventArgs e)  
       {  
           int len = serialPort1.BytesToRead;  
           if (len > 0)  
           {  
               byte[] temp = new byte[len];  
               serialPort1.Read(temp, 0, len);  
               queue.Enqueue(temp);  
               while (queue.Find()) //while可处理同时接收到多个包  
               {  
                   byte[] readBuffer =  queue.Dequeue();  
                   OnReceiveData(readBuffer); //<这里自己写一个委托吧就OK了  
               }  
  
           }  
  
       }  
[/quote] 如果我用的是 socket.BeginReceive(_recvDataBuffer, 0, _recvDataBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveData), client); 那么这个该怎么办?
SomethingJack 2015-03-10
  • 打赏
  • 举报
回复
引用 15 楼 wyd1520 的回复:

 queue.Enqueue(temp);    
               while (queue.Find()) //while可处理同时接收到多个包    
               {    
                   byte[] readBuffer =  queue.Dequeue();    
                   OnReceiveData(readBuffer); //<这里自己写一个委托吧就OK了    
               }    
就这个了,你放到Socket接收的那个代码中 就是int length=socket.Recive(xxx,xxxx) 的后面呀,至于同步异步,看你自己写的接收方式与这个缓冲器没关系的。你只管把Socket收到的内容向里面放就是了,他就能逐个解析出包
昨天根据你的代码 我修改了一下 但是一直报错 不知道哪里有问题 希望帮忙再卡看
ByteQueue queue = new ByteQueue();
            try
            {
                int port = 4001;
                string host = "192.168.0.80";
                IPAddress ip = IPAddress.Parse(host);
                IPEndPoint ipe = new IPEndPoint(ip, port);//把ip和端口转化为IPEndPoint实例
                Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//创建一个Socket
                Console.WriteLine("Conneting...");
                c.Connect(ipe);//连接到服务器
                string recvStr = "";
                byte[] recvBytes = new byte[1024];
                int bytes;
                bytes = c.Receive(recvBytes, recvBytes.Length, 0);//从服务器端接受返回信息
                if (bytes > 0)
                {
                    byte[] temp = new byte[bytes];
                    queue.Enqueue(temp);
                    while (queue.Find()) //while可处理同时接收到多个包    
                    {
                        byte[] readBuffer = queue.Dequeue();
                    }
                }
                c.Close();
            }
            catch (ArgumentNullException e)
            {
                Console.WriteLine("ArgumentNullException: {0}", e);
            }
            catch (SocketException e)
            {
                Console.WriteLine("SocketException: {0}", e);
            }
            Console.Read();
本拉灯 2015-03-09
  • 打赏
  • 举报
回复
引用 楼主 SomethingJack 的回复:
设备媒50MS发送一次数据,一次的数据可能不是完整的数据包. 开头以FF FF FF FF CA CB CC CD 开始,以EA EB EC ED结束 这位一个完整的数据包. 如果 50MS过来一次数据 但是数据分了3次或者N次 第一次 FF FF FF FF CA CB CC CD ** 第二次 ** ** ** 第三次 EA EB EC ED 如何去合成这个完整的包 根据开始和结束判断。 希望给出伪代码。谢谢各位!
你这个包头有没有数据长度体现在里面?把包的完整格式说一下,还是说只有包头与包尾。。
SomethingJack 2015-03-09
  • 打赏
  • 举报
回复
引用 8 楼 networkcomms 的回复:
可以参考我的这篇文章:c#源码分享-TCP通信中的大文件传送
你这个跟我的大致方向都不一样的
於黾 2015-03-09
  • 打赏
  • 举报
回复
收到数据先放List<byte>里啊
本拉灯 2015-03-09
  • 打赏
  • 举报
回复

 queue.Enqueue(temp);    
               while (queue.Find()) //while可处理同时接收到多个包    
               {    
                   byte[] readBuffer =  queue.Dequeue();    
                   OnReceiveData(readBuffer); //<这里自己写一个委托吧就OK了    
               }    
就这个了,你放到Socket接收的那个代码中 就是int length=socket.Recive(xxx,xxxx) 的后面呀,至于同步异步,看你自己写的接收方式与这个缓冲器没关系的。你只管把Socket收到的内容向里面放就是了,他就能逐个解析出包
SomethingJack 2015-03-09
  • 打赏
  • 举报
回复
引用 13 楼 wyd1520 的回复:
看这里 有DEMO http://blog.csdn.net/wyd1520/article/details/44153543 这是缓冲区代码


/// <summary>  
    /// 字节缓冲器  
    /// </summary>  
    public class ByteQueue  
    {  
        private List<byte> m_buffer = new List<byte>();  
        private byte[] headBuffer = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, 0xCB, 0xCC, 0xCD }; //包头开始部份  
        private byte[] tailBuffer = new byte[] { 0xEA, 0xEB, 0xEC, 0xED };//包结束部份  
  
        private int IndexOf(List<byte> source, byte[] seach, int startIndex)  
        {  
            string baseStr = BitConverter.ToString(source.ToArray());  
            string searchStr=BitConverter.ToString(seach);  
            return baseStr.IndexOf(searchStr, startIndex) / 3;  
        }  
  
         
        public bool Find()  
        {  
            if (m_buffer.Count == 0)  
                return false;  
            int HeadIndex = IndexOf(m_buffer, headBuffer,0);//找开头的位置  
            if (HeadIndex == -1)  
            {  
                m_buffer.Clear();  
                return false; //没找到  
            }  
            else if (HeadIndex != 0) //不为开头移掉之前的字节  
            {  
                if (HeadIndex > 1)  
                {  
                    m_buffer.RemoveRange(0, HeadIndex);  
                    HeadIndex = 0;  
                }  
            }  
  
  
  
            int TailIndex = IndexOf(m_buffer, tailBuffer, headBuffer.Length); //查找结尾的位置  
  
            if (TailIndex == -1)  
            {  
                //这一步为防止连发一个开头的包后,没发结尾,而又发了一个包头  
                int head = IndexOf(m_buffer, headBuffer, 0);  
                if (head > -1)  
                {  
                    m_buffer.RemoveRange(0, head);  
                }  
                return false;  
            }  
            int packLength = TailIndex - HeadIndex;  
            if (packLength<0) //计算包尾是否与包长度相等  
            {  
                m_buffer.RemoveRange(0, TailIndex + tailBuffer.Length);  
                return false;  
            }  
            return true;  
        }  
  
  
  
  
         
  
        /// <summary>  
        /// 包长度  
        /// </summary>  
        /// <returns></returns>  
        private int GetLength()  
        {  
            int len = IndexOf(m_buffer, tailBuffer, headBuffer.Length) + tailBuffer.Length; //查找结尾的位置  
            return len;  
        }  
        /// <summary>  
        /// 提取数据  
        /// </summary>  
        public byte[]  Dequeue()  
        {  
            int length = GetLength();  
            List<byte> result = m_buffer.Take(length).ToList();//提取数据包  
            result.RemoveRange(0, headBuffer.Length);//移掉开头部份 只保留内容  
            result.RemoveRange(result.Count - tailBuffer.Length, tailBuffer.Length); //移掉结尾 只保留内容  
            m_buffer.RemoveRange(0, length);//缓冲区内容移除  
            return result.ToArray();  
            
        }  
  
        /// <summary>  
        /// 队列数据  
        /// </summary>  
        /// <param name="buffer"></param>  
        public void Enqueue(byte[] buffer)  
        {  
            m_buffer.AddRange(buffer);  
        }  
    }  

非常感谢 最后还有一点不明白 你在串口例子中调用的时候 有两个地方是对应Socket的哪部分?同步或者异步都可以说一下。 这里

private ByteQueue queue = new ByteQueue();  
private void socket_DataReceived(object sender, SerialDataReceivedEventArgs e)  
       {  
           int len = serialPort1.BytesToRead;  
           if (len > 0)  
           {  
               byte[] temp = new byte[len];  
               serialPort1.Read(temp, 0, len);  
               queue.Enqueue(temp);  
               while (queue.Find()) //while可处理同时接收到多个包  
               {  
                   byte[] readBuffer =  queue.Dequeue();  
                   OnReceiveData(readBuffer); //<这里自己写一个委托吧就OK了  
               }  
  
           }  
  
       }  
本拉灯 2015-03-09
  • 打赏
  • 举报
回复
看这里 有DEMO http://blog.csdn.net/wyd1520/article/details/44153543 这是缓冲区代码


/// <summary>  
    /// 字节缓冲器  
    /// </summary>  
    public class ByteQueue  
    {  
        private List<byte> m_buffer = new List<byte>();  
        private byte[] headBuffer = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF, 0xCA, 0xCB, 0xCC, 0xCD }; //包头开始部份  
        private byte[] tailBuffer = new byte[] { 0xEA, 0xEB, 0xEC, 0xED };//包结束部份  
  
        private int IndexOf(List<byte> source, byte[] seach, int startIndex)  
        {  
            string baseStr = BitConverter.ToString(source.ToArray());  
            string searchStr=BitConverter.ToString(seach);  
            return baseStr.IndexOf(searchStr, startIndex) / 3;  
        }  
  
         
        public bool Find()  
        {  
            if (m_buffer.Count == 0)  
                return false;  
            int HeadIndex = IndexOf(m_buffer, headBuffer,0);//找开头的位置  
            if (HeadIndex == -1)  
            {  
                m_buffer.Clear();  
                return false; //没找到  
            }  
            else if (HeadIndex != 0) //不为开头移掉之前的字节  
            {  
                if (HeadIndex > 1)  
                {  
                    m_buffer.RemoveRange(0, HeadIndex);  
                    HeadIndex = 0;  
                }  
            }  
  
  
  
            int TailIndex = IndexOf(m_buffer, tailBuffer, headBuffer.Length); //查找结尾的位置  
  
            if (TailIndex == -1)  
            {  
                //这一步为防止连发一个开头的包后,没发结尾,而又发了一个包头  
                int head = IndexOf(m_buffer, headBuffer, 0);  
                if (head > -1)  
                {  
                    m_buffer.RemoveRange(0, head);  
                }  
                return false;  
            }  
            int packLength = TailIndex - HeadIndex;  
            if (packLength<0) //计算包尾是否与包长度相等  
            {  
                m_buffer.RemoveRange(0, TailIndex + tailBuffer.Length);  
                return false;  
            }  
            return true;  
        }  
  
  
  
  
         
  
        /// <summary>  
        /// 包长度  
        /// </summary>  
        /// <returns></returns>  
        private int GetLength()  
        {  
            int len = IndexOf(m_buffer, tailBuffer, headBuffer.Length) + tailBuffer.Length; //查找结尾的位置  
            return len;  
        }  
        /// <summary>  
        /// 提取数据  
        /// </summary>  
        public byte[]  Dequeue()  
        {  
            int length = GetLength();  
            List<byte> result = m_buffer.Take(length).ToList();//提取数据包  
            result.RemoveRange(0, headBuffer.Length);//移掉开头部份 只保留内容  
            result.RemoveRange(result.Count - tailBuffer.Length, tailBuffer.Length); //移掉结尾 只保留内容  
            m_buffer.RemoveRange(0, length);//缓冲区内容移除  
            return result.ToArray();  
            
        }  
  
        /// <summary>  
        /// 队列数据  
        /// </summary>  
        /// <param name="buffer"></param>  
        public void Enqueue(byte[] buffer)  
        {  
            m_buffer.AddRange(buffer);  
        }  
    }  

SomethingJack 2015-03-09
  • 打赏
  • 举报
回复
引用 11 楼 wyd1520 的回复:
[quote=引用 楼主 SomethingJack 的回复:]
设备媒50MS发送一次数据,一次的数据可能不是完整的数据包.
开头以FF FF FF FF CA CB CC CD 开始,以EA EB EC ED结束 这位一个完整的数据包.

如果 50MS过来一次数据 但是数据分了3次或者N次

第一次 FF FF FF FF CA CB CC CD **
第二次 ** ** **
第三次 EA EB EC ED

如何去合成这个完整的包 根据开始和结束判断。

希望给出伪代码。谢谢各位!



你这个包头有没有数据长度体现在里面?把包的完整格式说一下,还是说只有包头与包尾。。[/quote]


你看一下 这就是我的数据包 我用箭头指出了开始和结尾 实际上这个抓包软件是做过了处理的 一次数据过来可能是分多次才能收完。
长空X 2015-03-08
  • 打赏
  • 举报
回复
引用 2 楼 sp1234 的回复:
你可以把
public delegate void OnMessageEventHandler(NetworkStream stream, string message);
修改为
public delegate void OnMessageEventHandler(NetworkStream stream, byte[] message);
来抛出二进制数据消息,而不需要考虑字符串编码。 不过,通常如果设计一个比较高级(特别是面向可读字符串)的协议,例如基于json的协议,不但更容易调试,也更容易动态扩展消息内容。面向byte[ ]来设计,看似通用,实则影响了许多子系统的应用扩展能力。
基于JSON的话 是不是想当于把字节型换成JSON?原谅还没了解过JSON
Sunny5816 2015-03-08
  • 打赏
  • 举报
回复
可以参考我的这篇文章:c#源码分享-TCP通信中的大文件传送
  • 打赏
  • 举报
回复
你可以把
public delegate void OnMessageEventHandler(NetworkStream stream, string message);
修改为
public delegate void OnMessageEventHandler(NetworkStream stream, byte[] message);
来抛出二进制数据消息,而不需要考虑字符串编码。 不过,通常如果设计一个比较高级(特别是面向可读字符串)的协议,例如基于json的协议,不但更容易调试,也更容易动态扩展消息内容。面向byte[ ]来设计,看似通用,实则影响了许多子系统的应用扩展能力。
  • 打赏
  • 举报
回复
我在帖子 http://bbs.csdn.net/topics/390987992 中写了一个例子,你把 0d 0a 改为 EA EB EC ED 好了。
本拉灯 2015-03-07
  • 打赏
  • 举报
回复
黄小明。对此明星无爱。。。 来楼主给你看看这个 http://blog.csdn.net/wyd1520/article/details/23822313 字节缓冲区 就能解决你的问题了。
  • 打赏
  • 举报
回复
引用 2 楼 sp1234 的回复:
你可以把
public delegate void OnMessageEventHandler(NetworkStream stream, string message);
修改为
public delegate void OnMessageEventHandler(NetworkStream stream, byte[] message);
来抛出二进制数据消息,而不需要考虑字符串编码。 不过,通常如果设计一个比较高级(特别是面向可读字符串)的协议,例如基于json的协议,不但更容易调试,也更容易动态扩展消息内容。面向byte[ ]来设计,看似通用,实则影响了许多子系统的应用扩展能力。
二楼分析得太好了,正解!
江南小鱼 2015-03-07
  • 打赏
  • 举报
回复
粘包、分包这个很正常 在并发量比较大的情况下,就会出现一次接受并不能完整的获取所有的数据,也就是tcp进行了分包。 针对分包现象: 1、定义适当的协议,比如包头是报文长度,当获取到指定的包长时才说明接收完整 2、指定包的结束标识,当获取到结束标识时,说明包获取完整。

110,536

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧