程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> DIOCP開源項目-定義自己要發送的數據結構(MyObject)

DIOCP開源項目-定義自己要發送的數據結構(MyObject)

編輯:Delphi

印象中網絡程序都是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/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved