zoukankan      html  css  js  c++  java
  • DIOCP开源项目-定义自己要发送的数据结构(MyObject)

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

  • 相关阅读:
    Select2插件ajax方式加载数据并刷新页面数据回显
    转 proguard 混淆工具的用法 (适用于初学者参考)
    转 【Android】- Android与html5交互操作
    转 android开发笔记之handler+Runnable的一个巧妙应用
    转 Android 多线程:手把手教你使用AsyncTask
    转 android design library提供的TabLayout的用法
    转 Android Lifecycle、ViewModel和LiveData
    转 onSaveInstanceState()和onRestoreInstanceState()使用详解
    转 MessageDigest来实现数据加密
    转 GSON
  • 原文地址:https://www.cnblogs.com/DKSoft/p/3369015.html
Copyright © 2011-2022 走看看