现在的位置: 首页 > 综合 > 正文

Socket接收字节缓冲区

2017年11月15日 ⁄ 综合 ⁄ 共 2169字 ⁄ 字号 评论关闭

        我们接收Socket字节流数据一般都会定义一个数据包协议( 协议号,长度,内容),由于Socket接收数据是连续的,对方发两个包过来,Socket的 Recive事件有可能只触发一次或触发三次,也就是大家听到的粘包,为解决这个粘包,所以我们必要建一个字节缓冲区,将所有的接收到的字节流全放到这个缓冲区内 由这个缓冲区来分隔每个数据包的内容。

        这份代码也是为论坛某个人解决串口接收数据包时而写的。不多说了上代码:

 /// <summary>
    /// 字节缓冲器
    /// </summary>
    public class ByteQueue
    {
        private List<byte> m_buffer = new List<byte>();
        public bool Find()
        {
            if (m_buffer.Count == 0)
                return false;
            int HeadIndex = m_buffer.FindIndex(o => o == 0xAA);

            if (HeadIndex == -1)
            {
                m_buffer.Clear();
                return false; //没找到AA
            }

            else if (HeadIndex != 0) //不为开头移掉之前的字节
            {
                if (HeadIndex > 1)
                    m_buffer.RemoveRange(0, HeadIndex);
            }

            int length= GetLength();

            if (m_buffer.Count <length)
            {
                return false;
            }

            int TailIndex = m_buffer.FindIndex(o => o == 0x55); //查找55的位置

            if (TailIndex == -1)
            {
                //这一步为防止连发一个AA开头的包后,没发55,而又发了一个AA
                int head = m_buffer.FindLastIndex(o => o == 0xAA);
                if (head > -1)
                {
                    m_buffer.RemoveRange(0, head);
                }
                return false;
            }
            else if (TailIndex + 1 != length) //计算包尾是否与包长度相等
            {
                m_buffer.RemoveRange(0, TailIndex);
                return false;
            }

            return true;
        }

        /// <summary>
        /// 命令类型
        /// </summary>
        /// <returns></returns>
        public byte Cmd()
        {
            if (m_buffer.Count >= 2)
            {
                return m_buffer[1];
            }
            return 0;
        }

        /// <summary>
        /// 序号
        /// </summary>
        /// <returns></returns>
        public byte Number()
        {
            if (m_buffer.Count >= 3)
            {
                return m_buffer[2];
            }
            return 0;
        }

        /// <summary>
        /// 包长度
        /// </summary>
        /// <returns></returns>
        public int GetLength()
        {
            int len = 5;//AA 命令类型 序号 校验和 55
            if (m_buffer.Count >= 3)
            {
                switch (m_buffer[2]) //第三字节为序号
                { 
                    case 0x00: //序号
                        return len + 16;
                    case 0x01: //序号
                        return len + 10;
                    case 0x02: //序号
                        return len + 12;
                }
            }
            return 0;
        }
        /// <summary>
        /// 提取数据
        /// </summary>
        public void Dequeue(byte[] buffer, int offset,int size)
        {
            m_buffer.CopyTo(0,buffer,offset,size);
            m_buffer.RemoveRange(0, size);
        }

        /// <summary>
        /// 队列数据
        /// </summary>
        /// <param name="buffer"></param>
        public void Enqueue(byte[] buffer)
        {
            m_buffer.AddRange(buffer);
        }
    }

 调用列子:

 private ByteQueue queue = new ByteQueue();
 private void serialPort1_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可处理同时接收到多个AA ... 55 ,AA...55的包
                {
                    int length = queue.GetLength();
                    byte[] readBuffer = new byte[len];
                    queue.Dequeue(readBuffer, 0, length);
                    OnReceiveData(readBuffer); //<这里自己写一个委托吧就OK了
                }

            }

        }

上面的字节接收容器是用List来处理为方便进出字节后移除整个数据包的字节数据,当然更高效的应用byte[] 数组作成环形缓冲会好很多相对应的写法也会难一些,

抱歉!评论已关闭.