高性能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基础(14):Eclipse、面向对象、自定义数据类型的使用
1. Eclipse的应用 1. 常用快捷操作 Ctrl+T:查看所选中类的继承树 例如,在下面代码中,选中Teacher类名,然后按Ctrl+T,就会显示出Teacher类的继承关系 //员工 ab ...
- 我用python爬取了知乎Top沙雕问题排行榜
前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 数据森麟 PS:如有需要Python学习资料的小伙伴可以加点击下方 ...
- 机器学习实战:基于Scikit-Learn和TensorFlow 读书笔记 第6章 决策树
数据挖掘作业,要实现决策树,现记录学习过程 win10系统,Python 3.7.0 构建一个决策树,在鸢尾花数据集上训练一个DecisionTreeClassifier: from sklearn. ...
- 如何通过纯javascript实现表单提交
通常,如果是POST方法,一般使用vuejs+axios,或使用Jquery实现表单提交.有些地方,我想使用纯JS实现,比方简单的登陆跳转.话不多说,看原代码, laravel中的HTML部分,如果不 ...
- Dynamics CRM定制子网格添加按钮实例之一
关注本人微信和易信公众号: 微软动态CRM专家罗勇 ,回复221或者20160430可方便获取本文,同时可以在第一间得到我发布的最新的博文信息,follow me!我的网站是 www.luoyong. ...
- Gatech OMSCS的申请和学习之奥妙
https://zhuanlan.zhihu.com/p/54680585 我写东西一向希望能给大家带来正能量,提供有价值的信息,不辱没母校的厚德价值观. 我是传统没落工科毕业后开发软件,但是一直没下 ...
- [20191002]函数dump的bug.txt
[20191002]函数dump的bug.txt --//前几天写raw转化oracle number脚本,在使用函数dump时遇到一些问题,做一个记录:--//oracle number 0 编码 ...
- nginx 的 proxy_cache 缓存配置
开头:某个项目涉及到 视频切片下载到本地,然后提供服务给客户端.一个视频有多个文件,存储在本地路径下.当客户端请求的视频在本地没有就会回源, 回源拿到的视频文件再返回给客户端,同时在本地缓存一份,提供 ...
- 线性回归——lasso回归和岭回归(ridge regression)
目录 线性回归--最小二乘 Lasso回归和岭回归 为什么 lasso 更容易使部分权重变为 0 而 ridge 不行? References 线性回归很简单,用线性函数拟合数据,用 mean squ ...
- Pwnable-mistake
先看看c的源码 #include <stdio.h> #include <fcntl.h> #define PW_LEN 10 #define XORKEY 1 void xo ...