异步SOCKET分包和组包的一种通用算法

异步SOCKET分包和组包的一种算法

unit uFun;
// 应用协议
// cxg 2016-9-23

interface

uses
SysUtils, Classes, PeachCtrl.Net.IocpTcpServer, System.Generics.Collections
;

const // 包长
pack_len = 8192;

const // 命令分类
cmd_qry_req = 1;
cmd_qry_res = 2;
cmd_post_req = 3;
cmd_post_res = 4;
cmd_up_file_req = 5;
cmd_up_file_res = 6;
cmd_down_file_req = 7;
cmd_down_file_res = 8;
cmd_data = 9;

type
THead = packed record // 包头
cmd: Byte;
len: Integer;
packNo: Integer;
packQty: Integer;
ver: Byte;
end;

type
TTask = record // 一次任务
context: Integer;
body: TBytes;
end;

PTTask = ^TTask;

var
g_TaskList: TList<PTTask>; // 任务队列
g_tmpList: TList<PTTask>; // 临时队列

function ValidHead(AHead: THead): Boolean;
function GetTask(AContext: TCustomIocpTcpServer.TPerHandleData): PTTask;
procedure ProcessRecved(AContext: TCustomIocpTcpServer.TPerHandleData);

implementation

function ValidHead(AHead: THead): Boolean;
begin
Result := (AHead.cmd >= 1) and (AHead.len > SizeOf(THead)) and (AHead.packNo >= 1) and (AHead.packQty >= 1);
end;

function GetTask(AContext: TCustomIocpTcpServer.TPerHandleData): PTTask;
var
i: Integer;
begin
Result := nil;
if (AContext = nil) or (g_tmpList.Count = 0) then
Exit;
System.TMonitor.Enter(g_tmpList);
try
for i := 0 to g_tmpList.Count - 1 do
begin
if g_tmpList.Items[i].context = Integer(AContext) then
begin
Result := g_tmpList.Items[i];
Exit;
end;
end;
finally
System.TMonitor.Exit(g_tmpList);
end;
end;

procedure ProcessRecved(AContext: TCustomIocpTcpServer.TPerHandleData);
var
pTask: PTTask;
buf: TBytes;
head: THead;
bodyLen: Integer;
headLen: Integer;
begin
headLen := SizeOf(THead); // 包头长
if AContext.RingBuffer.NoProcessBufLen < headLen then
Exit;
AContext.RingBuffer.Peep(head, headLen); // 取包头
if not uFun.ValidHead(head) then // 校验包头
Exit;

if head.packQty = 1 then // 一批次只有一个包
begin
if AContext.RingBuffer.NoProcessBufLen < head.len then
Exit;
New(pTask);
pTask.context := Integer(AContext);
bodyLen := head.len - headLen;
SetLength(pTask.body, bodyLen);
SetLength(buf, head.len);
AContext.RingBuffer.Pop(buf[0], head.len);
Move(buf[headLen], pTask.body[0], bodyLen);
g_TaskList.Add(pTask); // 提交任务队列
end
else if head.packQty > 1 then // 一批次有多个包
begin
if head.packNo = 1 then // 首包
begin
if AContext.RingBuffer.NoProcessBufLen < pack_len then
Exit;
New(pTask);
pTask.context := Integer(AContext);
SetLength(pTask.body, head.len - head.packQty * headLen); // 一次分好缓存
SetLength(buf, pack_len);
AContext.RingBuffer.Pop(buf[0], pack_len);
bodyLen := pack_len - headLen;
Move(buf[headLen], pTask.body[0], bodyLen);
g_tmpList.Add(pTask); // 提交临时队列
end
else
if head.packNo > 1 then // 非首包
begin
if AContext.RingBuffer.NoProcessBufLen < head.len then
Exit;
pTask := GetTask(AContext);
if pTask = nil then
Exit;
SetLength(buf, head.len);
AContext.RingBuffer.Pop(buf[0], head.len);
bodyLen := head.len - headLen;
Move(buf[headLen], pTask.body[(head.packNo - 1) * bodyLen], bodyLen);
g_tmpList.Add(pTask); // 提交临时队列
if head.packNo = head.packQty then // 包都收齐了
begin
g_TaskList.Add(pTask); // 提交任务队列
System.TMonitor.Enter(g_tmpList);
try
g_tmpList.Delete(g_tmpList.IndexOf(pTask)); // 从临时队列中删除
finally
System.TMonitor.Exit(g_tmpList);
end;
end;
end;
end;
end;

end.

时间: 2024-11-05 12:10:06

异步SOCKET分包和组包的一种通用算法的相关文章

Socket之UDP分包组包

