• DelphiIOCP学习笔记<八>=======服务端的数据发送和JSonStreamObject的编码器


    现在IOCP的功能还剩下服务端数据的返回。

    还是采用netty的方式。netty返回数据的调用是这样的contenxt.write(<TObject> obj);这样将obj对象发送给客户端。

    1.将回传的对象进行编码成buffer

    2.通过socket进行传送.

    >>>>>>>>>>>>>>

    下面我贴出回传数据的过程.

    procedure TClientContext.writeObject(const pvDataObject:TObject);
    var
      lvOutBuffer:TBufferLink;
    begin
      lvOutBuffer := TBufferLink.Create;
      try
        TContextFactory.instance.FEncoder.Encode(pvDataObject, lvOutBuffer);
        TIOCPTools.SendBuffer(self.FSocket, lvOutBuffer);
      finally
        lvOutBuffer.Free;
      end;
    end;

    在数据处理的时候,我尝试调用这段代码

    procedure TClientContext.dataReceived(const pvDataObject:TObject);
    var
      lvJsonStream:TJSonStreamObject;
      lvFile:String;
    begin
      lvJsonStream := TJSonStreamObject(pvDataObject);
    
      //客户端发送文件
      if lvJsonStream.JSon.I['cmdIndex'] = 102 then
      begin
        lvFile := ExtractFilePath(ParamStr(0)) + 'tempFile\';
        ForceDirectories(lvFile);
        lvFile := lvFile + lvJsonStream.JSon.S['file'];
        TMemoryStream(lvJsonStream.Stream).Position := 0;
        TMemoryStream(lvJsonStream.Stream).SaveToFile(lvFile);
      end else
      begin
        //返回数据
        writeObject(lvJsonStream);
      end;
    
    
      TLogClientWrapper.logINfo(lvJsonStream.JSon.AsJSon(True));
    
    end;

    在IOCP的工作线程中如果数据发送完成,回收内存块。

    end else if PerIoData.IO_TYPE = IO_TYPE_Send then
              begin    //发送完成数据<WSASend>完成
                
                //回收数据块
                TIODataMemPool.instance.giveBackIOData(PerIoData);
              end;

    TIOCPTools.SendBuffer代码

    unit IOCPTools;
    
    interface
    
    uses
      uBuffer, JwaWinsock2, uMemPool, Windows;
    
    const
      IO_TYPE_Accept = 1;
      IO_TYPE_Recv = 2;
      IO_TYPE_Send = 3;   //发送数据
    
    type
      TIOCPTools=class(TObject)
      public
        class procedure SendBuffer(pvSocket: TSocket; const ouBuf: TBufferLink);
      end;
    
    implementation
    
    class procedure TIOCPTools.SendBuffer(pvSocket: TSocket; const ouBuf:
        TBufferLink);
    var
      lvIOData:LPPER_IO_OPERATION_DATA;
      lvRet:Cardinal;
    begin
      while ouBuf.validCount > 0 do
      begin
        lvIOData := TIODataMemPool.instance.borrowIOData;
        lvIOData.IO_TYPE := IO_TYPE_Send;
        //这里我改变了内存块的大小,每次发送的长度不能超过设定的内存块大小。但是数据不够的情况下
        //Databuf.len是指定了要发送内存块的大小。在回收内存块的时候,需要还原大小。
        lvIOData.DataBuf.len := ouBuf.readBuffer(lvIOData.DataBuf.buf, lvIOData.DataBuf.len);
    
        if (WSASend(pvSocket,
           @lvIOData.DataBuf,
           1,
           lvIOData.WorkBytes,
           lvIOData.WorkFlag,
           @lvIOData^, nil) = SOCKET_ERROR) then
        begin
          lvRet := GetLastError();
          //重叠IO,出现ERROR_IO_PENDING是正常的,
          //表示数据尚未接收完成,如果有数据接收,GetQueuedCompletionStatus会有返回值
          if (lvRet <> ERROR_IO_PENDING) then
          begin
            closesocket(pvSocket);
            Break;
          end;
        end;
      end;
    
    
    end;
    
    end.

    //编码器代码,负责将发送的对象转换成流

    unit uJSonStreamEncoder;
    
    interface
    
    
    uses
      uIOCPDecoder, uBuffer, Classes, superobject, SysUtils;
    
    type
      TJSonStreamEncoder = class(TIOCPEncoder)
      public
        /// <summary>
        ///   编码要发送的对象
        /// </summary>
        /// <param name="pvDataObject"> 要进行编码的对象 </param>
        /// <param name="ouBuf"> 编码好的数据 </param>
        procedure Encode(pvDataObject:TObject; const ouBuf: TBufferLink); override;
      end;
    
    implementation
    
    uses
      uJSonStreamObject, Windows;
    
    procedure TJSonStreamEncoder.Encode(pvDataObject:TObject; const ouBuf:
        TBufferLink);
    var
      lvJSonStreamObject:TJSonStreamObject;
      lvJSonLength:Integer;
      lvStreamLength:Integer;
      sData:String;
      lvStream:TStream;
      lvTempBuf:PAnsiChar;
    begin
      if pvDataObject = nil then exit;
      lvJSonStreamObject := TJSonStreamObject(pvDataObject);
    
      sData := lvJSonStreamObject.JSon.AsJSon(True);
      lvJSonLength := Length(sData);
      lvStream := lvJSonStreamObject.Stream;
    
      ouBuf.AddBuffer(@lvJSonLength, SizeOf(lvJSonLength));
    
    
      if lvStream <> nil then
      begin
        lvStreamLength := lvStream.Size;
      end else
      begin
        lvStreamLength := 0;
      end;
    
      ouBuf.AddBuffer(@lvStreamLength, SizeOf(lvStreamLength));
    
    
      //json bytes
      ouBuf.AddBuffer(@sData[1], lvJSonLength);
      if lvStreamLength > 0 then
      begin
        //stream bytes
        GetMem(lvTempBuf, lvStreamLength);
        try
          lvStream.Position := 0;
          lvStream.ReadBuffer(lvTempBuf^, lvStreamLength);
          ouBuf.AddBuffer(lvTempBuf, lvStreamLength);
        finally
          FreeMem(lvTempBuf, lvStreamLength);
        end;
      end;
    end;
    
    
    
    end.

    >>>>>好了关键性的代码我都贴出来了。这次就不提供demo了。如果有需要的请留言

    下一次学习的主题是做一个压力测试的demo

  • 相关阅读:
    preprocess
    数组
    共用体
    动态内存管理函数
    C链表
    文件的定位与出错检查
    字符串读写函数
    C文件操作
    位运算
    爱好-超级IP:超级IP
  • 原文地址:https://www.cnblogs.com/DKSoft/p/3039659.html
Copyright © 2020-2023  润新知