DIOCP开源项目-定义自己要发送的数据结构(MyObject)

简介: 印象中网络程序都是sendBuffer和recvBuffer来发送数据和接收数据,本次Demo演示如何定义定义一个自己的对象,然后我们按照OO的思想直接进行对象的发送和接收,先上个流程图。   下面是客户端发送和接收的测试代码。

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

image

 

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

image

 

下面我们来看看详细的设计。

第一步(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/

目录
相关文章
|
4月前
|
存储 算法 C语言
数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
本文详细介绍了单链表的理论知识,涵盖单链表的定义、优点与缺点,并通过示例代码讲解了单链表的初始化、插入、删除、查找等核心操作。文中还具体分析了按位序插入、指定节点前后插入、按位序删除及按值查找等算法实现,并提供了尾插法和头插法建立单链表的方法,帮助读者深入理解单链表的基本原理与应用技巧。
743 6
|
4月前
MITK中的数据结构和常量定义
本文介绍了MITK中的数据结构、反射机制、常量定义、DataNode类和类宏定义,包括多图映射、反射接口、事件宏和属性列表等高级特性。
|
6月前
|
存储 数据可视化 数据处理
`geopandas`是一个开源项目,它为Python提供了地理空间数据处理的能力。它基于`pandas`库,并扩展了其对地理空间数据(如点、线、多边形等)的支持。`GeoDataFrame`是`geopandas`中的核心数据结构,它类似于`pandas`的`DataFrame`,但包含了一个额外的地理列(通常是`geometry`列),用于存储地理空间数据。
`geopandas`是一个开源项目,它为Python提供了地理空间数据处理的能力。它基于`pandas`库,并扩展了其对地理空间数据(如点、线、多边形等)的支持。`GeoDataFrame`是`geopandas`中的核心数据结构,它类似于`pandas`的`DataFrame`,但包含了一个额外的地理列(通常是`geometry`列),用于存储地理空间数据。
|
7月前
|
存储
数据结构学习记录——堆的插入(堆的结构类型定义、最大堆的创建、堆的插入:堆的插入的三种情况、哨兵元素)
数据结构学习记录——堆的插入(堆的结构类型定义、最大堆的创建、堆的插入:堆的插入的三种情况、哨兵元素)
50 2
|
7月前
|
机器学习/深度学习 存储
数据结构学习记录——哈夫曼树(什么是哈夫曼树、哈夫曼树的定义、哈夫曼树的构造、哈夫曼树的特点、哈夫曼编码)
数据结构学习记录——哈夫曼树(什么是哈夫曼树、哈夫曼树的定义、哈夫曼树的构造、哈夫曼树的特点、哈夫曼编码)
241 1
|
7月前
|
存储 机器学习/深度学习
数据结构学习记录——什么是图(抽象数据类型定义、常见术语、邻接矩阵表示法、邻接表表示法)
数据结构学习记录——什么是图(抽象数据类型定义、常见术语、邻接矩阵表示法、邻接表表示法)
85 0
|
7月前
|
算法
数据结构和算法学习记录——平衡二叉树(基本介绍、平衡因子、平衡二叉树的定义、平衡二叉树的高度)
数据结构和算法学习记录——平衡二叉树(基本介绍、平衡因子、平衡二叉树的定义、平衡二叉树的高度)
167 0
|
7月前
|
算法
数据结构和算法学习记录——初识二叉树(定义、五种基本形态、几种特殊的二叉树、二叉树的重要性质、初识基本操作函数)
数据结构和算法学习记录——初识二叉树(定义、五种基本形态、几种特殊的二叉树、二叉树的重要性质、初识基本操作函数)
77 0
|
7月前
|
机器学习/深度学习 存储 算法
数据结构和算法学习记录——树(基本介绍、树的定义、树的特点、树的一些基本术语、树的表示、儿子-兄弟表示法)
数据结构和算法学习记录——树(基本介绍、树的定义、树的特点、树的一些基本术语、树的表示、儿子-兄弟表示法)
123 0
|
7月前
|
存储 算法
数据结构和算法学习记录——特殊线性表之队列-队列的概念、队列结构体类型定义 、基本接口函数、初始化函数、销毁队列函数、入队列函数、判断队列是否为空、出队列函数、读取队头队尾的数据 、计算队列数据个数
数据结构和算法学习记录——特殊线性表之队列-队列的概念、队列结构体类型定义 、基本接口函数、初始化函数、销毁队列函数、入队列函数、判断队列是否为空、出队列函数、读取队头队尾的数据 、计算队列数据个数
51 0