一般传输大的文件和信息的时候需要涉及到分包和组包,方法有很多,下面一种是借鉴了别人的思路,供大家参考哈 分包 1.取出需要传输的文件和字符的长度和大小放入缓存区里面: 2.设定固定传输的长度,用需要传输的长度除以固定传输的长度都可以得到需要传输的次数: 3.传输一次字节流中包括(文件名字.文件名字大小.顺序.数据总块数.数据长度.数据总长度) 4.包组装完成后,都剩下发送:当确定到接收方收到后,在传下一次包: FileStream m = new FileStream(FullName, Fil

DELPHI高性能大容量SOCKET并发(四):粘包、分包、解包

DELPHI高性能大容量SOCKET并发(四):粘包.分包.解包 粘包 使用TCP长连接就会引入粘包的问题,粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾.粘包可能由发送方造成,也可能由接收方造成.TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据,造成多个数据包的粘连.如果接收进程不及时接收数据,已收到的数据就放在系统接收缓冲区,用户进程读取数据时就可能同时读到多个数据包. 粘包一般的解决办法是制定通讯协议,由协议来规

蓝牙收发数据过大需要分包-组包处理

{ static unsigned char Bt_RxData_Merge_Status=Bt_RxData_Merge_Defaul_Status; //意思是一个iAP数据包被一个Bt_RxData发送 if(Bt_RxData_Merge_Status==Bt_RxData_Merge_Defaul_Status && 0x55==Bt_RxData->MsgData[2]) { //首先检测iAP数据包的长度 //iAP数据包:Length域数据包含1或3字节,这取决于载荷

Qt Socket 收发图片——图像拆包、组包、粘包处理

之前给大家分享了一个使用python发图片数据.Qt server接收图片的Demo.之前的Demo用于传输小字节的图片是可以的,但如果是传输大的图片,使用socket无法一次完成发送该怎么办呢?本次和大家分享一个对大的图片拆包.组包.处理粘包的例子. 程序平台:ubuntu . Qt 5.5.1 为了对接收到的图像字节进行组包,我们需要对每包数据规定协议,协议如下图: 每包数据前10个字节对应含义如下:前两个字节对应数据包类型,中间四字节预留,最后四字节是包内数据实际长度.对应协议图片更方便刚

TCP 组包和拆包算法

/************************************* 文件名: server.c TCP 组包和拆包实现算法 作者: 马中海 QQ: 284358503 Email: [email protected] */ #include <stdlib.h> #include <sys/types.h> #include <stdio.h> #include <sys/socket.h> #include <linux/in.h>

C# 异步Socket

C# 异步Socket (BeginXXXX)服务器代码 前言: 1.最近维护公司的一个旧项目,是Socket通讯的,主要用于接收IPC(客户端)发送上来的抓拍图像,期间要保持通讯,监测数据包并进行处理.但是看之前那人写的代码个人觉得并不是很适合自己,于是就重写了,不过项目暂时弃置了,为了以后能够方便使用,也方便更多像我一样还是渣渣程序员的人,记录一些心得.我还是坚信那句话,分享才能够进步得更快 2.其实在做之前我对这个东西了解也很少,毕竟以我的认识,在国内C#更多地是用来开发网站,于是也在网上

Unity3d 游戏与C#服务器 异步Socket 交互 (一)

Unity3d中提供了Socket供开发者使用,语法和.net中的一致. 一般来说,对于手游客户端,分为两个线程,一个是GLES渲染,另一个就是Socket线程了. 文章转自(http://blog.csdn.net/huutu) 不论是服务器,还是客户端.其间的数据包的接收与发送,都是通过Socket. 比如客户端要登录,我们就新建一个Socket,Connect到帐号服务器.帐号服务器一直在等待客户端的连接,客户端连接进来之后就准备发送接收数据包了. 文章转自(http://blog.csd

采用异步socket实现客户端和服务端通信的demo

MAC系统基于UNIX的核心系统增强了系统的稳定性.性能以及响应能力.由于unix需要付费,以及版本基本上不更新,很多采用unix系统的电脑转用linux,unix处于停滞不前状态,而linux由于是开源的,免费的,所以全球很多技术大牛在不断改进它,给它增加新技术,增加新理念,是它日新月异的发展.所以mac os后期主要借鉴linux的新技术,所以现在的mac os更像linux而非unix.可以说苹果系统是从linux和unix演化而来的,所以linux的socket的编程对苹果系统仍然有效.

Windows 8 Metro 关于StreamSocket与原异步Socket

前一篇 <Windows 8 Metro 关于 StreamSocket MSDN示例阅读>我们基本懂得如何通过StreamSocket 做监听.连接.发送接收数据. 同时前一篇留下的几个疑问,我们在这里进行实验和解答. 在“原有的异步Socket”连接方式与现在WIN8 Metro App 的StreamSocket 如何通信呢? 首先解释下这里说的“原有的异步Socket” 是什么. 在await/async 关键字出现前,我们的Socket异步是依靠System.Net 以及 Syste