c# socket 解决粘包,半包

处理原理:

半包:即一条消息底层分几次发送,先有个头包读取整条消息的长度,当不满足长度时,将消息临时缓存起来,直到满足长度再解码

粘包:两条完整/不完整消息粘在一起,一般是解码完上一条消息,然后再判断是否有剩余字节,有的话缓存起来,循环半包处理

客户端接收代码:

    private void callReceived(object sender, SocketAsyncEventArgs args)
    {
        var socket = sender as Socket;
        var bb = args.UserToken as ByteBuffer;
        if (args.SocketError == SocketError.Success)
        {
          bb.WriteBytes(args.Buffer, args.Offset, args.BytesTransferred);
          bb.MarkReaderIndex();
          int headLength = bb.ReadInt();
          int msgLength = headLength;//长度已-4
          int readByteLength = bb.ReadableBytes();
          //解决半包
          if (msgLength > readByteLength)
          {
              //还原读取索引记录
              bb.ResetReaderIndex();
          }
          else {
              //是否去掉包头
              byte[] filthyBytes= bb.ToArray();
              System.Console.WriteLine(System.Text.Encoding.UTF8.GetString(filthyBytes));

              //解决粘包剩余
              bb.Clear();
              int useLength = filthyBytes.Length;
              int lastOffSetLength = filthyBytes.Length - useLength;
              if (lastOffSetLength > 0) {
                  bb.WriteBytes(filthyBytes, lastOffSetLength, filthyBytes.Length);
              }
          }
        }
        else {
            //丢去byte处理
            System.Console.WriteLine("error callReceived");
        }
        _socket.ReceiveAsync(args);
    }

服务端发送代码:

            ByteBuffer bb = ByteBuffer.Allocate(50);
            byte[] sendBytes=System.Text.Encoding.UTF8.GetBytes("1234567890abcdefg");
            Console.WriteLine("send msg length : " + sendBytes.Length);
            bb.WriteInt(sendBytes.Length);
            bb.WriteBytes(sendBytes);
            Send(bb.ToArray(), _clients.ToArray());

    public void Send(byte[] msg, params  SocketAsyncEventArgs[] sockets)
    {
        System.Console.WriteLine(" Send msg :");
        foreach (SocketAsyncEventArgs s in sockets)
        {
            SocketAsyncEventArgs args = new SocketAsyncEventArgs();
            args.SetBuffer(msg, 0, msg.Length);
            //args.RemoteEndPoint = s.RemoteEndPoint;
            args.AcceptSocket = s.AcceptSocket;

            args.Completed += new EventHandler<SocketAsyncEventArgs>(this.callSended);
            System.Console.WriteLine(" AcceptSocket :" + s.AcceptSocket.RemoteEndPoint.ToString());

            args.AcceptSocket.SendAsync(args);
        }

    }
ByteBuffer 类基础跟netty相同,网上复制的
using System;

public class ByteBuffer
{
    //字节缓存区
    private byte[] buf;
    //读取索引
    private int readIndex = 0;
    //写入索引
    private int writeIndex = 0;
    //读取索引标记
    private int markReadIndex = 0;
    //写入索引标记
    private int markWirteIndex = 0;
    //缓存区字节数组的长度
    private int capacity;

    /**
     * 构造方法
     */
    private ByteBuffer(int capacity)
    {
        buf = new byte[capacity];
        this.capacity = capacity;
    }

    /**
     * 构造方法
     */
    private ByteBuffer(byte[] bytes)
    {
        buf = bytes;
        this.capacity = bytes.Length;
    }

    /**
     * 构建一个capacity长度的字节缓存区ByteBuffer对象
     */
    public static ByteBuffer Allocate(int capacity)
    {
        return new ByteBuffer(capacity);
    }

    /**
     * 构建一个以bytes为字节缓存区的ByteBuffer对象,一般不推荐使用
     */
    public static ByteBuffer Allocate(byte[] bytes)
    {
        return new ByteBuffer(bytes);
    }

