高性能TcpServer - 3.命令通道(处理:掉包,粘包,垃圾包)
Posted chen1880
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高性能TcpServer - 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 - 3.命令通道(处理:掉包,粘包,垃圾包)的主要内容,如果未能解决你的问题,请参考以下文章
高性能TcpServer - 4.文件通道(处理:文件分包,支持断点续传)
CocosCreator游戏性能优化(3):GPU优化之降低计算分辨率