异步tcp通信——APM.Core 解包
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 解包的更多相关文章
- 异步tcp通信——APM.Core 服务端概述
为什么使用异步 异步线程是由线程池负责管理,而多线程,我们可以自己控制,当然在多线程中我们也可以使用线程池.就拿网络扒虫而言,如果使用异步模式去实现,它使用线程池进行管理.异步操作执行时,会将操作丢给 ...
- 异步tcp通信——APM.Server 消息推送服务的实现
消息推送服务 服务器推送目前流行就是私信.发布/订阅等模式,基本上都是基于会话映射,消息对列等技术实现的:高性能.分布式可以如下解决:会话映射可采用redis cluster等技术实现,消息对列可使用 ...
- 异步tcp通信——APM.ConsoleDemo
APM测试 俗话说麻雀虽小,五脏俱全.apm虽然简单,但是可以实现单机高性能消息推送(可以采用redis.kafka等改造成大型分布式消息推送服务器). 测试demo: using System; u ...
- TCP、UDP详解与抓包工具使用
参考:https://www.cnblogs.com/HPAHPA/p/7737641.html TCP.UDP详解 1.传输层存在的必要性 由于网络层的分组传输是不可靠的,无法了解数据到达终点的时间 ...
- TCP通信丢包原因总结
今天在公司问老大,公司的项目底层,是使用的TCP,因为可靠,自动断线重连,在底层都实现了,但是我记得TCP也会有掉包的问题,所以这文章就诞生了——关于TCP掉包的问题,TCP是基于不可靠的网络实现可靠 ...
- TCP封包解包---如有错误,请纠正!
最近遇见很多的关于TCP中封包解包的数据,在TCP节点之间的信息传递,每次传送的内容是结构体,所以每次在传送的时候,要将结构体中的数据进行封包,然后当一端接收到数据之后,要对接收到的buf参数中的数据 ...
- TCP通信粘包问题分析和解决(全)(转)
TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...
- 用Go实现的简易TCP通信框架
接触到GO之后,GO的网络支持非常令人喜欢.GO实现了在语法层面上可以保持同步语义,但是却又没有牺牲太多性能,底层一样使用了IO路径复用,比如在LINUX下用了EPOLL,在WINDOWS下用了IOC ...
- C# TCP socket发送大数据包时,接收端和发送端数据不一致 服务端接收Receive不完全
简单的c# TCP通讯(TcpListener) C# 的TCP Socket (同步方式) C# 的TCP Socket (异步方式) C# 的tcp Socket设置自定义超时时间 C# TCP ...
随机推荐
- vi文本编辑器
vi文本编辑器分为3个模式: 命令模式 插入模式 ex模式 在命令模式下我们可以使用一下功能 o 插入新的行 u 撤销 n yy 复制n行 p 粘贴 / 查找 i 进入插入模式 exc到命令模式 e ...
- bash shell学习-shell基础 (笔记)
When you hoist the sails to cross the sea, you willride the wind and cleave the waves. "长风破浪会有时 ...
- Java学习----变量是什么
1.变量必须拥有的类型 2.变量必须拥有的名字 变量:具备名字和类型的可以存放类型匹配的数据的量 public class Student { public static void main(Stri ...
- 将与系统时间格式不同的字符串格式化为DATETIME类型
若系统时间格式为2012/03/05 08:12:12,那么若将("2012-03-05 08:12:12")格式化为时间变量时会报错,在转化之前先将系统时间格式改变再转换就不会报 ...
- 如何用angularjs制作一个完整的表格之一__创建简单表格
初步接手人生的第一个项目,需要用angularjs制作表格和实现各种功能,因此遇到了各种问题和以前不熟悉的知识点,在此记录下来,以供大家学习交流,解决方式可能并不完善或符合规范,如果大家有更好的方式欢 ...
- thinkphp的nginx配置
thinkphp的nginx配置 server { listen 80; server_name www.abc.com; #charset utf-8; access_log /var/www/ww ...
- 编写可维护的javascript代码--- 2015.11.22(注释)
1.单行注释 // 这是一句单行注释 2.多行注释 /* 这里是代码 */ /* 这里都是注释 1232132 */ java的注释风格 /* * 另一段注释 * 这段注释包含2 ...
- python3.4.2 安装Pillow
Python 3.x 安装Pillow给Python安装Pillow非常简单,使用pip或easy_install只要一行代码即可.在命令行使用PIP安装: pip install Pillow或在命 ...
- 使用Struts1完成用户登录功能
1.Struts框架 框架(framework):就是一系列代码和开发模式的整合,使用框架后,所有开发人员都会按照框架提供的规范进行开发,使代码更容易维护和扩展. 使用框架的优点: 1) 易于维护 ...
- PHP 错误提示
HTTP/1.1 200 OKServer: nginxDate: Thu, 20 Jun 2013 03:06:10 GMTContent-Type: text/html; charset=utf- ...