DIOCP开源项目-定义自己要发送的数据结构(MyObject)
印象中网络程序都是sendBuffer和recvBuffer来发送数据和接收数据,本次Demo演示如何定义定义一个自己的对象,然后我们按照OO的思想直接进行对象的发送和接收,先上个流程图。

下面是客户端发送和接收的测试代码。

下面我们来看看详细的设计。
第一步(TMyObject):首先我们需要设计一个需要进行传输的对象.
type
TMyObject = class(TObject)
private
FDataString:String;
FOle:OleVariant;
public
property DataString:String read FDataString write FDataString;
property Ole:OleVariant read FOle write FOle;
end;
对象很简单,一个DataString,和Ole。Ole可以存放各种数据。
第二步:编写客户端发送和接收过程
发送对象:
把对象变成可以传递的格式,然后用IdTcpClient进行发送,编码的格式为:字符串长度+ole数据流长度 + 字符串数据 + Ole流数据,代码很简单如下。
class function TMyObjectCoderTools.Encode(pvSocket: TIdTcpClient;
pvObject: TObject): Integer;
var
lvMyObj:TMyObject;
lvOleStream:TMemoryStream;
lvOleLen, lvStringLen:Integer;
begin
lvMyObj := TMyObject(pvObject);
lvOleStream := TMemoryStream.Create;
try
WriteOleVariant(lvMyObj.Ole, lvOleStream);
lvOleLen := lvOleStream.Size;
lvOleStream.Position := 0;
//字符串长度+ole长度 + 字符串数据 + Ole数据
lvStringLen := Length(AnsiString(lvMyObj.DataString));
Result := 0;
Result := Result + sendBuffer(pvSocket,@lvStringLen,sizeOf(Integer));
Result := Result + sendBuffer(pvSocket,@lvOleLen,sizeOf(Integer));
Result := Result + sendBuffer(pvSocket,PAnsiChar(AnsiString(lvMyObj.DataString)), lvStringLen);
Result := Result + sendBuffer(pvSocket,lvOleStream.Memory, lvOleLen);
//result 发送长度
finally
lvOleStream.Free;
end;
end;
接收对象:
用IdTcpClient接收数据,把接收到的数据按照协议格式进行拆分,放入到对象的属性中,依次读取字符串长度+ole长度 + 字符串数据 + Ole数据,代码如下
class function TMyObjectCoderTools.Decode(pvSocket: TIdTcpClient;
pvObject: TObject): Boolean;
var
lvStringLength, lvStreamLength:Integer;
lvData, lvTemp:AnsiString;
lvStream:TStream;
l, lvRemain:Integer;
lvBufData:PAnsiChar;
begin
Result := false;
lvStringLength := 0;
lvStreamLength := 0;
recvBuffer(pvSocket, @lvStringLength, SizeOf(Integer));
recvBuffer(pvSocket, @lvStreamLength, SizeOf(Integer));
if (lvStringLength = 0) and (lvStreamLength = 0) then exit;
//读取json字符串
if lvStringLength > 0 then
begin
SetLength(lvData, lvStringLength);
l := recvBuffer(pvSocket, PAnsiChar(lvData), lvStringLength);
TMyObject(pvObject).DataString := lvData;
end;
//读取Ole值
if lvStreamLength > 0 then
begin
GetMem(lvBufData, lvStreamLength);
try
recvBuffer(pvSocket, lvBufData, lvStreamLength);
lvStream := TMemoryStream.Create;
try
lvStream.WriteBuffer(lvBufData^, lvStreamLength);
lvStream.Position := 0;
TMyObject(pvObject).Ole := ReadOleVariant(lvStream);
finally
lvStream.Free;
end;
finally
FreeMem(lvBufData, lvStreamLength);
end;
end;
Result := true;
end;
第三步:服务端的接收和发送,服务端接收到数据后也需要解码,返回数据也需要编码。在服务端需要编写编码器,过程与客户端的发送和接收类似。
接收的解码器。
TMyObjectDecoder = class(TIOCPDecoder)
public
/// <summary>
/// 解码收到的数据,如果有接收到数据,调用该方法,进行解码
/// </summary>
/// <returns>
/// 返回解码好的对象
/// </returns>
/// <param name="inBuf"> 接收到的流数据 </param>
function Decode(const inBuf: TBufferLink): TObject; override;
end;
function TMyObjectDecoder.Decode(const inBuf: TBufferLink): TObject;
var
lvStringLen, lvStreamLength:Integer;
lvData:AnsiString;
lvBuffer:array of Char;
lvBufData:PAnsiChar;
lvStream:TMemoryStream;
lvValidCount:Integer;
lvBytes:TIOCPBytes;
begin
Result := nil;
//如果缓存中的数据长度不够包头长度,解码失败<字符串长度,Ole流长度>
lvValidCount := inBuf.validCount;
if (lvValidCount < SizeOf(Integer) + SizeOf(Integer)) then
begin
Exit;
end;
//记录读取位置
inBuf.markReaderIndex;
inBuf.readBuffer(@lvStringLen, SizeOf(Integer));
inBuf.readBuffer(@lvStreamLength, SizeOf(Integer));
//如果缓存中的数据不够json的长度和流长度<说明数据还没有收取完毕>解码失败
lvValidCount := inBuf.validCount;
if lvValidCount < (lvStringLen + lvStreamLength) then
begin
//返回buf的读取位置
inBuf.restoreReaderIndex;
exit;
end else if (lvStringLen + lvStreamLength) = 0 then
begin
//两个都为0<两个0>客户端可以用来作为自动重连使用
TIOCPFileLogger.logDebugMessage('接收到一次[00]数据!');
Exit;
end;
//解码成功
Result := TMyObject.Create;
//读取json字符串
if lvStringLen > 0 then
begin
SetLength(lvData, lvStringLen);
inBuf.readBuffer(PAnsiChar(lvData), lvStringLen);
TMyObject(Result).DataString := lvData;
end;
//读取Ole值
if lvStreamLength > 0 then
begin
GetMem(lvBufData, lvStreamLength);
try
inBuf.readBuffer(lvBufData, lvStreamLength);
lvStream := TMemoryStream.Create;
try
lvStream.WriteBuffer(lvBufData^, lvStreamLength);
lvStream.Position := 0;
TMyObject(Result).Ole := ReadOleVariant(lvStream);
finally
lvStream.Free;
end;
finally
FreeMem(lvBufData, lvStreamLength);
end;
end;
end;
发送的编码器
TMyObjectEncoder = class(TIOCPEncoder)
public
/// <summary>
/// 编码要发送的对象
/// </summary>
/// <param name="pvDataObject"> 要进行编码的对象 </param>
/// <param name="ouBuf"> 编码好的数据
/// 字符串长度+ole长度 + 字符串数据 + Ole数据
/// </param>
procedure Encode(pvDataObject:TObject; const ouBuf: TBufferLink); override;
end;
procedure TMyObjectEncoder.Encode(pvDataObject: TObject;
const ouBuf: TBufferLink);
var
lvMyObj:TMyObject;
lvOleStream:TMemoryStream;
lvOleLen, lvStringLen:Integer;
begin
lvMyObj := TMyObject(pvDataObject);
lvOleStream := TMemoryStream.Create;
try
WriteOleVariant(lvMyObj.Ole, lvOleStream);
lvOleLen := lvOleStream.Size;
lvOleStream.Position := 0;
//字符串长度+ole长度 + 字符串数据 + Ole数据
lvStringLen := Length(AnsiString(lvMyObj.DataString));
ouBuf.AddBuffer(@lvStringLen,sizeOf(Integer));
ouBuf.AddBuffer(@lvOleLen,sizeOf(Integer));
ouBuf.AddBuffer(PAnsiChar(AnsiString(lvMyObj.DataString)), lvStringLen);
ouBuf.AddBuffer(lvOleStream.Memory, lvOleLen);
finally
lvOleStream.Free;
end;
end;
然后在启动IOCP的之前注册编码器和解码器
FDecoder := TMyObjectDecoder.Create;
FEncoder := TMyObjectEncoder.Create;
//注册解码器
TIOCPContextFactory.instance.registerDecoder(FDecoder);
//注册编码器
TIOCPContextFactory.instance.registerEncoder(FEncoder);
服务端然后就可以在ClientContext中编写相应的逻辑处理代码就行了
procedure TClientContext.dataReceived(const pvDataObject:TObject);
var
lvMyObject:TMyObject;
begin
lvMyObject := TMyObject(pvDataObject);
try
//直接回传
writeObject(lvMyObject);
except
on E:Exception do
begin
lvMyObject.DataString := E.Message;
writeObject(lvMyObject);
end;
end;
end;
本次DEMO使用XE5进行编写,可以在D7-XE5中可以运行。
Demo在已经上传在SVN中
>>>>>>DIOCP讨论群:320641073
>>>>>>SVN源码和DEMO下载:https://code.google.com/p/diocp/
DIOCP开源项目-定义自己要发送的数据结构(MyObject)的更多相关文章
- DIOCP开源项目-高效稳定的服务端解决方案(DIOCP + 无锁队列 + ZeroMQ + QWorkers) 出炉了
[概述] 自从上次发布了[DIOCP开源项目-利用队列+0MQ+多进程逻辑处理,搭建稳定,高效,分布式的服务端]文章后,得到了很多朋友的支持和肯定.这加大了我的开发动力,经过几个晚上的熬夜,终于在昨天 ...
- DIOCP开源项目-DIOCP3的重生和稳定版本发布
DIOCP3的重生 从开始写DIOCP到现在已经有一年多的时间了,最近两个月以来一直有个想法做个 30 * 24 稳定的企业服务端架构,让程序员专注于逻辑实现就好.虽然DIOCP到现在通讯层已经很稳定 ...
- DIOCP开源项目-DIOCP3直接发送对象,帮你处理粘包问题
该DEMO演示,如何在客户端与服务端之间直接传递TStream对象,让你专注于处理数据逻辑,可以忽略处理网络传输间粘包的问题. 上面由服务端向所有的客户端推送一个消息TMemoryStream对象(该 ...
- 【DIOCP开源项目】实际应用案例
案例1 DIOCP是Delphi下进行IOCP服务端通讯开发的一个非常好的开源框架,稳定.高效并且使用起来十分简单. 自己两个多月之前因为需要使用Delphi开发一个TCP服务端,当时也是到处爬文,希 ...
- DIOCP开源项目-利用队列+0MQ+多进程逻辑处理,搭建稳定,高效,分布式的服务端
最近头脑里面一直在想怎么样让能让大家基于DIOCP上写出稳定的服务端程序.很多朋友问我,你DIOCP稳定吗,我可以用他来做三层服务器吗? 当时我是这样回答的,我只能保证DIOCP底层通信的稳定. 说实 ...
- DIOCP开源项目-Delphi高性能无锁队列(lock-free)
最近想在DIOCP中加入任务调度线程,DIOCP的工作线程作为生产者(producer)将接受到的数据对象,投递到任务调度线程中,然后统一进行分配.然而这一切都需要一个队列, 这几天都在关注无锁队列. ...
- DIOCP开源项目-数据库连接池的使用<多帐套数据库>
很久没有写DIOCP的Demo了,主要公司的事情太繁琐,工作之余都不想动了,之前承若的群里面朋友们的DEMO,昨天晚上恶补了一下,把对数据库连接池的操作加入到了Demo中,大家可以通过SVN下载到最新 ...
- DIOCP开源项目-DIOCP3的LoadRunner11测试报告
昨天有个多年的群友(B3.Locet)用LoadRunner11对DIOCP3做压力测试,说测试的时候出现了大量的10053,10054的报告.昨天晚上下载了个LoadRunner11, 今天捣鼓了下 ...
- DIOCP开源项目-DIOCP3 大文件的传输DEMO<断点续传>
首先该DEMO在StreamCoder上面做的改动,期间导致StreamCoderDEMO经常出现问题,导致大家运行的时候,频频出现问题,表示道歉. 以下是测试的结果,从服务器下载传输了一个3G左右的 ...
随机推荐
- Oracle的关于小数的使用
整理了部分Oracle的关于小数的使用方法,如下 --round(x,[y]) 舍入到小数点右边y位的x值 ) from dual; --1.235 --trunc(x,[y])返回截尾到y位小数的x ...
- 一行Python
只要理解了函数式编程,使用神奇的Lambda, 配合列表推导以及复杂一点的判断语句,任何的python 代码都可以转换成一行代码 1.打印九九乘法表 # 打印九九乘法表 print('\n'.join ...
- suricata 的安装编译
最近打算研究suricata源码,下载并安装了稳定版3.2.3版本,操作系统是Ubuntu 16.04.2 LTS,下来描述我的操作过程: 1,安装suricata运行可能用到的库: sudo apt ...
- 【转】GDB中应该知道的几个调试方法
文章来源:http://coolshell.cn/articles/3643.html GDB中应该知道的几个调试方法 2011年02月10日 陈皓 评论 40 条评论 70,776 人阅读 七.八 ...
- arping 帮助——翻译
[root@localhost ~]# arping --helparping: invalid option -- '-'Usage: arping [-fqbDUAV] [-c count] [- ...
- 使用 awstats 分析 Nginx 的访问日志(IBM)
前言 在我的上一篇文章<使用 Nginx 提升网站访问速度>中介绍了 Nginx 这个 HTTP 服务器以及如何通过它来加速网站的访问速度.在实际的网站运营中,我们经常需要了解到网站的访问 ...
- MySQL高可用性大杀器之MHA
MySQL高可用性大杀器之MHA 提到MySQL高可用性,很多人会想到MySQL Cluster,亦或者Heartbeat+DRBD,不过这些方案的复杂性常常让人望而却步,与之相对,利用MySQL ...
- HDU 1896 Stones (优先队列)
Stones Time Limit: 5000/3000 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others)Total Subm ...
- C# 用timer做成服务后 timer_Tick () 为什么不执行?
不能使用 窗体的 Timer,他只能在窗体中使用,服务中无法使用请使用 System.Timers.Timer类 protected override void OnStart(string[] ar ...
- 在debug模式下引入一些性能检测工具
我们经常在debug模式下使用一些性能检测工具,例如blockCannary,leakCannary.Stetho等,但是我们release的时候又不需要这些检测工具,通常情况下我们的做法是在buil ...