处理原理:

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

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

客户端接收代码:

    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 > ) {
bb.WriteBytes(filthyBytes, lastOffSetLength, filthyBytes.Length);
}
}
}
else {
//丢去byte处理
System.Console.WriteLine("error callReceived");
}
_socket.ReceiveAsync(args);
}

服务端发送代码:

            ByteBuffer bb = ByteBuffer.Allocate();
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, , 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 = ;
//写入索引
private int writeIndex = ;
//读取索引标记
private int markReadIndex = ;
//写入索引标记
private int markWirteIndex = ;
//缓存区字节数组的长度
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) * ;
if (futureLen > size)
{
//以将来的大小的2次方的两倍确定内部字节缓存区大小
size = FixLength(futureLen) * ;
}
byte[] newbuf = new byte[size];
Array.Copy(buf, , newbuf, , currLen);
buf = newbuf;
capacity = newbuf.Length;
}
return futureLen;
} /**
* 根据length长度,确定大于此leng的最近的2次方数,如length=7,则返回值为8
*/
private int FixLength(int length)
{
int n = ;
int b = ;
while (b < length)
{
b = << n;
n++;
}
return b;
} /**
* 将bytes字节数组从startIndex开始的length字节写入到此缓存区
*/
public ByteBuffer WriteBytes(byte[] bytes, int startIndex, int length)
{
lock (this)
{
int offset = length - startIndex;
if (offset <= ) 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, , 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() <= ) 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,,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 + ;
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, , len);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
readIndex += len;
return bytes;
} /**
* 读取一个uint16数据
*/
public ushort ReadUshort()
{
return BitConverter.ToUInt16(Read(), );
} /**
* 读取一个int16数据
*/
public short ReadShort()
{
return BitConverter.ToInt16(Read(), );
} /**
* 读取一个uint32数据
*/
public uint ReadUint()
{
return BitConverter.ToUInt32(Read(), );
} /**
* 读取一个int32数据
*/
public int ReadInt()
{
return BitConverter.ToInt32(Read(), );
} /**
* 读取一个uint64数据
*/
public ulong ReadUlong()
{
return BitConverter.ToUInt64(Read(), );
} /**
* 读取一个long数据
*/
public long ReadLong()
{
return BitConverter.ToInt64(Read(), );
} /**
* 读取一个float数据
*/
public float ReadFloat()
{
return BitConverter.ToSingle(Read(), );
} /**
* 读取一个double数据
*/
public double ReadDouble()
{
return BitConverter.ToDouble(Read(), );
} /**
* 从读取索引位置开始读取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 <= ) return;
int len = buf.Length - readIndex;
byte[] newbuf = new byte[len];
Array.Copy(buf, readIndex, newbuf, , len);
buf = newbuf;
writeIndex -= readIndex;
markReadIndex -= readIndex;
if (markReadIndex < )
{
markReadIndex = readIndex;
}
markWirteIndex -= readIndex;
if (markWirteIndex < || markWirteIndex < readIndex || markWirteIndex < markReadIndex)
{
markWirteIndex = writeIndex;
}
readIndex = ;
} /**
* 清空此对象
*/
public void Clear()
{
buf = new byte[buf.Length];
readIndex = ;
writeIndex = ;
markReadIndex = ;
markWirteIndex = ;
} /**
* 设置开始读取的索引
*/
public void SetReaderIndex(int index)
{
if (index < ) 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, , bytes, , bytes.Length);
return bytes;
} /**
* 获取缓存区大小
*/
public int GetCapacity()
{
return this.capacity;
}
}

最后小结一下:

相信大家都看过TCP编程的书,书本上的理论废话长篇,说了一堆又给出一堆无用的代码。只有看源码才知道原理是怎样,源码实现一切。。。

在看的过程发现原作者写得也不是很好,简单的事情总是搞得那么复杂。。。。。

