高性能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远程执行命令 服务 ...
随机推荐
- MySQL学习——操作自定义函数
MySQL学习——操作自定义函数 摘要:本文主要学习了使用DDL语句操作自定义函数的方法. 了解自定义函数 是什么 自定义函数是一种与存储过程十分相似的过程式数据库对象.它与存储过程一样,都是由SQL ...
- Java生鲜电商平台-深刻理解电商的库存架构与解决方案
Java生鲜电商平台-深刻理解电商的库存架构与解决方案 说明:一般电商的库存都是跟SKU相关联的,那么怎么样才能进行SKU的库存管理呢?有以下几种方式与方法: 一.七大库存分类 首先得学习什么是库存, ...
- JavaWeb之Servlet(3)
Servlet(3) HttpServletRequest 该类的对象封装了所以客户端提交过来的数据 获取所有请求头数据 public java.util.Enumeration<E> g ...
- 使用AVFoundation完成照片拍摄存储相册, 开启关闭闪光灯, 切换摄像头
在开启这个旅程之前, 请记住, AVFoundation是一个复杂的工具. 在很多情况下, 我我们使用苹果默认的API(比如:UIImagePickerController)就足够了. 在您阅读之前, ...
- 【JavaScript】使用document.write输出覆盖HTML问题
您只能在 HTML 输出中使用 document.write.如果您在文档加载后使用该方法,会覆盖整个文档. 分析 HTML输出流是指当前数据形式是HTML格式的数据,这部分数据正在被导出.传输或显示 ...
- git零基础快速入门实战,重点讲解,在实际生产中整合idea对版本、分支的管理等
1.什么是版本管理 (多人协作)项目中常见的问题: 代码放在什么地方 ?? 同步(到服务器),代码的冲突问题 ?? 服务器访问权限问题 ?? (代码)服务器内容修改的细节 ?? 项目版本的发布 ?? ...
- [20191003]oracle number类型存储转化脚本.txt
[20191003]oracle number类型存储转化脚本.txt --//完善脚本,增加支持科学记数法.比如1e4之类的写法. 2.测试:$ cat test.txt012251234100-4 ...
- [20190510]rman备份的疑问8.txt
[20190510]rman备份的疑问8.txt --//上午测试rman备份多个文件,探究input memory buffer 的问题.--//补充测试5个文件的情况.--//http://blo ...
- MySQL第三课
首先创建一个数据库: CREATE DATABASE ku; Query OK, 1 row affected 查看一下是否有此数据库: SHOW DATABASES; +-------------- ...
- 在C++工程上添加CUDA编译环境
1.直接在新建工程的时候选择CUDA,这样的工程既能编译C++也能编译CU 2.在已有的C++工程上添加CUDA编译环境 右键工程-->生成依赖项-->生成自定义-->勾选CUDA ...