    /**
     * 翻转字节数组,如果本地字节序列为低字节序列,则进行翻转以转换为高字节序列
     */
    private byte[] flip(byte[] bytes)
    {
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(bytes);
        }
        return bytes;
    }

    /**
 * 确定内部字节缓存数组的大小
 */
    private int FixSizeAndReset(int currLen, int futureLen)
    {
        if (futureLen > currLen)
        {
            //以原大小的2次方数的两倍确定内部字节缓存区大小
            int size = FixLength(currLen) * 2;
            if (futureLen > size)
            {
                //以将来的大小的2次方的两倍确定内部字节缓存区大小
                size = FixLength(futureLen) * 2;
            }
            byte[] newbuf = new byte[size];
            Array.Copy(buf, 0, newbuf, 0, currLen);
            buf = newbuf;
            capacity = newbuf.Length;
        }
        return futureLen;
    }

    /**
     * 根据length长度,确定大于此leng的最近的2次方数,如length=7,则返回值为8
     */
    private int FixLength(int length)
    {
        int n = 2;
        int b = 2;
        while (b < length)
        {
            b = 2 << n;
            n++;
        }
        return b;
    }

    /**
     * 将bytes字节数组从startIndex开始的length字节写入到此缓存区
     */
    public ByteBuffer WriteBytes(byte[] bytes, int startIndex, int length)
    {
        lock (this)
        {
            int offset = length - startIndex;
            if (offset <= 0) return this;
            int total = offset + writeIndex;
            int len = buf.Length;
            FixSizeAndReset(len, total);
            for (int i = writeIndex, j = startIndex; i < total; i++, j++)
            {
                this.buf[i] = bytes[j];
            }
            writeIndex = total;
        }
        return this;
    }

    /**
     * 将字节数组中从0到length的元素写入缓存区
     */
    public ByteBuffer WriteBytes(byte[] bytes, int length)
    {
        return WriteBytes(bytes, 0, length);
    }

    /**
     * 将字节数组全部写入缓存区
     */
    public ByteBuffer WriteBytes(byte[] bytes)
    {
       return WriteBytes(bytes, bytes.Length);
    }

    /**
     * 将一个ByteBuffer的有效字节区写入此缓存区中
     */
    public ByteBuffer Write(ByteBuffer buffer)
    {
        if (buffer == null) return this;
        if (buffer.ReadableBytes() <= 0) return this;
       return WriteBytes(buffer.ToArray());
    }

    /**
     * 写入一个int16数据
     */
    public ByteBuffer WriteShort(short value)
    {
       return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 写入一个uint16数据
     */
    public ByteBuffer WriteUshort(ushort value)
    {
        return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**写入字符串*/
    public ByteBuffer WriteString(string value)
    {
        int len = value.Length;
        WriteInt(len);
        //System.Text.Encoding.BigEndianUnicode.GetBytes
        WriteBytes(System.Text.Encoding.UTF8.GetBytes(value));
        return this;
    }
    /**读取字符串*/
    public String ReadString()
    {
        int len =ReadInt();
        byte[] bytes =new byte[len];
        ReadBytes(bytes,0,len); 

        return System.Text.Encoding.UTF8.GetString(bytes);
    }

    /**
     * 写入一个int32数据
     */
    public ByteBuffer WriteInt(int value)
    {
        //byte[] array = new byte[4];
        //for (int i = 3; i >= 0; i--)
        //{
        //    array[i] = (byte)(value & 0xff);
        //    value = value >> 8;
        //}
        //Array.Reverse(array);
        //Write(array);
       return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 写入一个uint32数据
     */
    public ByteBuffer WriteUint(uint value)
    {
        return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 写入一个int64数据
     */
    public ByteBuffer WriteLong(long value)
    {
       return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 写入一个uint64数据
     */
    public ByteBuffer WriteUlong(ulong value)
    {
      return  WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 写入一个float数据
     */
    public ByteBuffer WriteFloat(float value)
    {
       return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 写入一个byte数据
     */
    public ByteBuffer WriteByte(byte value)
    {
        lock (this)
        {
            int afterLen = writeIndex + 1;
            int len = buf.Length;
            FixSizeAndReset(len, afterLen);
            buf[writeIndex] = value;
            writeIndex = afterLen;
        }
        return this;
    }

    /**
     * 写入一个double类型数据
     */
    public ByteBuffer WriteDouble(double value)
    {
        return WriteBytes(flip(BitConverter.GetBytes(value)));
    }

    /**
     * 读取一个字节
     */
    public byte ReadByte()
    {
        byte b = buf[readIndex];
        readIndex++;
        return b;
    }

    /**
     * 从读取索引位置开始读取len长度的字节数组
     */
    private byte[] Read(int len)
    {
        byte[] bytes = new byte[len];
        Array.Copy(buf, readIndex, bytes, 0, len);
        if (BitConverter.IsLittleEndian)
        {
            Array.Reverse(bytes);
        }
        readIndex += len;
        return bytes;
    }

    /**
     * 读取一个uint16数据
     */
    public ushort ReadUshort()
    {
        return BitConverter.ToUInt16(Read(2), 0);
    }

    /**
     * 读取一个int16数据
     */
    public short ReadShort()
    {
        return BitConverter.ToInt16(Read(2), 0);
    }

    /**
     * 读取一个uint32数据
     */
    public uint ReadUint()
    {
        return BitConverter.ToUInt32(Read(4), 0);
    }

    /**
     * 读取一个int32数据
     */
    public int ReadInt()
    {
        return BitConverter.ToInt32(Read(4), 0);
    }

    /**
     * 读取一个uint64数据
     */
    public ulong ReadUlong()
    {
        return BitConverter.ToUInt64(Read(8), 0);
    }

    /**
     * 读取一个long数据
     */
    public long ReadLong()
    {
        return BitConverter.ToInt64(Read(8), 0);
    }

    /**
     * 读取一个float数据
     */
    public float ReadFloat()
    {
        return BitConverter.ToSingle(Read(4), 0);
    }

    /**
     * 读取一个double数据
     */
    public double ReadDouble()
    {
        return BitConverter.ToDouble(Read(8), 0);
    }

    /**
     * 从读取索引位置开始读取len长度的字节到disbytes目标字节数组中
     * @params disstart 目标字节数组的写入索引
     */
    public void ReadBytes(byte[] disbytes, int disstart, int len)
    {
        int size = disstart + len;
        for (int i = disstart; i < size; i++)
        {
            disbytes[i] = this.ReadByte();
        }
    }

    /**
     * 清除已读字节并重建缓存区
     */
    public void DiscardReadBytes()
    {
        if(readIndex <= 0) return;
        int len = buf.Length - readIndex;
        byte[] newbuf = new byte[len];
        Array.Copy(buf, readIndex, newbuf, 0, len);
        buf = newbuf;
        writeIndex -= readIndex;
        markReadIndex -= readIndex;
        if (markReadIndex < 0)
        {
            markReadIndex = readIndex;
        }
        markWirteIndex -= readIndex;
        if (markWirteIndex < 0 || markWirteIndex < readIndex || markWirteIndex < markReadIndex)
        {
            markWirteIndex = writeIndex;
        }
        readIndex = 0;
    }

    /**
     * 清空此对象
     */
    public void Clear()
    {
        buf = new byte[buf.Length];
        readIndex = 0;
        writeIndex = 0;
        markReadIndex = 0;
        markWirteIndex = 0;
    }

    /**
     * 设置开始读取的索引
     */
    public void SetReaderIndex(int index)
    {
        if (index < 0) return;
        readIndex = index;
    }

    /**
     * 标记读取的索引位置
     */
    public int MarkReaderIndex()
    {
        markReadIndex = readIndex;
        return markReadIndex;
    }

    /**
     * 标记写入的索引位置
     */
    public void MarkWriterIndex()
    {
        markWirteIndex = writeIndex;
    }

    /**
     * 将读取的索引位置重置为标记的读取索引位置
     */
    public void ResetReaderIndex()
    {
        readIndex = markReadIndex;
    }

    /**
     * 将写入的索引位置重置为标记的写入索引位置
     */
    public void ResetWriterIndex()
    {
        writeIndex = markWirteIndex;
    }

    /**
     * 可读的有效字节数
     */
    public int ReadableBytes()
    {
        return writeIndex - readIndex;
    }

    /**
     * 获取可读的字节数组
     */
    public byte[] ToArray()
    {
        byte[] bytes = new byte[writeIndex];
        Array.Copy(buf, 0, bytes, 0, bytes.Length);
        return bytes;
    }

    /**
     * 获取缓存区大小
     */
    public int GetCapacity()
    {
        return this.capacity;
    }
}
 
时间: 2024-11-05 20:48:14

c# socket 解决粘包,半包的相关文章

[编织消息框架][设计协议]解决粘包半包(下)

接下来介绍netty如何切割分包 学习目的,了解处理业务,方便以后脱离依赖 读者如果不感兴趣或看不懂可以先忽略,难度比较大 LengthFieldBasedFrameDecoder.class public LengthFieldBasedFrameDecoder( ByteOrder byteOrder, //大小端模式 默认大端 ByteOrder BIG_ENDIAN int maxFrameLength, //包Frame netty叫帧概念 最大上限 int lengthFieldOf

Java 粘包/半包 原理与拆包实战(史上最全)

疯狂创客圈 Java 聊天程序[ 亿级流量]实战系列之13 [博客园 总入口 ] 本文的源码工程:Netty 粘包/半包原理与拆包实战 源码 本实例是<Netty 粘包/半包原理与拆包实战> 一文的源代码工程. 写在前面 大家好,我是作者尼恩. 为了完成了一个高性能的 Java 聊天程序,在前面的文章中,尼恩已经再一次的进行了通讯协议的重新选择. 这就是:放弃了大家非常熟悉的json 格式,选择了性能更佳的 Protobuf协议. 在上一篇文章中,并且完成了Netty 和 Protobuf协议

Socket解决粘包问题1

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

Http 调用netty 服务,服务调用客户端,伪同步响应.ProtoBuf 解决粘包,半包问题.

实际情况是: 公司需要开发一个接口给新产品使用,需求如下 1.有一款硬件设备,客户用usb接上电脑就可以,但是此设备功能比较单一,所以开发一个服务器程序,辅助此设备业务功能 2.解决方案,使用Socket调用此设备 3.增强此设备功能,增加Socket客户端连接到Socket服务端 4.Http请求,同步响应 测试注意: 1.nettyServer 在ubuntu下编码,使用Epoll 2.Http请求的测试最好运行再Linux 下进行,因为Windows 可能会因为并发高的时候占满端口限制,H

Socket解决粘包问题2

在AsynServer中对接收函数增加接收判断,如果收到客户端发送的请求信息,则发送10个测试包给发送端,否则继续接收,修改后的接收代码如下: private void AsynReceive() { byte[] data = new byte[1024];//接收缓存 string receiveStr; string[] sendArr = PackageBuilder.BuildPackage(10);//生成发送数组,10个包 socket.BeginReceive(data, 0,

socket编程 粘包和半包 问题的及处理

一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系. 粘包 为x.5个包 半包 为0.5个包 由于网络原因 一次可能会来 0.5/1 /2/ 2.5/ ....个包 当接收到时 要先看看那这个包中有多少个完整的包.把完整的包都处理了 也就是说把x都处理了.剩下的0.5留在接收区中,等待下次接收. 这回接收到的就是0.5+1.5/0.5+1.3/0.5+0.5..... 把完整的包都处理了,有残缺的扔掉 0.8

C#下利用封包、拆包原理解决Socket粘包、半包问题(新手篇)

介于网络上充斥着大量的含糊其辞的Socket初级教程,扰乱着新手的学习方向,我来扼要的教一下新手应该怎么合理的处理Socket这个玩意儿. 一般来说,教你C#下Socket编程的老师,很少会教你如何解决Socket粘包.半包问题. 更甚至,某些师德有问题的老师,根本就没跟你说过Socket的粘包.半包问题是什么玩意儿. 直到有一天,你的Socket程序在传输信息时出现了你预期之外的结果(多于的信息.不完整的信息.乱码.Bug等等). 任你喊了一万遍“我擦”,依旧是不知道问题出在哪儿! 好了,不说

SOCKET TCP 粘包及半包问题

大家在使用SOCKET通信编程的时候,一般会采用UDP和TCP两种方式:TCP因为它没有包的概念,它只有流的概念,并且因为发送或接收缓冲区大小的设置问题,会产生粘包及半包的现象. 场景: 服务端向连续发送三个"HelloWorld"(三次消息无间隔),那么客户端接收到的情况会有以下三种: 1)HelloWorld HelloWorld HelloWorld (客户端接收三次) 2)HelloWorldHelloWor ldHelloWorld (客户端接收两次) 3)HelloWorl

socket tcp 粘包解决

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