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左右的 ... 
随机推荐
- 【转载】Mysql主从复制、和MySQL集群(主主复制)
			转载:https://www.cnblogs.com/phpstudy2015-6/p/6485819.html 请同时参考和结合这篇文件进行处理:https://blog.csdn.net/envo ... 
- [转]NLP Tasks
			Natural Language Processing Tasks and Selected References I've been working on several natural langu ... 
- iOS – 单例模式写一次就够了
			一. 单例模式简介 单例模式的作用 可以保证在程序运行过程,一个类只有一个实例,而且该实例易于供外界访问 从而方便地控制了实例个数,并节约系统资源 单例模式的使用场合 在整个应用程序中,共享一份资源( ... 
- Kafka 如何读取指定topic中的offset   -------------用来验证分区是不是均衡!!!(__consumer_offsets)(已验证!)
			我现在使用的是librdkafka 的C/C++ 的客户端来生产消息,用flume来辅助处理异常的数据,,, 但是在前段时间,单独使用flume测试的时候发现,flume不能对分区进行负载均衡!同一个 ... 
- php.ini 中文版
			作者:金步国 版权声明 本文作者是一位开源理念的坚定支持者,所以本文虽然不是软件,但是遵照开源的精神发布. 无担保:本文作者不保证作品内容准确无误,亦不承担任何由于使用此文档所导致的损失. 自由使用: ... 
- 如何用STAF进行自动化测试分布式运行
			本文的目的在于引导读者去了解STAF及如何调用其接口去实现自动化测试的分布式动行. 提到分布式运行,很多人想到了Jenkins,Jenkins里面有个node插件,可以去分派任务给slave,Jenk ... 
- 进阶之路(中级篇) - 016 温湿度传感器DHT11
			如果想使用 Arduino 开发板驱动 DHT11 来获取温湿度的时候建议使用第三方的库,这样可以加快程序的开发速度,而且不容易出错,下面的代码我已经安转了第三方的库了.详细的安装方法请参考极客先锋的 ... 
- 【Oracle】Oracle中dump函数的用法
			一.函数标准格式: DUMP(expr[,return_fmt[,start_position][,length]]) 基本参数时4个,最少可以填的参数是0个.当完全没有参数时,直接返回null.另外 ... 
- 【C语言】练习2-9
			题目来源:<The C programming language>中的习题P38 练习2-9: 在求对二的补码时,表达式x &= (x-1)可以删除x中最右边值为1的一个二进 ... 
- 【C语言】练习1-22
			题目来源:<The C programming language>中的习题 练习1-22:编写一个程序,把较长的输入行‘折’成短一些的两行或者多行,折行的位置在输入行的第n列之前的最后 ... 
