高性能TcpServer(C#) - 3.命令通道(处理:掉包,粘包,垃圾包)
高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)
高性能TcpServer(C#) - 3.命令通道(处理:掉包,粘包,垃圾包)
高性能TcpServer(C#) - 4.文件通道(处理:文件分包,支持断点续传)
处理原理:
每个client创建各自的byte[]数组,通过遍历每个字节的数据
1.判断包长,确定掉包;
2.判断解析完后byte数组是否还有未解析的数据,确定粘包;
3.判断包头,确定垃圾包;
缓存数据类
/// <summary>
/// 缓存数据类
/// </summary>
public class CByteBuffer
{
// 默认1k
int m_iBufferSize = 1024 * 1;
// 数据解析
byte[] m_abyBuf;
int m_iPosition = 0;
int m_iRecvLength = 0;
bool bWaitRecvRemain;// 数据未接收完等待接收
object m_lock = new object(); // 内部同步锁
public int Position
{
get { return m_iPosition; }
set { m_iPosition = value; }
}
public int RecvLength
{
get { return m_iRecvLength; }
set { m_iRecvLength = value; }
}
public bool WaitRecvRemain
{
get { return bWaitRecvRemain; }
set { bWaitRecvRemain = value; }
}
public CByteBuffer(int buffSize)
{
m_iBufferSize = buffSize;
m_abyBuf = new byte[m_iBufferSize];
}
public int GetPosition()
{
return m_iPosition;
}
public int GetRecvLength()
{
return m_iRecvLength;
}
public void Put(SocketAsyncEventArgs e)
{
int iLength = e.BytesTransferred;
if (m_iRecvLength + iLength >= m_iBufferSize)
{
Clear();
return;
}
lock (m_lock)
{
Array.Copy(e.Buffer, e.Offset, m_abyBuf, m_iRecvLength, iLength);
m_iRecvLength += iLength;
}
}
public byte GetByte()
{
bWaitRecvRemain = false;
if (m_iPosition >= m_iRecvLength)
{
bWaitRecvRemain = true;
return 0;
}
byte byRet;
lock (m_lock)
{
byRet = m_abyBuf[m_iPosition];
}
m_iPosition++;
return byRet;
}
public byte[] GetByteArray(int Length)
{
bWaitRecvRemain = false;
if (m_iPosition + Length > m_iRecvLength)
{
bWaitRecvRemain = true;
return null;
}
byte[] ret = new byte[Length];
lock (m_lock)
{
Array.Copy(m_abyBuf, m_iPosition, ret, 0, Length);
m_iPosition += Length;
}
return ret;
}
public bool HasRemaining()
{
return m_iPosition < m_iRecvLength;
}
public int Remaining()
{
return m_iRecvLength - m_iPosition;
}
public void Clear()
{
m_iPosition = 0;
m_iRecvLength = 0;
bWaitRecvRemain = false;
}
~CByteBuffer()
{
m_abyBuf = null;
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
GC.SuppressFinalize(this);
}
}
public void Dispose()
{
Dispose(true);
}
}
协议解析类
public void Process(CByteBuffer bBuffer, CProtocolAnalysis analysis, string sn)
{
analysis.BagStatus = CProtocolAnalysis.EBagStatus.BagNone;
analysis.WhetherToSend = false;
int iPosition = bBuffer.Position;
byte head1 = 0; byte head2 = 0; byte head3 = 0; byte head4 = 0; byte head5 = 0; byte head6 = 0; bool headok = false;
if (!bBuffer.HasRemaining()) return;
while (bBuffer.HasRemaining())
{
head1 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
if (HEAD1 == head1)
{
iPosition = bBuffer.Position - 1;
head2 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
head3 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
head4 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
head5 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
head6 = bBuffer.GetByte(); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
if (HEAD2 == head2 && HEAD3 == head3 && HEAD4 == head4 && HEAD5 == head5 && HEAD6 == head6)
{
headok = true;
break;
}
else
{
CLogHelp.AppendLog("Error,Unable to parse the data2:Position=" + iPosition.ToString() + ",Index=" + (bBuffer.GetPosition()).ToString() + ",Head2=" + head2.ToString());
}
}
else
{
CLogHelp.AppendLog("Error,Unable to parse the data1:Position=" + iPosition.ToString() + ",Index=" + (bBuffer.GetPosition()).ToString() + ",Head1=" + head1.ToString());
}
}
if (!bBuffer.HasRemaining())
{
if (headok)
{
if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
}
return;
}
byte[] arrlen = bBuffer.GetByteArray(4); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
int len = CCommonFunc.String2Int(CCommonFunc.ByteToString(arrlen)); if (-1 == len) return;
byte[] source = bBuffer.GetByteArray(len); if (!analysis.IsRemainData(iPosition, bBuffer, analysis)) return;
if (!bBuffer.HasRemaining())
{
bBuffer.Clear();
}
else
{
analysis.BagStatus = CProtocolAnalysis.EBagStatus.BagStick;
}
// #WaterMeter-001#01##
string data = CCommonFunc.ByteToString(source);
if (null == data || 0 == data.Length || data.Length - 1 != data.LastIndexOf(SPLIT1))
{
return;
}
data = data.Substring(1, data.Length - 2);
string[] item = data.Split(SPLIT1);
if (null == item || 4 != item.Length)
{
return;
}
string uid = item[0];
string taskid = item[1];
int cmd = CCommonFunc.String2Int(item[2]);
string content = item[3];
Program.AddMessage("R: [" + sn + "] cmd=" + cmd.ToString() + " data=" + data);
analysis.Cmd = cmd;
analysis.Uid = uid;
analysis.TaskId = taskid;
if (cmd == 1 || cmd == 2 || cmd == 3 || cmd == 4 || cmd == 5 || cmd == 6 || cmd == 7)
{
analysis.WhetherToSend = true;
}
string softtype = "";
try
{
switch (cmd)
{
case 1:
analysis.Msg = "ok";
break;
case 2:
analysis.Msg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
break;
case 3:
// HTEMP=0263#WaterMeter-001#1520557004#03#buildid=44@edmid=37@meterid=1228@senddate=2018-02-05 17:36:22@[{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}+{132,0.0000}]#
analysis.Msg = "ok";
break;
case 4:
{
// 获取版本信息
softtype = content.Split(SPLIT2)[1];
StorageFile(softtype, System.Windows.Forms.Application.StartupPath + "\\test.zip");
analysis.Msg = "2";// version
}
break;
case 5:
// 获取包数
{
softtype = content.Split(SPLIT2)[1];
if (!dicSoft.ContainsKey(softtype))
{
StorageFile(softtype, System.Windows.Forms.Application.StartupPath + "\\test.zip");
}
// 获取包数
int count = 0;
FileCut entity = null;
dicSoft.TryGetValue(softtype, out entity);
if (null != entity) count = entity.Count;
analysis.Msg = count.ToString();
}
break;
case 6:
// 执行更新动作
{
string[] items = content.Split(SPLIT2);
softtype = items[1];
int downindex = CCommonFunc.String2Int(items[2]);
if (!dicSoft.ContainsKey(softtype))
{
analysis.Msg = "error@" + softtype + " 未找到更新文件,请先获取包数";
}
else
{
FileCut entity = null;
dicSoft.TryGetValue(softtype, out entity);
if (null != entity)
{
string filedata = "";
entity.Data.TryGetValue(downindex, out filedata);
if (string.IsNullOrEmpty(filedata))
analysis.Msg = "error@" + softtype + " 第" + downindex + "包的数据为空";
else
analysis.Msg = filedata;
}
}
}
break;
case 7:
// 更新版本信息(update sql)
analysis.Msg = "ok";
break;
}
}
catch (Exception ex)
{
analysis.Msg = "error@" + ex.Message;
}
Program.AddMessage("S: [" + sn + "] cmd=" + cmd.ToString() + " data=" + analysis.Msg);
}
测试效果
正常包
HTEMP=0026#Meter-001#1533022506#01##

掉包(分两包发送)
HTEMP=0026#
Meter-001#1533022506#01##

粘包(两包一起发送)
HTEMP=0026#Meter-001#1533022506#01##HTEMP=0026#Meter-001#1533022506#01##

高性能TcpServer(C#) - 3.命令通道(处理:掉包,粘包,垃圾包)的更多相关文章
- 高性能TcpServer(C#) - 4.文件通道(处理:文件分包,支持断点续传)
高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...
- C#高性能大容量SOCKET并发(五):粘包、分包、解包
原文:C#高性能大容量SOCKET并发(五):粘包.分包.解包 粘包 使用TCP长连接就会引入粘包的问题,粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一 ...
- DELPHI高性能大容量SOCKET并发(四):粘包、分包、解包
粘包 使用TCP长连接就会引入粘包的问题,粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.粘包可能由发送方造成,也可能由接收方造成.TCP为提 ...
- 高性能TcpServer(C#) - 1.网络通信协议
高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...
- 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP)
高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...
- 高性能TcpServer(C#) - 5.客户端管理
高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...
- 高性能TcpServer(C#) - 6.代码下载
高性能TcpServer(C#) - 1.网络通信协议 高性能TcpServer(C#) - 2.创建高性能Socket服务器SocketAsyncEventArgs的实现(IOCP) 高性能TcpS ...
- 高性能TcpServer(Java) - Netty
源码下载 -> 提取码 QQ:505645074 Netty 是一个高性能.异步事件驱动的 NIO 框架,它提供了对 TCP.UDP 和文件传输的支持,作为一个异步 NIO 框架,Netty ...
- 网络编程之模拟ssh远程执行命令、粘包问题 、解决粘包问题
目录 模拟ssh远程执行命令 服务端 客户端 粘包问题 什么是粘包 TCP发送数据的四种情况 粘包的两种情况 解决粘包问题 struct模块 解决粘包问题 服务端 客户端 模拟ssh远程执行命令 服务 ...
随机推荐
- 高强度学习训练第五天总结:JAVA对象+GC
第五天了.. 理清了Java对象的创建过程,分配内存,线程安全性,对象头和对象的访问定位 理清了JVM GC的发展历史,算法,例如: 可达性分析 引用计数法 标记-清楚法 复制算法 标记-整理算法 分 ...
- UISlider增加触动区域
- (CGRect)thumbRectForBounds:(CGRect)bounds trackRect:(CGRect)rect value:(float)value { rect.origin. ...
- Linux中LVM逻辑卷管理
一.简介 LVM是逻辑盘卷管理(Logical Volume Manager)的简称,它是Linux环境下对磁盘分区 进行管理的一种机制,LVM是建立在硬盘和分区之上的一个逻辑层,来提高磁盘分区管理的 ...
- Linux下环境变量(.bash_profile和.bashrc的区别)
在linux系统下,如果下载并安装了应用程序,在启动时很有可能在键入它的名称时出现"command not found"的提示内容.如果每次都到安装目标文件夹内,找到可执行文件来进 ...
- 16.Linux-LCD驱动(详解)【转】
转自:https://www.cnblogs.com/lifexy/p/7604011.html 在上一节LCD层次分析中,得出写个LCD驱动入口函数,需要以下4步: 1) 分配一个fb_info结构 ...
- HDU 4729 An Easy Problem for Elfness(树链剖分边权+二分)
题意 链接:https://cn.vjudge.net/problem/HDU-4729 给你n个点,然你求两个点s和t之间的最大流.而且你有一定的钱k,可以进行两种操作 1.在任意连个点之间建立一个 ...
- OpenTelemetry项目中的Observability
最近,在实操zipkin,jaeger,opencensus,opentracing,opentelemetry等. opentelemetry将Observability提到了重要页面, 并进行了讲 ...
- input:file样式怎样修改
问题描述: 我需要点击input:file来修改img中的图片,但是input:file样式太丑 解决办法: 给file设置透明度为0,让用户看不见他 创建新的button按钮 修改button按钮样 ...
- HTML与CSS学习笔记(5)
1.文字阴影?(针对文字) text-shadow: 例如 text-shadow: 10px 10px 10px blue;四个值分别是 x y blur color blur表示模糊值,越大越模糊 ...
- miniapp之登录、授权和支付
微信小程序代码实现(登录.授权和支付) ==整体流程看上一篇博客,或者去微信公众平台查看文档== ==只列出核心代码,详细代码见码云michaelben== 登录 // //小程序端 // app.j ...