• QQ
  • nahooten@sina.com
  • 常州市九洲新世界花苑15-2

技术天地

DIOCP使用说明

原创内容,转载请注明原文网址:http://homeqin.cn/a/wenzhangboke/jishutiandi/2018/1015/83.html

今天我们常州手机APP软件开发与培训工作室来给大家介绍一个网络开发利器。
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;
 

上篇:上一篇:正则表达式基本语法
下篇:下一篇:socket C#与Delphi互传结构体