TCP通信解包

  虽说这是一个老生长谈的问题,不过网上基本很少见完整业务;或多或少都没有写完或者存在bug。接收到的数据包可以简单分成:小包、大包、跨包三种情况,根据这三种情况作相对应的拆包处理,示例如下:

 /*****************************************************************************************************
* 本代码版权归@wenli所有,All Rights Reserved (C) 2015-2016
*****************************************************************************************************
* CLR版本:4.0.30319.42000
* 唯一标识:7a846c42-665d-4628-b91f-9d58b670437d
* 机器名称:WENLI-PC
* 联系人邮箱:wenguoli_520@qq.com
*****************************************************************************************************
* 项目名称:$projectname$
* 命名空间:APM.Core
* 类名称:UserToken
* 创建时间:2016/11/30 16:56:29
* 创建人:wenli
* 创建说明:
*****************************************************************************************************/
using System;
using System.Net.Sockets; namespace APM.Core
{
/// <summary>
/// tcp用户信息
/// </summary>
public class UserToken
{
private int offset, count = ; private byte[] _myBuffer; private byte[] _myLenBuffer; public int MaxBufferSize
{
get; private set;
} /// <summary>
/// 用户标识
/// </summary>
public string ID
{
get; set;
}
/// <summary>
/// 连接的客户
/// </summary>
public Socket Client
{
get; set;
} /// <summary>
/// 会话验证码
/// </summary>
public int Auth
{
get; set;
} public UserToken(int maxBufferSize = * )
{
this.MaxBufferSize = maxBufferSize;
this.ReceiveBuffer = new byte[this.MaxBufferSize];
} /// <summary>
/// 处理收取数据
/// 解包
/// </summary>
/// <param name="receiveData"></param>
/// <param name="action"></param>
internal void UnPackage(byte[] receiveData, Action<TcpPackage> action)
{
//当前包取内容的
if (offset == )
{
var packageLength = ;
if (this._myLenBuffer != null) //长度不完整的(包头不完整的)
{
//调整receiveData包内容
var nData = new byte[this._myLenBuffer.Length + receiveData.Length];
Buffer.BlockCopy(this._myLenBuffer, , nData, , this._myLenBuffer.Length);
Buffer.BlockCopy(receiveData, , nData, this._myLenBuffer.Length, receiveData.Length);
receiveData = nData;
nData = null;
this._myLenBuffer = null;
}
else //全新包(包头完整的)
{
packageLength = TcpPackage.GetLength(receiveData);
if (packageLength == )
return;
}
if (packageLength < receiveData.Length)
{
var package = TcpPackage.Parse(receiveData);
if (action != null && package != null)
{
action(package);
} var slen = TcpPackage.GetLength(receiveData, package.Length);
if (slen >= )
{
var next = new byte[receiveData.Length - package.Length];
Buffer.BlockCopy(receiveData, package.Length, next, , next.Length);
this.UnPackage(next, action);
}
}
else if (packageLength == receiveData.Length)
{
var package = TcpPackage.Parse(receiveData);
if (action != null && package != null)
{
action(package);
}
}
else if (packageLength > receiveData.Length)
{
this.count = packageLength;
this._myBuffer = new byte[packageLength];
Buffer.BlockCopy(receiveData, , this._myBuffer, , receiveData.Length);
this.offset = receiveData.Length;
}
receiveData = null; }
else //跨包取内容的
{
if (receiveData.Length + offset < count) //包内容超出
{
Buffer.BlockCopy(receiveData, , this._myBuffer, offset, receiveData.Length);
offset += receiveData.Length;
}
else if (receiveData.Length + offset >= count) //包内容短的
{
var packageLast = count - offset;
Buffer.BlockCopy(receiveData, , this._myBuffer, offset, packageLast);
var package = TcpPackage.Parse(this._myBuffer);
if (action != null && package != null)
{
action(package);
}
this._myBuffer = null;
count = offset = ;
var receiveLast = receiveData.Length - packageLast;
if (receiveLast >= )//包含包头长度
{
var packageLength = TcpPackage.GetLength(receiveData, packageLast);
if (packageLength > )
{
if (receiveLast > packageLength)
{
var nextData = new byte[receiveLast];
Buffer.BlockCopy(receiveData, packageLast, nextData, , receiveLast);
this.UnPackage(nextData, action);
}
else
{
this._myBuffer = new byte[packageLength];
Buffer.BlockCopy(receiveData, packageLast, this._myBuffer, , receiveLast);
offset = receiveLast;
count = packageLength;
}
}
else
{
this._myBuffer = null;
count = offset = ;
this._myLenBuffer = null;
}
}
else if (receiveLast > )//不包含包头长度
{
this._myLenBuffer = new byte[receiveLast];
Buffer.BlockCopy(receiveData, packageLast, this._myLenBuffer, , receiveLast);
if (TcpPackage.GetLength(this._myLenBuffer) == )
{
this._myLenBuffer = null;
}
this._myBuffer = null;
count = offset = ;
}
}
receiveData = null;
}
} public byte[] ReceiveBuffer
{
get; set;
} public void ClearReceiveBuffer()
{
for (int i = ; i < this.ReceiveBuffer.Length; i++)
{
this.ReceiveBuffer[i] = ;
}
}
}
}

  有了解包就可以发超长消息、文件等

异步tcp通信——APM.Core 服务端概述

异步tcp通信——APM.Core 解包

异步tcp通信——APM.Server 消息推送服务的实现

异步tcp通信——APM.ConsoleDemo

转载请标明本文来源:http://www.cnblogs.com/yswenli/
更多内容欢迎star作者的github:https://github.com/yswenli/APM
如果发现本文有什么问题和任何建议,也随时欢迎交流~

异步tcp通信——APM.Core 解包的更多相关文章

  1. 异步tcp通信——APM.Core 服务端概述

    为什么使用异步 异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池.就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理.异步操作执行时,会将操作丢给 ...

  2. 异步tcp通信——APM.Server 消息推送服务的实现

    消息推送服务 服务器推送目前流行就是私信.发布/订阅等模式,基本上都是基于会话映射,消息对列等技术实现的:高性能.分布式可以如下解决:会话映射可采用redis cluster等技术实现,消息对列可使用 ...

  3. 异步tcp通信——APM.ConsoleDemo

    APM测试 俗话说麻雀虽小,五脏俱全.apm虽然简单,但是可以实现单机高性能消息推送(可以采用redis.kafka等改造成大型分布式消息推送服务器). 测试demo: using System; u ...

  4. TCP、UDP详解与抓包工具使用

    参考:https://www.cnblogs.com/HPAHPA/p/7737641.html TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间 ...

  5. TCP通信丢包原因总结

    今天在公司问老大,公司的项目底层,是使用的TCP,因为可靠,自动断线重连,在底层都实现了,但是我记得TCP也会有掉包的问题,所以这文章就诞生了——关于TCP掉包的问题,TCP是基于不可靠的网络实现可靠 ...

  6. TCP封包解包---如有错误,请纠正!

    最近遇见很多的关于TCP中封包解包的数据,在TCP节点之间的信息传递,每次传送的内容是结构体,所以每次在传送的时候,要将结构体中的数据进行封包,然后当一端接收到数据之后,要对接收到的buf参数中的数据 ...

  7. TCP通信粘包问题分析和解决(全)(转)

    TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...

  8. 用Go实现的简易TCP通信框架

    接触到GO之后,GO的网络支持非常令人喜欢.GO实现了在语法层面上可以保持同步语义,但是却又没有牺牲太多性能,底层一样使用了IO路径复用,比如在LINUX下用了EPOLL,在WINDOWS下用了IOC ...

  9. C# TCP socket发送大数据包时,接收端和发送端数据不一致 服务端接收Receive不完全

    简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的TCP Socket (异步方式) C# 的tcp Socket设置自定义超时时间 C# TCP ...

随机推荐

  1. Linux下安装LANMP环境

    记录下来,方便下次再用时从头查找资料 首先是操作系统和php环境:CentOS 6.5 64位(之所以不是7.0是因为本身对linux不熟,而7.0改变挺大的,搜索查询资料也不好搜索),Php版本:5 ...

  2. 用soaplib的django webserver

    前面写过怎么利用suds来调用webservicePython调用基于https协议的SOAP WebService,这篇讲的是如何用soaplib开发SOAP WebService(最近发现国外开源 ...

  3. soket客户端程序(一)

    soket客户端主要完成以下步骤: 1.建立soket套接字(将套接字理解为一个通道) 2.建立连接 3.向服务器发送http请求 4.接收得到的数据 5.关闭连接 6.本地处理得到的数据 http: ...

  4. Scut:GameWebSocketHost 解析

    想使用 Scut 做的是一个短连接项目,所以先直接看 GameWebSocketHost 了. 先来看下 GameWebSocketHost 的成员: protected bool EnableHtt ...

  5. 伯克利DB的一个BUG

    一旦没有手工close掉伯克利DB,则缓存里的数据不会主动写入到文件中,因此非常难于排查这个BUG,记录在这里提醒自己

  6. Altera quartus II遇到的问题

    编译时提示: Warning (13024): Output pins are stuck at VCC or GND Warning (13410): Pin "SCLK" is ...

  7. Milking Grid

    poj2185:http://poj.org/problem?id=2185 题意:在一个字符矩阵中,找一个最小的字符子矩阵,使其能够覆盖整个矩阵. 题解:在KMP中i-next[i]是这能够覆盖这个 ...

  8. 哈希集合——hashSet

    /**     哈希集合特点:存取顺序不确定,同一个哈希值的位置可以存放多个元素,                   哈希集合存放元素的时候是先判断哈希地址值:hashCode()是否相同,如果不同 ...

  9. js 数字添加逗号,格式化数字

    function addCommas(nStr) { nStr += ''; x = nStr.split('.'); x1 = x[0]; x2 = x.length > 1 ? '.' + ...

  10. NOI 2005 维修数列

    妈妈呀我终于过了!!!原来是数据坑我!!! 弃疗弃疗弃疗弃疗!!!!我调了一天呢....被GET_SUM 8 0打败了.... 啥也不说了....还是我太年轻.... 更新了一下常数,跑的还是可以的: ...