印象中网络程序都是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)的更多相关文章

  1. DIOCP开源项目-高效稳定的服务端解决方案(DIOCP + 无锁队列 + ZeroMQ + QWorkers) 出炉了

    [概述] 自从上次发布了[DIOCP开源项目-利用队列+0MQ+多进程逻辑处理,搭建稳定,高效,分布式的服务端]文章后,得到了很多朋友的支持和肯定.这加大了我的开发动力,经过几个晚上的熬夜,终于在昨天 ...

  2. DIOCP开源项目-DIOCP3的重生和稳定版本发布

    DIOCP3的重生 从开始写DIOCP到现在已经有一年多的时间了,最近两个月以来一直有个想法做个 30 * 24 稳定的企业服务端架构,让程序员专注于逻辑实现就好.虽然DIOCP到现在通讯层已经很稳定 ...

  3. DIOCP开源项目-DIOCP3直接发送对象,帮你处理粘包问题

    该DEMO演示,如何在客户端与服务端之间直接传递TStream对象,让你专注于处理数据逻辑,可以忽略处理网络传输间粘包的问题. 上面由服务端向所有的客户端推送一个消息TMemoryStream对象(该 ...

  4. 【DIOCP开源项目】实际应用案例

    案例1 DIOCP是Delphi下进行IOCP服务端通讯开发的一个非常好的开源框架,稳定.高效并且使用起来十分简单. 自己两个多月之前因为需要使用Delphi开发一个TCP服务端,当时也是到处爬文,希 ...

  5. DIOCP开源项目-利用队列+0MQ+多进程逻辑处理,搭建稳定,高效,分布式的服务端

    最近头脑里面一直在想怎么样让能让大家基于DIOCP上写出稳定的服务端程序.很多朋友问我,你DIOCP稳定吗,我可以用他来做三层服务器吗? 当时我是这样回答的,我只能保证DIOCP底层通信的稳定. 说实 ...

  6. DIOCP开源项目-Delphi高性能无锁队列(lock-free)

    最近想在DIOCP中加入任务调度线程,DIOCP的工作线程作为生产者(producer)将接受到的数据对象,投递到任务调度线程中,然后统一进行分配.然而这一切都需要一个队列, 这几天都在关注无锁队列. ...

  7. DIOCP开源项目-数据库连接池的使用<多帐套数据库>

    很久没有写DIOCP的Demo了,主要公司的事情太繁琐,工作之余都不想动了,之前承若的群里面朋友们的DEMO,昨天晚上恶补了一下,把对数据库连接池的操作加入到了Demo中,大家可以通过SVN下载到最新 ...

  8. DIOCP开源项目-DIOCP3的LoadRunner11测试报告

    昨天有个多年的群友(B3.Locet)用LoadRunner11对DIOCP3做压力测试,说测试的时候出现了大量的10053,10054的报告.昨天晚上下载了个LoadRunner11, 今天捣鼓了下 ...

  9. DIOCP开源项目-DIOCP3 大文件的传输DEMO<断点续传>

    首先该DEMO在StreamCoder上面做的改动,期间导致StreamCoderDEMO经常出现问题,导致大家运行的时候,频频出现问题,表示道歉. 以下是测试的结果,从服务器下载传输了一个3G左右的 ...

随机推荐

  1. ios中uitableview上拉刷新和下拉刷新(1)

    在一个项目开发过程中为了更好的体验经常会用到下拉刷新更新数据,当然也伴随一些上拉加载更多数据的情况:当前比较火的EGOTableViewPullRefresh只实现了下拉功能,而没有上拉的功能.这里介 ...

  2. Linux-LVS为何不能完全替代DNS轮询

    转自:链接 上一篇文章“一分钟了解负载均衡的一切”引起了不少同学的关注,评论中大家争论的比较多的一个技术点是接入层负载均衡技术,部分同学持这样的观点: 1)nginx前端加入lvs和keepalive ...

  3. [转载]TortoiseGit安装与使用

    原文地址:TortoiseGit安装与使用作者:了凡春秋 之前一直用SVN做项目开发,确实感觉这些版本控制工具非常实用,尤其是在一个团队开发项目的时候.最近偶然看到一个新的版本管理工具Git,它本来是 ...

  4. JavaScript概念梳理

    一.JavaScript 的奇葩命名史 1995 年,网景浏览器(Netscape Navigator)首次发布了一种运行于浏览器端的脚本语言,网景给这个新语言命名为 LiveScript.一年后,为 ...

  5. nodejs中的exports和module.exports

    module是随文件而生的全局变量,它有exports属性,这个属性默认是一个空的字典. node的设计者画蛇添足有定义了一个exports全局变量,export指向module.exports所指向 ...

  6. 编程实践笔记{Java 线程 并发处理 Webservice}(转)

    http://www.cnblogs.com/mingzi/archive/2009/03/09/1406694.html 1, 保证线程安全的三种方法: a, 不要跨线程访问共享变量 b, 使共享变 ...

  7. 利用 WireShark 深入调试网络请求

    来源:伯乐在线 - bestswifter 如有好文章投稿,请点击 → 这里了解详情 背景 最近发现我们产品在打开广告链接(Webview)时有一定概率会非常慢,白屏时间超过 10s,追查广告的过程中 ...

  8. 工程部署从tomcat6.0迁移到jboss6.0错误总结

    2014-11-08 内容存档在evernote,笔记名"工程部署从tomcat6.0迁移到jboss6.0错误总结"

  9. Python学习笔记(十)—— 高级特性

    一.切片 1.定义: 经常取指定索引范围的操作,用循环十分繁琐,因此,Python提供了切片(Slice)操作符. 2.语法: A[1:3] 取出1到3,都是正数的情况下,缺填的为0(第一个),end ...

  10. hadoop资料

    1."Hadoop.Operations.pdf.zip" http://vdisk.weibo.com/s/vDOQs6xMAQH6 2."Hadoop权威指南(中文版 ...