这行真是水深火热啊

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

  1. Netty 粘包/半包原理与拆包实战

    Java NIO 粘包 拆包 (实战) - 史上最全解读 - 疯狂创客圈 - 博客园 https://www.cnblogs.com/crazymakercircle/p/9941658.html 本 ...

  2. Socket解决粘包问题1

    粘包是指发送端发送的包速度过快,到接收端那边多包并成一个包的现象,比如发送端连续10次发送1个字符'a',因为发送的速度很快,接收端可能一次就收到了10个字符'aaaaaaaaaa',这就是接收端的粘 ...

  3. 网络编程基础【day09】:socket解决粘包问题之MD5(八)

    本节内容 1.概述 2.代码实现 一.概述 上一篇博客讲到的用MD5来校验还是用的之前解决粘包的方法,就是客户端发送一个请求,等待服务端的确认的这样的一个笨方法.下面我们用另外一种方法:就是客户端已经 ...

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

    实际情况是: 公司需要开发一个接口给新产品使用,需求如下 1.有一款硬件设备,客户用usb接上电脑就可以,但是此设备功能比较单一,所以开发一个服务器程序,辅助此设备业务功能 2.解决方案,使用Sock ...

  5. netty解决粘包半包问题

    前言:开发者用到TCP/IP交互时,偶尔会遇到粘包或者半包的数据,这种情况有时会对我们的程序造成严重的影响,netty框架为解决这种问题提供了若干框架 1. LineBasedFrameDecoder ...

  6. Socket解决粘包问题2

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

  7. socket编程 TCP 粘包和半包 的问题及解决办法

    一般在socket处理大数据量传输的时候会产生粘包和半包问题,有的时候tcp为了提高效率会缓冲N个包后再一起发出去,这个与缓存和网络有关系. 粘包 为x.5个包 半包 为0.5个包 由于网络原因 一次 ...

  8. Netty - 粘包和半包(上)

    在网络传输中,粘包和半包应该是最常出现的问题,作为 Java 中最常使用的 NIO 网络框架 Netty,它又是如何解决的呢?今天就让我们来看看. 定义 TCP 传输中,客户端发送数据,实际是把数据写 ...

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

    介于网络上充斥着大量的含糊其辞的Socket初级教程,扰乱着新手的学习方向,我来扼要的教一下新手应该怎么合理的处理Socket这个玩意儿. 一般来说,教你C#下Socket编程的老师,很少会教你如何解 ...

随机推荐

  1. C++ 用于大型程序的工具

    <C++ Primer 4th>读书笔记 相对于小的程序员团队所能开发的系统需求而言,大规模编程对程序设计语言的要求更高.大规模应用程序往往具有下列特殊要求: 1. 更严格的正常运转时间以 ...

  2. JavaScript原生DOM操作API总结

    最近面试的时候被这个问题给卡了,所以抽时间好好复习一下. 原文:http://www.cnblogs.com/liuxianan/p/javascript-dom-api.html 几种对象 Node ...

  3. 关于网卡eth0、eth1以及服务器为什么要把内网和外网卡区分开

    在搜搜上看到了这个回答,它解释了什么是eth0,eth1: eth0和eth1这是网卡设备,只是个名称不必纠结.通常服务器会有多个网卡的,所以就有eth0 eth1 eth2 这样的名称,而且在一些系 ...

  4. paip. 混合编程的实现resin4 (自带Quercus ) 配置 php 环境

    paip. 混合编程的实现resin4 (自带Quercus ) 配置 php 环境 #---混合编程的类型 1.代码inline 方式 2.使用库/api  解析方式. #----配置resin 支 ...

  5. .NET Remoting学习笔记(三)信道

    目录 .NET Remoting学习笔记(一)概念 .NET Remoting学习笔记(二)激活方式 .NET Remoting学习笔记(三)信道 参考:♂风车车.Net .NET Framework ...

  6. Less安装与使用

    Less是一门CSS预处理语言,它扩展了CSS语言,增加了变量.Mixin.函数等特性,使CSS更易维护和拓展.(less可以运行在node或浏览器端) 首先:安装+配置环境 在node.js中通过它 ...

  7. linux进阶

    常用命令 rpm -q centos-release 查看centos版本 whereis java 查看文件安装路径 which java 查看可执行文件路径 echo $PATH echo $JA ...

  8. 搭建windows的solr6服务器

    准备工作: 目前最新版本6.0.下载solr 6.0:Solr6.0下载 JDK8 下载jdk1.8:jdk1.8[solr6.0是基于jdk8开发的] tomcat8.0 下载:tomcat8 ## ...

  9. 了解Browserify

    Browserify是一个Javascript的库,可以用来把多个Module打包到一个文件中,并且能很好地应对Modules之间的依赖关系.而Module是封装了属性和功能的单元,是一个Javasc ...

  10. Java int to String互转

    Integer.toString Integer.parseInt(lAyaNums);