TCP中间件_Delphi_client

1、界面

  1.1、formMain.pas

    1.1.1、

object frmMain: TfrmMain
  Left = 191
  Top = 103
  Width = 542
  Height = 466
  Caption = 'frmMain'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesktopCenter
  OnCreate = FormCreate
  DesignSize = (
    534
    439)
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 16
    Top = 8
    Width = 41
    Height = 13
    AutoSize = False
    Caption = 'IP : '
  end
  object Label2: TLabel
    Left = 176
    Top = 8
    Width = 49
    Height = 13
    AutoSize = False
    Caption = 'Port : '
  end
  object btnSetIpPort: TButton
    Left = 16
    Top = 32
    Width = 75
    Height = 25
    Caption = '设置信息'
    TabOrder = 0
    OnClick = btnSetIpPortClick
  end
  object Memo1: TMemo
    Left = 16
    Top = 128
    Width = 497
    Height = 299
    Anchors = [akLeft, akTop, akRight, akBottom]
    Lines.Strings = (
      'Memo1')
    ScrollBars = ssBoth
    TabOrder = 1
  end
  object btnSend: TButton
    Left = 96
    Top = 32
    Width = 75
    Height = 25
    Caption = '发送消息'
    TabOrder = 2
    OnClick = btnSendClick
  end
  object btnStatus: TButton
    Left = 176
    Top = 32
    Width = 75
    Height = 25
    Caption = 'skt状态'
    TabOrder = 3
    OnClick = btnStatusClick
  end
  object btnClearMemo: TButton
    Left = 256
    Top = 32
    Width = 89
    Height = 25
    Caption = '清空消息框'
    TabOrder = 4
    OnClick = btnClearMemoClick
  end
  object btnAllClients: TButton
    Left = 208
    Top = 64
    Width = 137
    Height = 25
    Caption = '所有客户端信息'
    TabOrder = 5
    OnClick = btnAllClientsClick
  end
  object btnSocket: TButton
    Left = 16
    Top = 96
    Width = 153
    Height = 25
    Caption = '客户端网络信息'
    TabOrder = 6
    OnClick = btnSocketClick
  end
  object edtAddress: TEdit
    Left = 16
    Top = 64
    Width = 185
    Height = 21
    TabOrder = 7
    Text = 'edtAddress'
  end
  object btnBlock: TButton
    Left = 192
    Top = 96
    Width = 153
    Height = 25
    Caption = '客户端内存块信息'
    TabOrder = 8
    OnClick = btnBlockClick
  end
  object edtIP: TEdit
    Left = 40
    Top = 4
    Width = 121
    Height = 21
    TabOrder = 9
    Text = '192.168.1.233'
  end
  object edtPort: TEdit
    Left = 216
    Top = 4
    Width = 89
    Height = 21
    TabOrder = 10
    Text = '9888'
  end
  object btnFrmSQL: TButton
    Left = 432
    Top = 32
    Width = 75
    Height = 25
    Caption = 'btnFrmSQL'
    TabOrder = 11
    OnClick = btnFrmSQLClick
  end
end

  1.2、formSQL.pas

    1.2.1、

object frmSQL: TfrmSQL
  Left = 362
  Top = 105
  Width = 457
  Height = 480
  Caption = 'frmSQL'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesktopCenter
  DesignSize = (
    449
    453)
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 16
    Top = 14
    Width = 145
    Height = 13
    AutoSize = False
    Caption = 'SQL语句 : '
  end
  object lvSQL: TListView
    Left = 16
    Top = 96
    Width = 416
    Height = 345
    Anchors = [akLeft, akTop, akRight, akBottom]
    Columns = <>
    GridLines = True
    OwnerData = True
    ReadOnly = True
    RowSelect = True
    TabOrder = 0
    ViewStyle = vsReport
    OnData = lvSQLData
  end
  object Button1: TButton
    Left = 359
    Top = 8
    Width = 75
    Height = 25
    Anchors = [akTop, akRight]
    Caption = 'Button1'
    TabOrder = 1
    OnClick = Button1Click
  end
  object edtSQL: TEdit
    Left = 80
    Top = 10
    Width = 264
    Height = 21
    Anchors = [akLeft, akTop, akRight]
    TabOrder = 2
    Text = 'select * from file_tbl where rownum<5'
  end
  object btnBLOB: TButton
    Left = 16
    Top = 48
    Width = 75
    Height = 25
    Caption = 'btnBLOB'
    TabOrder = 3
    OnClick = btnBLOBClick
  end
end

 

2、代码:

  2.1、formMain.pas

unit formMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,
  classDrTcp, Winsock2;

type
  TfrmMain = class(TForm)
    Memo1: TMemo;
    btnSetIpPort: TButton;
    btnSend: TButton;
    btnStatus: TButton;
    btnClearMemo: TButton;
    btnAllClients: TButton;
    btnSocket: TButton;
    edtAddress: TEdit;
    btnBlock: TButton;
    Label1: TLabel;
    edtIP: TEdit;
    Label2: TLabel;
    edtPort: TEdit;
    btnFrmSQL: TButton;
    procedure btnSetIpPortClick(Sender: TObject);
    procedure btnSendClick(Sender: TObject);
    procedure btnClearMemoClick(Sender: TObject);
    procedure btnStatusClick(Sender: TObject);
    procedure btnAllClientsClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnSocketClick(Sender: TObject);
    procedure btnBlockClick(Sender: TObject);
    procedure btnFrmSQLClick(Sender: TObject);
  public
  public
    procedure UnpackClients(_pc :PChar; _iLen :integer);
    procedure UnpackSocket(_pc :PChar; _iLen :integer);
    procedure UnpackBlock(_pc :PChar; _iLen :integer);
  end;

var
  frmMain: TfrmMain;

implementation

uses
  formSQL;

{$R *.dfm}

procedure TfrmMain.FormCreate(Sender: TObject);
begin
//  edtIP.Text := '192.168.1.15';
end;

procedure TfrmMain.btnSetIpPortClick(Sender: TObject);
var iPort :Integer;
begin
  //TdrTcpClient.SetIpPort('127.0.0.1', 9888);
  //TdrTcpClient.SetIpPort('192.168.1.15', 9888);
  //TdrTcpClient.SetIpPort('192.168.1.233', 9888);
  iPort := StrToIntDef(trim(edtPort.Text), 9888);
  TdrTcpClient.SetIpPort(Trim(edtIP.Text), iPort);
end;

procedure TfrmMain.btnSendClick(Sender: TObject);
begin
  TdrTcpClient.SendSQL('select * from ccc');
end;

procedure TfrmMain.btnStatusClick(Sender: TObject);
begin
  Memo1.Lines.Add(IntToStr(TdrTcpClient.Status));
end;

procedure TfrmMain.btnClearMemoClick(Sender: TObject);
begin
  Memo1.Lines.Clear;
end;

procedure TfrmMain.UnpackClients(_pc: PChar; _iLen: integer);
var iLen, iIdx :integer;
    str :string;
    ll :LONGLONG;
begin
  Memo1.Lines.Add('所有客户端的 IP/Port 信息 :');
  CopyMemory(@ll, @_pc[0], SizeOf(LONGLONG));
  Memo1.Lines.Add('   当前时间(ms) : '+inttostr(ll));
  iIdx := SizeOf(LONGLONG);
  Dec(_iLen, 8);

  while _iLen>0 do
  begin
    CopyMemory(@ll, @_pc[iIdx], 8);
    Memo1.Lines.Add('           时间(ms) : '+inttostr(ll));
    CopyMemory(@iLen, @_pc[iIdx+8], 4);
    SetLength(str, iLen);
    CopyMemory(@str[1], @_pc[iIdx+4+8], iLen);
    Inc(iIdx, 4+8+iLen);

    Memo1.Lines.Add('         '+str);
    Memo1.Lines.Add('');
    Dec(_iLen, 4+8+iLen);
  end;
  SetLength(str, 0);
end;

procedure TfrmMain.UnpackSocket(_pc: PChar; _iLen: integer);
var strIpRes, strMacRes, strReq :string;
    iLenIpRes, iLenMacRes, iLenReq :Integer;
    iLenMsg :Integer;
    strMsg :string;
begin
  CopyMemory(@iLenMsg, @_pc[0], 4);

  if (iLenMsg < 0) then
  begin
    iLenMsg := -iLenMsg;
    SetLength(strMsg, iLenMsg);
    CopyMemory(@strMsg[1], @_pc[4], iLenMsg);

    Memo1.Lines.Add('请求主机信息(未找到):');
    Memo1.Lines.Add('     '+strMsg);
  end
  else
  begin
    CopyMemory(@iLenIpRes, @_pc[4], 4);
    SetLength(strIpRes, iLenIpRes - 4);
    CopyMemory(@strIpRes[1], @_pc[8], iLenIpRes - 4);
  
    CopyMemory(@iLenMacRes, @_pc[4+iLenIpRes], 4);
    SetLength(strMacRes, iLenMacRes - 4);
    CopyMemory(@strMacRes[1], @_pc[4+iLenIpRes+4], iLenMacRes - 4);

    CopyMemory(@iLenReq, @_pc[4+iLenIpRes+iLenMacRes], 4);
    SetLength(strReq, iLenReq - 4);
    CopyMemory(@strReq[1], @_pc[4+iLenIpRes+iLenMacRes+4], iLenReq - 4);

    Memo1.Lines.Add('请求主机"'+strReq+'"的信息:');
    Memo1.Lines.Add('     IP : '+strIpRes);
    Memo1.Lines.Add('     Mac : '+strMacRes);
  end;
end;

procedure TfrmMain.UnpackBlock(_pc: PChar; _iLen: integer);
var iTotalLen :Integer;
    iValidBeginOffset :Integer;
    iValidLen :Integer;
    dwType :DWORD;
    dwLastTick :DWORD;
    // ***
    iLenBlockMsg, iOffset, iIdx :Integer;
    iLenReq :Integer;
    strReq :string;
    dwTick :DWORD;
begin
  CopyMemory(@iLenBlockMsg, @_pc[0], 4);
  CopyMemory(@dwTick, @_pc[4], 4);

  CopyMemory(@iLenReq, @_pc[iLenBlockMsg], 4);
  SetLength(strReq, iLenReq-4);
  CopyMemory(@strReq[1], @_pc[iLenBlockMsg+4], iLenReq-4);
  Memo1.Lines.Add('请求主机"'+strReq+'"的信息:'+inttostr(dwTick));
  Memo1.Lines.Add('     '+inttostr(dwTick)+' : '+inttostr(Length(strReq)));

  iIdx := 0;
  iOffset := 8;
  Dec(iLenBlockMsg, iOffset);
  while iLenBlockMsg > 0 do
  begin
    Inc(iIdx);
    CopyMemory(@iTotalLen,        @_pc[iOffset+0], 4);
    CopyMemory(@iValidBeginOffset,@_pc[iOffset+4], 4);
    CopyMemory(@iValidLen,        @_pc[iOffset+8], 4);
    CopyMemory(@dwType,           @_pc[iOffset+12], 4);
    CopyMemory(@dwLastTick,       @_pc[iOffset+16], 4);

    Memo1.Lines.Add('   内存块('+inttostr(iIdx)+') :');
    Memo1.Lines.Add('       总长 : '              +inttostr(iTotalLen));
    Memo1.Lines.Add('       (有效数据)开始偏移 : '+inttostr(iValidBeginOffset));
    Memo1.Lines.Add('       (有效数据)长度 : '    +inttostr(iValidLen));
    Memo1.Lines.Add('       用途 : 0x'            +inttohex(dwType, 8));
    Memo1.Lines.Add('       最后申请时间 : '      +inttostr(dwLastTick));
    Inc(iOffset, 20);
    Dec(iLenBlockMsg, 20);
  end;
end;

procedure TfrmMain.btnAllClientsClick(Sender: TObject);
var pc :array[0..255] of Char;
    iPktLen, iPktIdx, iPktType :Integer;
begin
  g_callbackWnd.FfuncClients := UnpackClients;
  
  iPktLen := TCP_PACKET_HEADER_LEN;
  iPktIdx := 0;
  iPktType:= OP_TYPE_MANAGE_CLIENTS;
  CopyMemory(@pc[0], @iPktLen, 4);
  CopyMemory(@pc[4], @iPktIdx, 4);
  CopyMemory(@pc[8], @iPktType, 4);
  TdrTcpClient.SendBytes(@pc[0], iPktLen);
end;

procedure TfrmMain.btnSocketClick(Sender: TObject);
var pc :array[0..255] of Char;
    iPktLen, iPktIdx, iPktType :Integer;
    strRemoteAddress :string;
    iLen :Integer;
begin
  strRemoteAddress := Trim(edtAddress.Text);
  g_callbackWnd.FfuncSocket := UnpackSocket;

  iPktLen := TCP_PACKET_HEADER_LEN + (4 + Length(strRemoteAddress));
  iPktIdx := 0;
  iPktType:= OP_TYPE_MANAGE_SOCKET_REQ;
  CopyMemory(@pc[0], @iPktLen, 4);
  CopyMemory(@pc[4], @iPktIdx, 4);
  CopyMemory(@pc[8], @iPktType, 4);
  // ***
  iLen := 4 + Length(strRemoteAddress);
  CopyMemory(@pc[12], @iLen, 4);
  CopyMemory(@pc[16], @strRemoteAddress[1], Length(strRemoteAddress));
  TdrTcpClient.SendBytes(@pc[0], iPktLen);
end;

procedure TfrmMain.btnBlockClick(Sender: TObject);
var pc :array[0..255] of Char;
    iPktLen, iPktIdx, iPktType :Integer;
    strRemoteAddress :string;
    iLen :Integer;
begin
  strRemoteAddress := Trim(edtAddress.Text);
  g_callbackWnd.FfuncBlock := UnpackBlock;

  iPktLen := TCP_PACKET_HEADER_LEN + (4+Length(strRemoteAddress));
  iPktIdx := 0;
  iPktType:= OP_TYPE_MANAGE_BLOCK_REQ;
  CopyMemory(@pc[0], @iPktLen, 4);
  CopyMemory(@pc[4], @iPktIdx, 4);
  CopyMemory(@pc[8], @iPktType, 4);
  // ***
  iLen := 4 + Length(strRemoteAddress);
  CopyMemory(@pc[12], @iLen, 4);
  CopyMemory(@pc[16], @strRemoteAddress[1], Length(strRemoteAddress));
  TdrTcpClient.SendBytes(@pc[0], iPktLen);
end;

procedure TfrmMain.btnFrmSQLClick(Sender: TObject);
begin
  frmSQL.Show;
end;

end.

  2.2、formSQL.pas

unit formSQL;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, StdCtrls,
  classDrTcp;

type
  TfrmSQL = class(TForm)
    lvSQL: TListView;
    Button1: TButton;
    edtSQL: TEdit;
    Label1: TLabel;
    btnBLOB: TButton;
    procedure Button1Click(Sender: TObject);
    procedure lvSQLData(Sender: TObject; Item: TListItem);
    procedure btnBLOBClick(Sender: TObject);
  private
    Fbuffer :TdrBuffer;
    procedure UnpackSQL(_buffer :TdrBuffer);
  public
    FdataSet :TdrDataSet;
    FiColumnCnt :Integer;
  end;

var
  frmSQL: TfrmSQL;

implementation

uses
  formMain;

{$R *.dfm}

procedure TfrmSQL.Button1Click(Sender: TObject);
var pc :array[0..255] of Char;
    iPktLen, iPktIdx, iPktType :Integer;
    strSql :string;
    iLenSQL :Integer;
begin
  if (Fbuffer <> nil) then
  begin
    g_bufferPool.ReleaseBlock(Fbuffer);
    Fbuffer := nil;
  end;
  
  strSql := Trim(edtSQL.Text);
  iLenSQL := Length(strSql);
  if iLenSQL = 0 then
  begin
    frmMain.Memo1.Lines.Add('no sql');
    Exit;
  end;
  g_callbackWnd.FfuncSQL := UnpackSQL;

  Inc(iLenSQL, 4);
  iPktLen := TCP_PACKET_HEADER_LEN + iLenSQL;
  iPktIdx := 0;
  iPktType:= OP_TYPE_MANAGE_SQL;
  CopyMemory(@pc[0], @iPktLen, 4);
  CopyMemory(@pc[4], @iPktIdx, 4);
  CopyMemory(@pc[8], @iPktType, 4);
  // ***
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN], @iLenSQL, 4);
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN + 4], PChar(strSql), iLenSQL - 4);
  TdrTcpClient.SendBytes(@pc[0], iPktLen);
end;

procedure TfrmSQL.UnpackSQL(_buffer :TdrBuffer);
var columnNew :TListColumn;
    i :Integer;
begin
  if (Fbuffer <> nil) then
  begin
    g_bufferPool.ReleaseBlock(Fbuffer);
    Fbuffer := nil;
  end;

  Fbuffer := _buffer;
  if not Assigned(FdataSet) then
    FdataSet := TdrDataSet.Create;
  FdataSet.Unpack(@_buffer.Fpc[TCP_PACKET_HEADER_LEN], _buffer.FiValidLen - TCP_PACKET_HEADER_LEN);

  if (FdataSet.ColumnCount <= 0) then
    Exit;

  lvSQL.Columns.Clear;
  FiColumnCnt := FdataSet.ColumnCount;
  for i:=0 to FiColumnCnt-1 do
  begin
    columnNew := lvSQL.Columns.Add;
    columnNew.AutoSize := True;
    columnNew.Caption := FdataSet.GetFieldName(i);
  end;

  lvSQL.Items.Count := FdataSet.RowCount;
  //lvSQL.Refresh;
  lvSQL.Repaint;
end;

procedure TfrmSQL.lvSQLData(Sender: TObject; Item: TListItem);
var iColIdx :Integer;
    rec :TdrRecord;
    wstr :WideString;
begin
  if (FdataSet = nil) then Exit;
  if (FdataSet.RowCount <= 0) then Exit;

  for iColIdx:=0 to FiColumnCnt-1 do
  begin
    rec := FdataSet.Rec[Item.Index, iColIdx];
    //wstr := rec.asString;
    //frmMain.Memo1.Lines.Add(IntToStr(Item.Index)+','+IntToStr(iColIdx)+' : '+IntToStr(Integer(rec.FpcRecord)));
    if (iColIdx = 0) then
      Item.Caption := rec.asString
    else
      Item.SubItems.Add(rec.asString);
  end;
end;

procedure TfrmSQL.btnBLOBClick(Sender: TObject);
var item :TListItem;
    rec :TdrRecord;
    pc :PChar;
    iLen :Integer;
begin
  item := lvSQL.Selected;
  frmMain.Memo1.Lines.Add('ListView select item index : '+IntToStr(item.Index));

  rec := FdataSet.Rec[item.Index, 3];
  rec.asByteArray(nil, iLen);
  frmMain.Memo1.Lines.Add(IntToStr(iLen));
end;

end.

  2.3、classDrTcp.pas

unit classDrTcp;

interface

uses
  Windows, Classes, Winsock, SysUtils, Messages, Math;// Math和StrUtils里面都有 IfThen(...)函数

const
  BUFFER_BLOCK = 1024 * 1024;
  TCP_PACKET_HEADER_LEN = 4 * 3;

  // 调试输出信息
  WM_TCP_RECV = WM_USER + $1000;
  WM_LOG_CONSOLE = WM_USER + $1001;

  WM_TCP_PUSH = WM_USER + 1000;
  WM_TCP_CLIENTS = WM_USER + 1001;
  WM_CLIENT_SOCKET_MSG_REQ = WM_USER + 1002;
  WM_CLIENT_SOCKET_MSG_RES = WM_USER + 1003;         
  WM_CLIENT_BLOCK_MSG_REQ  = WM_USER + 1004;
  WM_CLIENT_BLOCK_MSG_RES  = WM_USER + 1005;
  WM_MANAGE_SQL  = WM_USER + 1006;

  // TCP数据 操作类型:高16位:高一级类型; 低16位:低一级类型
  // (正值)正常的 C/S之间的业务逻辑数据
    OP_TYPE_SQL =       $00010000;
    OP_TYPE_PUSH =      $00020000;
  OP_TYPE_HEARTBEAT = $00030000;
  // (负值)C/S之间的 管理数据
    OP_TYPE_MANAGE =    $80000000;
  OP_TYPE_MANAGE_CLIENTS = $80000001; // c请求s,所有客户端的socket信息(简单)
  OP_TYPE_MANAGE_SOCKET_REQ =  $80000010; // c-->s,s-->c, 某个客户端的详细socket信息  (request) (搬运工)
  OP_TYPE_MANAGE_SOCKET_RES =  $80000020; //                                          (response)(搬运工)
  OP_TYPE_MANAGE_BLOCK_REQ =   $80000030; // c-->s,s-->c, 某个客户端的内存block信息   (request) (搬运工)
  OP_TYPE_MANAGE_BLOCK_RES =   $80000040; //                                          (response)(搬运工)
  OP_TYPE_MANAGE_SQL =     $80000002;    // 我的SQL语句操作
  OP_TYPE_RECV =   $90000000;

const
  DR_LONGNVARCHAR = -16;
  DR_NCHAR        = -15;
  DR_NVARCHAR     = -9;
  DR_ROWID        = -8;
  DR_BIT     = -7;
  DR_TINYINT = -6;
  DR_BIGINT  = -5;
  DR_LONGVARBINARY= -4;
  DR_VARBINARY    = -3;
  DR_BINARY       = -2;
  DR_LONGVARCHAR  = -1;
  DR_NULL = 0;
  DR_CHAR = 1;
  DR_NUMERIC  = 2;
  DR_DECIMAL  = 3;
  DR_INTEGER  = 4;
  DR_SMALLINT = 5;
  DR_FLOAT = 6;
  DR_REAL  = 7;
  DR_DOUBLE= 8;
  DR_VARCHAR = 12;
  DR_BOOLEAN = 16;
  DR_DATALINK= 70;
  DR_DATE = 91;
  DR_TIME = 92;
  DR_TIMESTAMP = 93;
  DR_OTHER = 1111;
  DR_JAVA_OBJECT = 2000;
  DR_DISTINCT = 2001;
  DR_STRUCT = 2002;
  DR_ARRAY = 2003;
  DR_BLOB = 2004;
  DR_CLOB = 2005;
  DR_REF = 2006;
  DR_SQLXML = 2009;
  DR_NCLOB = 2011;


//常量定义
Const
  MAX_HOSTNAME_LEN               = 128;
  MAX_DOMAIN_NAME_LEN            = 128;
  MAX_SCOPE_ID_LEN               = 256;
  MAX_ADAPTER_NAME_LENGTH        = 256;
  MAX_ADAPTER_DESCRIPTION_LENGTH = 128;
  MAX_ADAPTER_ADDRESS_LENGTH     = 8;
//定义相关结构
Type
  TIPAddressString = Array[0..4*4-1] of Char;

  PIPAddrString = ^TIPAddrString;
  TIPAddrString = Record
    Next      : PIPAddrString;
    IPAddress : TIPAddressString;
    IPMask    : TIPAddressString;
    Context   : Integer;
  end;

  PFixedInfo = ^TFixedInfo;
  TFixedInfo = Record { FIXED_INFO }
    HostName         : Array[0..MAX_HOSTNAME_LEN+3] of Char;
    DomainName       : Array[0..MAX_DOMAIN_NAME_LEN+3] of Char;
    CurrentDNSServer : PIPAddrString;
    DNSServerList    : TIPAddrString;
    NodeType         : Integer;
    ScopeId          : Array[0..MAX_SCOPE_ID_LEN+3] of Char;
    EnableRouting    : Integer;
    EnableProxy      : Integer;
    EnableDNS        : Integer;
  end;

  PIPAdapterInfo = ^TIPAdapterInfo;
  TIPAdapterInfo = Record { IP_ADAPTER_INFO }
    Next                : PIPAdapterInfo;
    ComboIndex          : Integer;
    AdapterName         : Array[0..MAX_ADAPTER_NAME_LENGTH+3] of Char;
    Description         : Array[0..MAX_ADAPTER_DESCRIPTION_LENGTH+3] of Char;
    AddressLength       : Integer;
    Address             : Array[1..MAX_ADAPTER_ADDRESS_LENGTH] of Byte;
    Index               : Integer;
    _Type               : Integer;
    DHCPEnabled         : Integer;
    CurrentIPAddress    : PIPAddrString;
    IPAddressList       : TIPAddrString;
    GatewayList         : TIPAddrString;
    DHCPServer          : TIPAddrString;
    HaveWINS            : Bool;
    PrimaryWINSServer   : TIPAddrString;
    SecondaryWINSServer : TIPAddrString;
    LeaseObtained       : Integer;
    LeaseExpires        : Integer;
  end;

  function GetAdaptersInfo(_ai : PIPAdapterInfo; var _dwBufLen : DWORD) : Integer;StdCall;external 'iphlpapi.dll';
  
type
  TdrBuffer = class
  public
    Fpc :PChar;
    FiTotalLen :Integer;

    FiValidBeginOffset :Integer;
    FiValidLen :Integer;

    FdwType :DWORD; // 用于什么的? TCP接收/SQL/PUSH/...?
    FdwLastTick :DWORD; // 最后一次被Aquire时候的GetTickCount
  end;

  TdrBufferPool = class//(TObject)
  public
    constructor create;
    destructor destroy;override;
  private
    Flist :TList;
    FlistAll :TList;//... 这个没弄,还有加锁没弄...
    FhEvent :THandle;//...
  private
    function NewBlock(_iLen :Integer = 0) :TdrBuffer;
    function DelBlock(_buffer :TdrBuffer) :Integer;
  public
    function AquireBlock(_dwType :DWORD; _iLen :Integer) :TdrBuffer;
    function ReleaseBlock(_buffer :TdrBuffer) :Integer;
  public
    property ListAll :TList read FlistAll;
  end;

// ***
  TdrRecvBuffer = class//(TObject)
  public
    constructor Create;
    destructor Destroy;override;
  private
    Flist :TList;
    FiTotalLen :Integer;
    FiPktIdx :Integer;   // TCP包 序号
    FhEvent :THandle;
    FiFirstPktLen :Integer;
  public
    function BufferAuqire(out _pc :PChar; out _iLen :integer) :integer;
    function BufferReleaseAll :integer;
    function BufferRecv(_iRecv :Integer; _iBlockIdx: integer) :Integer;
    function BufferHandleAll :Integer;// 处理所有已经接收到的 TCP包
  private
    function BufferHandle :Integer;// 处理单个 TCP包
  // ***
  private
    // 从接收到的缓冲区里面 得到TCP包头的信息
    function TcpPktHeader(out _iPktLen, _iPktIdx, _iPktType :Integer) :Integer; // 始终默认是 [0]的缓冲区块
    // 从TCP包头的信息中,判断 我们是否需要这个TCP包(0:不需要; >0:需要; <0:出错信息)
    function TcpPktIsNeed(_iPktIdx, _iPktType :integer):Integer;
    procedure TcpPktDispatch(_buffer :TdrBuffer);
  end;

// ***
  TdrTcpClient = class(TThread)
  public
    FiErrorNo :Integer;
    FiStatus :integer; // 线程的状态(0:初始状态; 1:运行中; -1:结束)
  public
    Fskt :TSocket;
  private
    function RecvTimeoutSet :Integer;
    function RecvTimeoutCancel :Integer;
    function Recv01() :Integer;
  protected
    procedure Execute; override;
  public
    function Send01(_pbyte :PByte; _iLen :integer) :Integer;
  public
    class procedure SetIpPort(_strDestIp :string; _iDestPort :integer);
    class function Conn :Integer;
    class procedure SendSQL(_str :string);
    class procedure SendBytes(_pc :PChar; _iLen :Integer);
    class function ConnectDest :TSocket;
    class function Status :Integer;
    class procedure SendHeartBeat;
  end;

// ***
  TdrDataSet = class;
  
  TdrRecord = class
  public
    constructor Create(_dataset :TdrDataSet; _iRowIdx, _iColumnIdx :Integer);
    procedure FenXi;
  private
    Fdataset :TdrDataSet;
    FiRowIdx, FiColumnIdx :Integer;
    FpcRecord :PChar; // 指向某个Record的指针
  public
    function asByteArray(_pByte :PByte; out _iLen :Integer) :Integer;
    function asInteger :Integer;
    function asSingle :Single;
    function asDouble :Double;
    function asString :string;
  private
    function GetFieldType :Integer;
    function GetFieldTypeName :string;
    function GetFieldName :string;
  public
    property FieldType :Integer read GetFieldType;
    property FieldTypeName :string read GetFieldTypeName;
    property FieldName :string read GetFieldName;
  end;

  TdrDataSet = class
  public
    function Unpack(_pc :PChar; _iLen :Integer) :integer;
  private
    FpcBuf :PChar;
    FiBufLen :Integer; // 缓冲区中 有效数据的长度
    FaryColumnType :array of Integer;
    FaryColumnName :array of string;
    FaryaryRecord :array of array of Integer; // 指向各个Record的指针
  public
    function GetFieldType(_iColumnIdx :integer) :Integer;
    function GetFieldTypeName(_iColumnIdx :integer) :string;
    function GetFieldName(_iColumnIdx :integer) :string;
  private
    Ffield :TdrRecord; // 这个属性有且只有一个
  public
    function GetRecord(_iRowIdx, _iColumnIdx :integer):TdrRecord;
    property Rec[_iRow, _iCol :Integer] :TdrRecord read GetRecord;
  public
    function GetRowCount :Integer;
    function GetColumnCount :Integer;
    property RowCount: integer read GetRowCount;
    property ColumnCount: integer read GetColumnCount;
  end;

// ***
  TfuncPush = procedure(_pc :PChar; _iLen :integer) of Object;
  TfuncCallback = procedure(_pc :PChar; _iLen :integer) of Object;
  TfuncCallback_Buf = procedure(_buffer :TdrBuffer) of Object;

  TcallbackWnd = class
  public
    constructor Create;
  public
    FhWnd :HWND;
    FdwErrNo :DWORD;
  private
    procedure CreateWindow;
  public     // 打包 信息
    function PackSocket(_pc :PChar; _iLen :integer) :integer;
    procedure PackBlock(_pc :PChar; _iLen :integer);
  public
    FfuncPush :TfuncPush;
    FfuncClients :TfuncCallback;   // 拆包 信息
    FfuncSocket :TfuncCallback;
    FfuncBlock :TfuncCallback;
    FfuncSQL :TfuncCallback_Buf;
  end;
  function ProcWindow(_hWnd :HWND; _uMsg :UINT; _wParam :WPARAM; _lParam :LPARAM):longint;stdcall;

  function ErrorNo2Str(_dwErrNo :DWORD):string;
  procedure BufferBlockUsage(_iOpType :Integer; var _str :string);
  
var
  g_callbackWnd :TcallbackWnd = nil;  // 需要 自己新建
  g_bufferPool :TdrBufferPool = nil;  // 需要 自己新建

implementation

uses
  formMain;

var

  g_drRecvBuffer :TdrRecvBuffer = nil;// 需要 自己新建
  g_tcpClient :TdrTcpClient = nil;    // 需要 自己新建(动态创建,不是在initialization下面创建)
  g_bufferSql :TdrBuffer = nil;
  //g_bufferPush :TdrBuffer = nil;
// ***
  g_strDestIp :string;
  g_iDestPort :integer;
  //g_tcpClient :TdrTcpClient = nil;    // 需要 自己新建(动态创建,不是在initialization下面创建)


// ***

procedure LogConsole(_str :string);
var pc :PChar;
    iLen :Integer;
begin
  iLen := Length(_str);
  GetMem(pc, iLen);
  PostMessage(g_callbackWnd.FhWnd, WM_LOG_CONSOLE, WParam(iLen), LParam(pc));
end;

function LogFile(_str :string) :integer;
var hFile :THandle;
    strFileName :string;
    dwWritten :DWORD;
    lb :LongBool;
begin
  Result := 0;
  strFileName := ParamStr(0)+'.'+FormatDateTime('yyyymmdd', now)+'.log';
  hFile := CreateFile(PChar(strFileName), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, 0, 0);
  if (hFile = INVALID_HANDLE_VALUE) then
  begin
    Result := GetLastError;
    Exit;
  end;
  lb := Windows.SetEndOfFile(hFile);
  if (not lb) then
  begin
    Result := GetLastError;
    CloseHandle(hFile);
    Exit;
  end;
  dwWritten := 0;
  lb := Windows.WriteFile(hFile, _str[1], Length(_str), dwWritten, nil);
  if (not lb) then
  begin
    Result := GetLastError;
    CloseHandle(hFile);
    Exit;
  end;
  CloseHandle(hFile);
end;

function FieldTypeName_f(_iFieldType :Integer; var _strFieldTypeName :string) :Integer;
begin
  case _iFieldType of
    DR_LONGNVARCHAR : _strFieldTypeName := 'DR_LONGNVARCHAR';
    DR_NCHAR : _strFieldTypeName := 'DR_NCHAR';
    DR_NVARCHAR : _strFieldTypeName := 'DR_NVARCHAR';
    DR_ROWID : _strFieldTypeName := 'DR_ROWID';
    DR_BIT : _strFieldTypeName := 'DR_BIT';
    DR_TINYINT : _strFieldTypeName := 'DR_TINYINT';
    DR_BIGINT : _strFieldTypeName := 'DR_BIGINT';
    DR_LONGVARBINARY : _strFieldTypeName := 'DR_LONGVARBINARY';
    DR_VARBINARY : _strFieldTypeName := 'DR_VARBINARY';
    DR_BINARY : _strFieldTypeName := 'DR_BINARY';
    DR_LONGVARCHAR : _strFieldTypeName := 'DR_LONGVARCHAR';
    DR_NULL : _strFieldTypeName := 'DR_NULL';
    DR_CHAR : _strFieldTypeName := 'DR_CHAR';
    DR_NUMERIC : _strFieldTypeName := 'DR_NUMERIC';
    DR_DECIMAL : _strFieldTypeName := 'DR_DECIMAL';
    DR_INTEGER : _strFieldTypeName := 'DR_INTEGER';
    DR_SMALLINT : _strFieldTypeName := 'DR_SMALLINT';
    DR_FLOAT : _strFieldTypeName := 'DR_FLOAT';
    DR_REAL : _strFieldTypeName := 'DR_REAL';
    DR_DOUBLE : _strFieldTypeName := 'DR_DOUBLE';
    DR_VARCHAR : _strFieldTypeName := 'DR_VARCHAR';
    DR_BOOLEAN : _strFieldTypeName := 'DR_BOOLEAN';
    DR_DATALINK : _strFieldTypeName := 'DR_DATALINK';
    DR_DATE : _strFieldTypeName := 'DR_DATE';
    DR_TIME : _strFieldTypeName := 'DR_TIME';
    DR_TIMESTAMP : _strFieldTypeName := 'DR_TIMESTAMP';
    DR_OTHER : _strFieldTypeName := 'DR_OTHER';
    DR_JAVA_OBJECT : _strFieldTypeName := 'DR_JAVA_OBJECT';
    DR_DISTINCT : _strFieldTypeName := 'DR_DISTINCT';
    DR_STRUCT : _strFieldTypeName := 'DR_STRUCT';
    DR_ARRAY : _strFieldTypeName := 'DR_ARRAY';
    DR_BLOB : _strFieldTypeName := 'DR_BLOB';
    DR_CLOB : _strFieldTypeName := 'DR_CLOB';
    DR_REF : _strFieldTypeName := 'DR_REF';
    DR_SQLXML : _strFieldTypeName := 'DR_SQLXML';
    DR_NCLOB : _strFieldTypeName := 'DR_NCLOB';
    else
      _strFieldTypeName := 'Unknown('+inttostr(_iFieldType)+')';
  end;
end;

procedure GetMacByIP(_iIp :integer; var _strMac :string);
var adapterInfo, pInfo : PIPAdapterInfo;
    dwSize    : DWORD;
    iRes     : Integer;
    ipAddrStr :TIPAddrString;
    pIpAddrStr :PIPAddrString;
    i :Integer;
    iIp :Integer;
    bFind :Boolean;
begin
  bFind := false;

  dwSize := 0;
  GetAdaptersInfo(nil, dwSize);
  GetMem(adapterInfo, dwSize);
  iRes := GetAdaptersInfo(adapterInfo, dwSize);
  If (iRes <> ERROR_SUCCESS) Then
  begin
     MessageBoxA(0,'获取IP信息失败', '错误', MB_OK or MB_ICONERROR);
     exit;
  end;
  pInfo := adapterInfo;
  repeat
    // IP地址
    ipAddrStr := pInfo.IPAddressList;
          iIp := inet_addr(ipAddrStr.IPAddress);
          if (_iIp = iIp) then
          begin
            bFind := True;
            _strMac := '';
            for i:=1 to pInfo.AddressLength do
            begin
              _strMac := _strMac + IntToHex(pInfo.Address[i], 2);
              if (i <> pInfo.AddressLength) then
                _strMac := _strMac + ':';
            end;

            Break;
          end;
    pIpAddrStr := ipAddrStr.Next;
    while pIpAddrStr<>nil do
    begin
          iIp := inet_addr(ipAddrStr.IPAddress);
          if (_iIp = iIp) then
          begin
            bFind := True;

            _strMac := '';
            for i:=1 to pInfo.AddressLength do
            begin
              _strMac := _strMac + IntToHex(pInfo.Address[i], 2);
              if (i <> pInfo.AddressLength) then
                _strMac := _strMac + ':';
            end;

            Break;
          end;
      pIpAddrStr := pIpAddrStr.Next;
    end;

    if (bFind) then Break;
    pInfo := pInfo^.Next;
  until (pInfo = nil);
  FreeMem(adapterInfo);
end;

procedure BufferBlockUsage(_iOpType :Integer; var _str :string);
begin
  if (_iOpType = OP_TYPE_SQL) then
    _str := 'SQL语句'
  else if (_iOpType = OP_TYPE_PUSH) then
    _str := 'TCP推送'
  else if (_iOpType = OP_TYPE_MANAGE) then
    _str := '管理数据'
  else if (_iOpType = OP_TYPE_MANAGE_CLIENTS) then
    _str := '管理(所有客户端IP/Port)'
  else if (_iOpType = OP_TYPE_MANAGE_SOCKET_RES) then
    _str := '管理(某客户端socket信息)'
  else if (_iOpType = OP_TYPE_MANAGE_BLOCK_RES) then
    _str := '管理(某客户端内存块信息)'
  else if (_iOpType = OP_TYPE_RECV) then
    _str := 'TCP接收缓冲';
end;

function MAKELANGID(_p, _s :word) :DWORD;
begin
  Result := (_s shl 10) or (_p);
end;

function ErrorNo2Str(_dwErrNo :DWORD):string;
const
  LANG_NEUTRAL = $0;
  SUBLANG_DEFAULT = $01;
var buf :array[0..255] of Char;
begin
  ZeroMemory(@buf[0], Length(buf));
  Windows.FormatMessage(
    FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS,
    nil,
    _dwErrNo,     
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    buf,
    Length(buf),
    nil);

  Result := buf;
end;

{ TdrBufferPool }

constructor TdrBufferPool.create;
begin
  //inherited Create;
  Flist := TList.Create;
  FlistAll := TList.Create;
  FhEvent := CreateEvent(nil, False, true, nil);
end;

destructor TdrBufferPool.destroy;//override;
begin
  if Assigned(Flist) then
    Flist.Free;

  inherited;
end;

function TdrBufferPool.NewBlock(_iLen: Integer): TdrBuffer;
var iYuShu :Integer;
begin
  iYuShu := _iLen mod BUFFER_BLOCK;

  Result := TdrBuffer.Create;
  if (_iLen<>0)and(iYuShu = 0) then
    Result.FiTotalLen := _iLen
  else
    Result.FiTotalLen := (_iLen div BUFFER_BLOCK + 1) * BUFFER_BLOCK;
  GetMem(Result.Fpc, Result.FiTotalLen);

  Result.FiValidBeginOffset := 0;
  Result.FiValidLen := 0;

  FlistAll.Add(Result);
end;

function TdrBufferPool.DelBlock(_buffer :TdrBuffer) :Integer;
var iIdx :Integer;
begin
  Result := 0;
  iIdx := FlistAll.IndexOf(_buffer);
  FlistAll.Delete(iIdx);
  if (iIdx = -1) then
    Result := -1;
end;

function TdrBufferPool.AquireBlock(_dwType :DWORD; _iLen: Integer): TdrBuffer;
var iAquireLen, i, iIdx, iYuShu :Integer;
    buffer :TdrBuffer;
begin
  Result := nil;
  WaitForSingleObject(FhEvent, INFINITE);
  try
    if (Flist.Count = 0) then
      Result := NewBlock(_iLen)
    else
    begin
      iIdx := -1;
      iYuShu := _iLen mod BUFFER_BLOCK;
      if (_iLen<>0)and(iYuShu = 0) then
        iAquireLen := _iLen
      else
        iAquireLen := (_iLen div BUFFER_BLOCK + 1) * BUFFER_BLOCK;
      for i:=0 to Flist.Count-1 do
      begin
        buffer := TdrBuffer(Flist.Items[i]);
        if iAquireLen <= buffer.FiTotalLen then
        begin
          iIdx := i;
          break;
        end;
      end;

      if (iIdx = -1) then
        Result := NewBlock(_iLen)
      else
      begin
        Result := TdrBuffer(Flist.Items[iIdx]);
        Flist.Delete(iIdx);
      end;

      Result.FiValidBeginOffset := 0;
      Result.FiValidLen := 0;
    end;
    Result.FdwType := _dwType;
    PostMessage(g_callbackWnd.FhWnd, WM_TCP_RECV, 1, _dwType);
    Result.FdwLastTick := GetTickCount;
  finally
    SetEvent(FhEvent);
  end;
end;

function TdrBufferPool.ReleaseBlock(_buffer: TdrBuffer): Integer;
const
  BUFFER_BLOCK_MAX_LEN = 1024 * 1024 * 500; // >=这个数的内存,采取直接释放内存缓冲区的操作方式
var i :Integer;
    buffer0, buffer1 :TdrBuffer;
begin
  PostMessage(g_callbackWnd.FhWnd, WM_TCP_RECV, 2, _buffer.FdwType);
  _buffer.FdwType := 0;
  Result := 0;
  WaitForSingleObject(FhEvent, INFINITE);
  try
    if (_buffer.FiTotalLen >= BUFFER_BLOCK_MAX_LEN) then  // >=这个数的内存,采取直接释放内存缓冲区的操作方式
    begin
      DelBlock(_buffer);
      
      FreeMem(_buffer.Fpc, _buffer.FiTotalLen);
      _buffer.Free;
      Exit;
    end;

    if (Flist.Count = 0) then
    begin
      Flist.Add(_buffer);
      Exit;
    end;

    buffer0 := TdrBuffer(Flist.Items[0]);
    if (_buffer.FiTotalLen <= buffer0.FiTotalLen) then
    begin
      Flist.Insert(0, _buffer);
      Exit;
    end
    else
    begin
      for i:=1 to Flist.Count-1 do
      begin
        buffer0 := TdrBuffer(Flist.Items[i-1]);
        buffer1 := TdrBuffer(Flist.Items[i]);

        if (_buffer.FiTotalLen > buffer0.FiTotalLen)and(_buffer.FiTotalLen <= buffer1.FiTotalLen) then
        begin
          Flist.Insert(i, _buffer);
          Exit;
        end;
      end;

      Flist.Add(_buffer);
    end;
  finally
    SetEvent(FhEvent);
  end;
end;

{ TdrRecvBuffer }

constructor TdrRecvBuffer.Create;
begin
  Flist := TList.Create;
  FhEvent := CreateEvent(nil, False, false, nil);
end;

destructor TdrRecvBuffer.destroy;//override;
begin
  if Assigned(Flist) then
    Flist.Free;
  if (FhEvent<>0) then
    CloseHandle(FhEvent);
  inherited;
end;

function TdrRecvBuffer.BufferAuqire(out _pc: PChar; out _iLen: integer): integer;
var buffer, bufferNew :TdrBuffer;
    iCnt1, iCntUsed :Integer;
begin
  Result := -1;
  if (Flist.Count = 0) then
  begin
    bufferNew := g_bufferPool.AquireBlock(OP_TYPE_RECV, 0);
    Flist.Add(bufferNew);
    Result := Flist.Count - 1;
    
    _pc := bufferNew.Fpc;
    _iLen := bufferNew.FiTotalLen;
  end
  else
  begin
    buffer := TdrBuffer(Flist.Items[Flist.Count - 1]);
    if (buffer.FiValidBeginOffset + buffer.FiValidLen) > buffer.FiTotalLen then
      raise Exception.CreateFmt('TdrRecvBuffer.BufferAuqire 缓冲区错误(1) : %d, %d, %d',
        [buffer.FiValidBeginOffset, buffer.FiValidLen, buffer.FiTotalLen])
    else if (buffer.FiValidBeginOffset + buffer.FiValidLen) = buffer.FiTotalLen then
    begin
      bufferNew := g_bufferPool.AquireBlock(OP_TYPE_RECV, 0);
      Flist.Add(bufferNew);
      Result := Flist.Count - 1;
      
      _pc := bufferNew.Fpc;
      _iLen := bufferNew.FiTotalLen;
    end
    else// if (buffer.FiValidBeginOffset + buffer.FiValidLen) < buffer.FiTotalLen then
    begin
      iCntUsed := buffer.FiValidBeginOffset + buffer.FiValidLen;
      _pc := @buffer.Fpc[iCntUsed];
      _iLen := buffer.FiTotalLen - iCntUsed;
      Result := Flist.Count - 1;
    end;
  end;
end;

function TdrRecvBuffer.BufferReleaseAll :integer;
begin
  while Flist.Count > 0 do
  begin
    g_bufferPool.ReleaseBlock(TdrBuffer(Flist.Items[0]));
    Flist.Delete(0);
  end;
end;

function TdrRecvBuffer.BufferRecv(_iRecv: Integer; _iBlockIdx: integer): Integer;
var iCntUsed :Integer;
    buffer :TdrBuffer;
begin
  Result := 0;
  Inc(FiTotalLen, _iRecv);
  
  buffer := TdrBuffer(Flist.Items[_iBlockIdx]);
  Inc(buffer.FiValidLen, _iRecv);
  if (buffer.FiValidBeginOffset + buffer.FiValidLen) > buffer.FiTotalLen then
    raise Exception.CreateFmt('TdrRecvBuffer.BufferRecv 缓冲区错误(1) : %d, %d, %d',
        [buffer.FiValidBeginOffset, buffer.FiValidLen, buffer.FiTotalLen]);
end;

function TdrRecvBuffer.BufferHandle: Integer;   // 处理单个 TCP包
var bufferCopy, buffer :TdrBuffer;
    iRtn, iPktIsNeed :Integer;
    iPktLen, iPktIdx, iPktType :Integer;
    iPktLen01 :Integer;
    iBlockIdx, iBlockDropEndIdx :Integer;
    iCopyOffset, i, iCopyLen :Integer;
    bNeedBreak :Boolean;
begin
  Result := 0;

  if (FiTotalLen < TCP_PACKET_HEADER_LEN) then // 一个TCP包都没有接收完毕
  begin
    Result := 1;
    Exit;
  end;
  if (FiFirstPktLen <> 0) and (FiTotalLen < FiFirstPktLen) then
  begin
    Result := 2;
    Exit;
  end;

  iRtn := TcpPktHeader(iPktLen, iPktIdx, iPktType);
  FiFirstPktLen := iPktLen;
  if (FiTotalLen < iPktLen) then // 一个TCP包都没有接收完毕
  begin
    Result := 3;
    Exit;
  end;
  FiFirstPktLen := 0;

  iPktIsNeed := TcpPktIsNeed(iPktIdx, iPktType);

  // *** (1) *** 缓冲区的分配/获取
      bufferCopy := nil;
      if (iPktIsNeed > 0) then
        bufferCopy := g_bufferPool.AquireBlock(iPktType, iPktLen);

  // *** (2) *** 缓冲区内容填充
  iCopyOffset := 0;
  iBlockIdx := 0;
  iPktLen01 := iPktLen;
  iBlockDropEndIdx := -1;
  while (iPktLen01 > 0) do
  begin
    bNeedBreak := false;
    if (iBlockIdx >= Flist.Count) then
      raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(1) : %d, %d', [iBlockIdx, Flist.Count]);

    buffer := TdrBuffer(Flist.Items[iBlockIdx]);
    if (buffer.FiValidBeginOffset + buffer.FiValidLen) > buffer.FiTotalLen then
      raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(2) : %d, %d, %d, %d',
          [iBlockIdx, buffer.FiValidBeginOffset, buffer.FiValidLen, buffer.FiTotalLen])
    else if (buffer.FiValidBeginOffset + buffer.FiValidLen) = buffer.FiTotalLen then
    begin
      if iBlockDropEndIdx <> (iBlockIdx-1) then
        raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(3) : %d, %d, %d', [iBlockIdx, iBlockDropEndIdx, iBlockIdx]);
      iBlockDropEndIdx := iBlockIdx;
    end
    else// if (buffer.FiValidBeginOffset + buffer.FiValidLen) < buffer.FiTotalLen then
      bNeedBreak := True;

    if (not bNeedBreak) then
    begin // 不是 某TCP包中的最后一块内存块的话,内存块的尾端肯定是占满的
      if (buffer.FiValidBeginOffset + buffer.FiValidLen) <> buffer.FiTotalLen then
        raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(4) : %d, %d, %d, %d',
          [iBlockIdx, buffer.FiValidBeginOffset, buffer.FiValidLen, buffer.FiTotalLen]);
    end;
    
    // 缓冲区数据 复制
    // 三部曲:(1)计算iCopyLen (2)CopyMemory (3)计算3个数值
    iCopyLen := 0;
    if (iBlockIdx = 0) then
    begin
      iCopyLen := Math.IfThen(iPktLen01 > buffer.FiValidLen, buffer.FiValidLen, iPktLen01);
      if (bufferCopy <> nil) then
        CopyMemory(@bufferCopy.Fpc[iCopyOffset], @buffer.Fpc[buffer.FiValidBeginOffset], iCopyLen);

      Dec(iPktLen01, iCopyLen);
      Inc(buffer.FiValidBeginOffset, iCopyLen); // 缓冲区指针移动
      Dec(buffer.FiValidLen, iCopyLen);
    end
    else
    begin
      if buffer.FiValidBeginOffset <> 0 then
        raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(5) : %d, %d', [iBlockIdx, buffer.FiValidBeginOffset]);

      iCopyLen := Math.IfThen(iPktLen01 > buffer.FiValidLen, buffer.FiValidLen, iPktLen01);

      if (bufferCopy <> nil) then
        CopyMemory(@bufferCopy.Fpc[iCopyOffset], @buffer.Fpc[0], iCopyLen);

      Dec(iPktLen01, iCopyLen);
      Inc(buffer.FiValidBeginOffset, iCopyLen); // 缓冲区指针移动
      Dec(buffer.FiValidLen, iCopyLen);
    end;
    Inc(bufferCopy.FiValidLen, iCopyLen);
    Inc(iCopyOffset, iCopyLen);

    // ***
    if (iCopyOffset > bufferCopy.FiTotalLen) then
      raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(6) : %d, %d, %d', [iBlockIdx, iCopyOffset, bufferCopy.FiTotalLen]);
      
    if (bNeedBreak) then
    begin
      if (iPktLen01 <> 0) then
        raise Exception.CreateFmt('TdrRecvBuffer.BufferHandle 缓冲区错误(7) : %d, %d', [iBlockIdx, buffer.FiValidBeginOffset]);

      break;
    end;

    Inc(iBlockIdx);
  end; // while
  
  // *** (3) *** 缓冲区 分发
  TcpPktDispatch(bufferCopy);

  // 扫尾处理
  Dec(FiTotalLen, iPktLen);
  if iBlockDropEndIdx > -1 then
    for i:=0 to iBlockDropEndIdx do
    begin
      g_bufferPool.ReleaseBlock(TdrBuffer(Flist.Items[0]));
      Flist.Delete(0);
    end;
end;

function TdrRecvBuffer.BufferHandleAll: Integer;  // 处理所有已经接收到的 TCP包
var iRtn : integer;
begin
  Result := 0;

  while FiTotalLen > (TCP_PACKET_HEADER_LEN) do
  begin
    iRtn := BufferHandle;
    if (iRtn > 0) then
      break;
  end;
end;

function TdrRecvBuffer.TcpPktHeader(out _iPktLen, _iPktIdx, _iPktType: Integer): Integer;  // 始终默认是 [0]的缓冲区块
var aryByte12 :array[0..11] of Char;
    buffer :TdrBuffer;
    iCntLeft :Integer;
    pi :PInteger;
begin
  Result := 0;
  buffer := TdrBuffer(Flist.Items[0]);
  if (buffer.FiValidLen >= TCP_PACKET_HEADER_LEN) then
  begin
    pi := @buffer.Fpc[buffer.FiValidBeginOffset + 0];  _iPktLen := pi^;
    pi := @buffer.Fpc[buffer.FiValidBeginOffset + 4];  _iPktIdx := pi^;
    pi := @buffer.Fpc[buffer.FiValidBeginOffset + 8];  _iPktType:= pi^;
  end
  else
  begin
    if (Flist.Count = 1) then
      raise Exception.CreateFmt('TdrRecvBuffer.TcpPktHeader 缓冲区错误(1) : %d', [Flist.Count]);

    iCntLeft := TCP_PACKET_HEADER_LEN - buffer.FiValidLen;
    
    CopyMemory(@aryByte12[0], @buffer.Fpc[buffer.FiValidBeginOffset], buffer.FiValidLen);
    
    buffer := TdrBuffer(Flist.Items[1]);
    if (buffer.FiValidBeginOffset <> 0)or(buffer.FiValidLen < iCntLeft) then
      raise Exception.CreateFmt('TdrRecvBuffer.TcpPktHeader 缓冲区错误(2) : %d, %d, %d',
         [buffer.FiValidBeginOffset, buffer.FiValidLen, iCntLeft]);
    CopyMemory(@aryByte12[0], @buffer.Fpc[0], iCntLeft);

    pi := @aryByte12[0];  _iPktLen := pi^;
    pi := @aryByte12[4];  _iPktIdx := pi^;
    pi := @aryByte12[8];  _iPktType:= pi^;
  end;
end;

function TdrRecvBuffer.TcpPktIsNeed(_iPktIdx, _iPktType :integer):Integer;
begin
  Result := 0;
  if (_iPktType = OP_TYPE_SQL) then // 主动的SQL语句请求
  begin
    if (_iPktIdx = FiPktIdx) then
      Result := 1;
  end
  else if (_iPktType = OP_TYPE_PUSH) then // TCP服务端推送消息
  begin
    Result := 1;
  end
  else if (_iPktType and OP_TYPE_MANAGE) <> 0 then // 管理
  begin
    Result := 1;
  end;
end;

procedure TdrRecvBuffer.TcpPktDispatch(_buffer :TdrBuffer);
var pi :PInteger;
    iPktLen, iPktIdx, iPktType :Integer;
begin
  pi := @_buffer.Fpc[4];  iPktIdx := pi^;
  pi := @_buffer.Fpc[8];  iPktType:= pi^;

  if (iPktType = OP_TYPE_SQL) then // 主动的SQL语句请求
  begin
    if (iPktIdx = FiPktIdx) then
      SetEvent(FhEvent);
  end
  else if (iPktType = OP_TYPE_PUSH) then // TCP服务端推送消息
  begin
    PostMessage(g_callbackWnd.FhWnd, WM_TCP_PUSH, 0, LParam(_buffer));
  end
  else if (iPktType and OP_TYPE_MANAGE) <> 0 then // 管理
  begin
    if DWORD(iPktType) = OP_TYPE_MANAGE_CLIENTS then // 所有客户端的信息
      PostMessage(g_callbackWnd.FhWnd, WM_TCP_CLIENTS, 0, LParam(_buffer))
    else if DWORD(iPktType) = OP_TYPE_MANAGE_SOCKET_REQ then
      PostMessage(g_callbackWnd.FhWnd, WM_CLIENT_SOCKET_MSG_REQ, 0, LParam(_buffer))
    else if DWORD(iPktType) = OP_TYPE_MANAGE_SOCKET_RES then
      PostMessage(g_callbackWnd.FhWnd, WM_CLIENT_SOCKET_MSG_RES, 0, LParam(_buffer))
    else if DWORD(iPktType) = OP_TYPE_MANAGE_BLOCK_REQ then
      PostMessage(g_callbackWnd.FhWnd, WM_CLIENT_BLOCK_MSG_REQ, 0, LParam(_buffer))
    else if DWORD(iPktType) = OP_TYPE_MANAGE_BLOCK_RES then
      PostMessage(g_callbackWnd.FhWnd, WM_CLIENT_BLOCK_MSG_RES, 0, LParam(_buffer))
    else if DWORD(iPktType) = OP_TYPE_MANAGE_SQL then
      PostMessage(g_callbackWnd.FhWnd, WM_MANAGE_SQL, 0, LParam(_buffer))
    else
    begin
    end;
  end;
end;

{ TdrTcpClient }

function TdrTcpClient.RecvTimeoutSet: Integer;
var iRecvTimeout, iRtn :Integer;
begin
  Result := 0;
  iRecvTimeout := 1; // 毫秒
  iRtn := setsockopt(Fskt, SOL_SOCKET, SO_RCVTIMEO, PChar(@iRecvTimeout), sizeof(integer));
  if SOCKET_ERROR = iRtn then
  begin
    Result := GetLastError;
    FiErrorNo := Result;
  end;
end;

function TdrTcpClient.RecvTimeoutCancel: Integer;
var iRecvTimeout, iRtn :Integer;
begin
  Result := 0;
  iRecvTimeout := 0; // 毫秒
  iRtn := setsockopt(Fskt, SOL_SOCKET, SO_RCVTIMEO, PChar(@iRecvTimeout), sizeof(integer));
  if SOCKET_ERROR = iRtn then
  begin
    Result := GetLastError;
    FiErrorNo := Result;
  end;
end;

procedure TdrTcpClient.Execute;
var pcRecv :PChar;
    iLenRecv :Integer;
    iRecvBlockIdx :integer;
    iRecv, iRtn :Integer;
begin
  //inherited;
  
  g_drRecvBuffer.BufferReleaseAll;
  FiStatus := 1;

  while True do
  begin
    iRecvBlockIdx := g_drRecvBuffer.BufferAuqire(pcRecv, iLenRecv);
    iRecv := recv(Fskt, pcRecv^, iLenRecv, 0);
    if (iRecv = 0) then // 连接优雅(gracefully)关闭
    begin
      FiErrorNo := 0;
      break;
    end
    else if (iRecv < 0) then
    begin
      FiErrorNo := GetLastError;
      break;
    end
    else// if (iRecv > 0) then
    begin
      g_drRecvBuffer.BufferRecv(iRecv, iRecvBlockIdx);
      g_drRecvBuffer.BufferHandleAll;
      PostMessage(g_callbackWnd.FhWnd, WM_TCP_RECV, 0, iRecv);
    end;
// ZC: 用timeout操作的时候,在数据读完之后 有明显感觉的卡顿现象(大概有1s左右)...于是,放弃使用timeout读数据
//    iRtn := Recv01;
//    if (iRtn <= 0) then
//      break;
//    g_drRecvBuffer.BufferHandleAll;
  end;

  closesocket(Fskt);
  FiStatus := -1;

  // 线程执行完毕(退出)
  //raise Exception.CreateFmt('socket线程退出 : %d', [FiErrorNo]);
end;

function TdrTcpClient.Recv01: Integer;
var pcRecv :PChar;
    iLenRecv :Integer;
    iRecvBlockIdx :integer;
    iRecv :Integer;
begin
  Result := 0;
  
  if RecvTimeoutSet<>0 then
  begin
    Result := GetLastError;
    Exit;
  end;

  while true do
  begin
    iRecvBlockIdx := g_drRecvBuffer.BufferAuqire(pcRecv, iLenRecv);
    iRecv := recv(Fskt, pcRecv^, iLenRecv, 0);
    if (iRecv = 0) then // 连接优雅(gracefully)关闭
    begin
      Result := -1;
      FiErrorNo := 0;
    end
    else if (iRecv < 0) then
    begin
      FiErrorNo := GetLastError;
      if WSAETIMEDOUT <> FiErrorNo then
      begin
        Result := -2;
      end
      else
      begin
        Result := 1;
      end;
      break;
    end
    else// if (iRecv > 0) then
    begin
      g_drRecvBuffer.BufferRecv(iRecv, iRecvBlockIdx);
      if (iRecv <> iLenRecv) then
      begin
        Result := 2;
        Break;
      end;
    end;
  end;

  if RecvTimeoutCancel <> 0 then
    Result := GetLastError;
end;

function TdrTcpClient.Send01(_pbyte: PByte; _iLen: integer): Integer;
var iRtn :Integer;
begin
  Result := 0;
  iRtn := send(Fskt, _pbyte^, _iLen, 0);
  if iRtn = SOCKET_ERROR then
    Result := GetLastError;
end;

class function TdrTcpClient.Conn :integer;
var skt :TSocket;
    bNeedCreate :boolean;
begin
  Result := 0;
  if (length(g_strDestIp)=0)or(g_iDestPort=0) then
  begin
    raise Exception.Create('服务器IP/Poer 未设置');
    Exit;
  end;

  bNeedCreate := false;
  if (g_tcpClient = nil) then
    bNeedCreate := true
  else
  begin
    if (g_tcpClient.FiStatus = 0) then
    begin
      Result := -1;
      Exit;
    end
    else if (g_tcpClient.FiStatus = -1) then
    begin
      bNeedCreate := True;
      g_tcpClient.Free;
    end;
  end;
  if (bNeedCreate) then
  begin
    skt := TdrTcpClient.ConnectDest;
    g_tcpClient := TdrTcpClient.Create(false);
    g_tcpClient.Fskt := skt;
  end;
end;

class procedure TdrTcpClient.SendSQL(_str: string);
var iPktLen, iPktIdx, iPktType :Integer;
    bytesSend :array[0..255] of byte;
    iRtn :Integer;
begin
  if Conn < 0 then
    raise Exception.Create('TCP接收线程正在初始化,请稍后再试...');

  Inc(g_drRecvBuffer.FiPktIdx);

  iPktLen := TCP_PACKET_HEADER_LEN + Length(_str);
  iPktIdx := g_drRecvBuffer.FiPktIdx;
  iPktType := OP_TYPE_SQL;
  
  CopyMemory(@bytesSend[0], @iPktLen, 4);  // TCP包头 --(1) 总长
  CopyMemory(@bytesSend[4], @iPktIdx, 4);  // TCP包头 --(2) 序号
  CopyMemory(@bytesSend[8], @iPktType, 4);                 // TCP包头 --(3) 类型
  CopyMemory(@bytesSend[12], PChar(_str), Length(_str));   // TCP包 内容

  iRtn := g_tcpClient.Send01(@bytesSend[0], iPktLen);
  if (iRtn <> 0) then
    raise Exception.CreateFmt('发送消息异常 : (%d)%s', [iRtn, ErrorNo2Str(iRtn)]);
  WaitForSingleObject(g_drRecvBuffer.FhEvent, INFINITE);
end;

class procedure TdrTcpClient.SendBytes(_pc :PChar; _iLen :Integer);
var iRtn :Integer;
begin
  if Conn < 0 then
    raise Exception.Create('TCP接收线程正在初始化,请稍后再试...');
    
  iRtn := g_tcpClient.Send01(@_pc[0], _iLen);
  if (iRtn <> 0) then
    raise Exception.CreateFmt('发送消息异常 : (%d)%s', [iRtn, ErrorNo2Str(iRtn)]);
end;

class function  TdrTcpClient.ConnectDest :TSocket;
var wsadata1 :WSADATA;
    addrSrv :SOCKADDR_IN;
    iRtn :Integer;
    skt :TSocket;
    dwErrorNo :DWORD;
begin
  Result := 0;
  WSAStartup(MAKEWORD(1,1), wsadata1);
  skt := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if (skt <= 0) then
  begin
    dwErrorNo := GetLastError;
    raise Exception.CreateFmt('%s %d', ['套接字创建异常:', dwErrorNo]);
  end;

  addrSrv.sin_family := AF_INET;
  addrSrv.sin_port := htons(g_iDestPort);
  addrSrv.sin_addr.S_addr := inet_addr(PChar(g_strDestIp));

  iRtn := connect(skt, addrSrv, sizeof(addrSrv));
  if(iRtn <> 0) then
  begin
    dwErrorNo := GetLastError;
    raise Exception.CreateFmt('%s %d %s', ['服务器连接异常:', dwErrorNo, ErrorNo2Str(dwErrorNo)]);
  end;
  Result := skt;
end;

class procedure TdrTcpClient.SetIpPort(_strDestIp: string; _iDestPort: integer);
begin
  g_strDestIp := _strDestIp;
  g_iDestPort := _iDestPort;
end;

class function TdrTcpClient.Status: Integer;
begin
  if (g_tcpClient = nil) then
    Result := -100
  else
    Result := g_tcpClient.FiStatus;
end;

class procedure TdrTcpClient.SendHeartBeat;
var pc :array[0..12] of Char;
    iPktLen, iPktIdx, iPktType :Integer;
begin
  iPktLen := 12;
  iPktIdx := 0;
  iPktType := OP_TYPE_HEARTBEAT;
  CopyMemory(@pc[0], @iPktLen, 4);
  CopyMemory(@pc[4], @iPktIdx, 4);
  CopyMemory(@pc[8], @iPktType, 4);
  SendBytes(pc, iPktLen);
end;

{ TcallbackWnd }

function ProcWindow(_hWnd :HWND;
                    _uMsg :UINT;
                    _wParam :WPARAM;
                    _lParam :LPARAM):longint;stdcall;
var buffer :TdrBuffer;
    pc :PChar;
begin
  Result := 0; // 用户已经处理
  if (_uMsg = WM_TCP_PUSH) then
  begin
    if Assigned(g_callbackWnd.FfuncPush) then
    begin
      // 得到 缓冲区
      buffer := TdrBuffer(_lParam);
      // 业务逻辑处理
      g_callbackWnd.FfuncPush(@buffer.Fpc[TCP_PACKET_HEADER_LEN], buffer.FiValidLen - TCP_PACKET_HEADER_LEN);
      // 释放缓冲区
      g_bufferPool.ReleaseBlock(buffer);
    end;
  end
  else if (_uMsg = WM_TCP_CLIENTS) then
  begin
    if Assigned(g_callbackWnd.FfuncClients) then
    begin
      buffer := TdrBuffer(_lParam);
      g_callbackWnd.FfuncClients(@buffer.Fpc[TCP_PACKET_HEADER_LEN], buffer.FiValidLen - TCP_PACKET_HEADER_LEN);
      g_bufferPool.ReleaseBlock(buffer);
    end;
  end
  else if (_uMsg = WM_CLIENT_SOCKET_MSG_REQ) then
  begin
    buffer := TdrBuffer(_lParam);
    g_callbackWnd.PackSocket(@buffer.Fpc[TCP_PACKET_HEADER_LEN], buffer.FiValidLen - TCP_PACKET_HEADER_LEN);
    g_bufferPool.ReleaseBlock(buffer);
  end
  else if (_uMsg = WM_CLIENT_BLOCK_MSG_REQ) then
  begin
    buffer := TdrBuffer(_lParam);
    g_callbackWnd.PackBlock(@buffer.Fpc[TCP_PACKET_HEADER_LEN], buffer.FiValidLen - TCP_PACKET_HEADER_LEN);
    g_bufferPool.ReleaseBlock(buffer);
  end
  else if (_uMsg = WM_CLIENT_SOCKET_MSG_RES) then
  begin
    if Assigned(g_callbackWnd.FfuncSocket) then
    begin
      buffer := TdrBuffer(_lParam);
      g_callbackWnd.FfuncSocket(@buffer.Fpc[TCP_PACKET_HEADER_LEN], buffer.FiValidLen - TCP_PACKET_HEADER_LEN);
      g_bufferPool.ReleaseBlock(buffer);
    end;
  end
  else if (_uMsg = WM_CLIENT_BLOCK_MSG_RES) then
  begin
    if Assigned(g_callbackWnd.FfuncBlock) then
    begin
      buffer := TdrBuffer(_lParam);
      g_callbackWnd.FfuncBlock(@buffer.Fpc[TCP_PACKET_HEADER_LEN], buffer.FiValidLen - TCP_PACKET_HEADER_LEN);
      g_bufferPool.ReleaseBlock(buffer);
    end;
  end
  else if (_uMsg = WM_MANAGE_SQL) then
  begin
    buffer := TdrBuffer(_lParam);
    if Assigned(g_callbackWnd.FfuncSQL) then
      g_callbackWnd.FfuncSQL(buffer);
    g_bufferPool.ReleaseBlock(buffer);
  end
  else if (WM_TCP_RECV = _uMsg) then
  begin
    if (_wParam = 0) then
      frmMain.Memo1.Lines.Add('Recv : '+inttostr(_lParam))
    else if (_wParam = 1) then
      frmMain.Memo1.Lines.Add('Allocate buffer block for : 0x'+inttohex(_lParam, 8))
    else if (_wParam = 2) then
      frmMain.Memo1.Lines.Add('Release buffer block from : 0x'+inttohex(_lParam, 8))
    else if (_wParam = 3) then
      frmMain.Memo1.Lines.Add('Space for unicode string : '+inttostr(_lParam))
    else if (_wParam = 4) then
      frmMain.Memo1.Lines.Add('Space for ansi string : '+inttostr(_lParam))
    else if (_wParam = 5) then
      frmMain.Memo1.Lines.Add('UTF-8 string space is : '+inttostr(_lParam));
  end
  else if (WM_LOG_CONSOLE = _uMsg) then
  begin
    pc := PChar(_lParam);
    frmMain.Memo1.Lines.Add(PChar(_lParam));
    FreeMem(pc, _wParam);
  end
  else if (_uMsg = WM_TIMER) then
  begin
    if (1 = _wParam) then
    begin
      if (g_tcpClient<>nil)and(g_tcpClient.FiStatus = 1) then
        TdrTcpClient.SendHeartBeat;
    end;
  end
  else if (_uMsg = WM_DESTROY) then
  begin
    DestroyWindow(_hwnd);
    PostQuitMessage(0);
  end
  else  
    Result := DefWindowProc(_hWnd, _uMsg, _wParam, _lParam);
end;

constructor TcallbackWnd.Create;
begin
  CreateWindow;

  //SetTimer(FhWnd, 1, 1000 * 10, nil);
end;

procedure TcallbackWnd.CreateWindow;
var wndcls :WNDCLASS;
    hInstance :THandle;
begin
  hInstance := Windows.GetModuleHandle(nil);

  ZeroMemory(@wndcls, sizeof(wndcls));
    wndcls.cbClsExtra    := 0;
    wndcls.cbWndExtra    := 0;
    wndcls.hbrBackground := HBRUSH(GetStockObject(WHITE_BRUSH));    // 背景画刷
    wndcls.hCursor        := LoadCursor(0, IDC_CROSS);
    wndcls.hIcon        := LoadIcon(0, IDI_ERROR);    // 窗口图标
    wndcls.hInstance    := hInstance;
    wndcls.lpfnWndProc    := @ProcWindow;
    wndcls.lpszClassName:= 'DrTcpCallbackWnd';
    wndcls.lpszMenuName := nil;
    wndcls.style        := CS_HREDRAW or CS_VREDRAW;
  Windows.RegisterClass(wndcls);

  FhWnd := CreateWindowEx(
        WS_EX_CLIENTEDGE,
        wndcls.lpszClassName,
        wndcls.lpszClassName,
        WS_OVERLAPPEDWINDOW,
        100, 100,
        400, 300,
        0,
        0, //g_hMenu,
        hInstance,
        0);
  if (FhWnd = 0) then
    FdwErrNo := GetLastError;

//    ShowWindow(hWnd1, SW_SHOWNORMAL);
    UpdateWindow(FhWnd);
end;

function TcallbackWnd.PackSocket(_pc :PChar; _iLen :integer) :integer;
var sockAddr :TSockAddr;
    iNameLen :Integer;
    dwErrNo :DWORD;
    strMac, strIP :string;
    iLenIp, iLenMac, iLenMsg :Integer;
    pc :array[0..255] of Char;
    iPktLen, iPktIdx, iPktType :Integer;
begin
  Result := 0;
  iNameLen := SizeOf(sockAddr);
  if SOCKET_ERROR = getsockname(g_tcpClient.Fskt, sockAddr, iNameLen) then
  begin
    dwErrNo := GetLastError;
    Result := dwErrNo;
    Exit;
  end;

  strIP := inet_ntoa(sockAddr.sin_addr);
  strMac := '';
  GetMacByIP(sockAddr.sin_addr.S_addr, strMac);
  iLenIp := 4 + length(strIP);
  iLenMac:= 4 + length(strMac);
  iLenMsg := 4 + iLenIp + iLenMac;


  iPktLen := TCP_PACKET_HEADER_LEN + iLenMsg + _iLen;
  iPktIdx := 0;
  iPktType:= OP_TYPE_MANAGE_SOCKET_RES;
  CopyMemory(@pc[0], @iPktLen, 4);
  CopyMemory(@pc[4], @iPktIdx, 4);
  CopyMemory(@pc[8], @iPktType, 4);
  // ***
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN], @iLenMsg, 4);
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN + 4], @iLenIp, 4);
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN + 8], PChar(strIP), iLenIp - 4);
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN + 4 + iLenIp], @iLenMac, 4);
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN + 4 + iLenIp + 4], PChar(strMac), iLenMac - 4);
  // ***
  CopyMemory(@pc[TCP_PACKET_HEADER_LEN+iLenMsg], _pc, _iLen);

  TdrTcpClient.SendBytes(@pc[0], iPktLen);
end;

procedure TcallbackWnd.PackBlock(_pc :PChar; _iLen :integer);
var iPktLen, iPktIdx, iPktType :Integer;
    iIdx :Integer;
    buffer :TdrBuffer;
    mem :TMemoryStream;
    iLenMsg :Integer;
    pc :PChar;
    dwTick :DWORD;
begin
  mem := TMemoryStream.Create;
  try
    iPktLen := TCP_PACKET_HEADER_LEN;
    iPktIdx := 0;
    iPktType:= OP_TYPE_MANAGE_BLOCK_RES;
    mem.Write(iPktLen, 4);
    mem.Write(iPktIdx, 4);
    mem.Write(iPktType, 4);
    
    iLenMsg := 8;
    mem.Write(iLenMsg, 4);
    dwTick := GetTickCount;
    mem.Write(dwTick, 4);
    
    iIdx := 0;
    while iIdx < g_bufferPool.ListAll.Count do
    begin
      buffer := TdrBuffer(g_bufferPool.ListAll.Items[iIdx]);

      mem.Write(buffer.FiTotalLen, 4);
      mem.Write(buffer.FiValidBeginOffset, 4);
      mem.Write(buffer.FiValidLen, 4);
      mem.Write(buffer.FdwType, 4);
      mem.Write(buffer.FdwLastTick, 4);

      Inc(iLenMsg, 20);
      Inc(iIdx);
    end;

    iPktLen := TCP_PACKET_HEADER_LEN + iLenMsg + _iLen;
    iPktIdx := 0;
    iPktType:= OP_TYPE_MANAGE_BLOCK_RES;

    pc := PChar(mem.Memory);
    CopyMemory(@pc[0], @iPktLen, 4);
    CopyMemory(@pc[4], @iPktIdx, 4);
    CopyMemory(@pc[8], @iPktType, 4);
    CopyMemory(@pc[TCP_PACKET_HEADER_LEN], @iLenMsg, 4);    
    mem.Write(_pc^, _iLen);

    TdrTcpClient.SendBytes(mem.Memory, iPktLen);
  finally
    mem.Free;
  end;
end;

{ TdrDataSet }

function TdrDataSet.Unpack(_pc :PChar; _iLen :Integer) :integer;
var iTotalLen, iColumnCnt, iRowCnt :Integer;
    i, iOffset, iLenColumnName, iRowIdx, iColIdx :Integer;
    iLenRecord :Integer;
begin
  Result := 0;
  FpcBuf := _pc;
  FiBufLen := _iLen;
  
  CopyMemory(@iTotalLen,  @_pc[0], 4);
  if (iTotalLen <> _iLen) then
  begin
    Result := -1;
    Exit;
  end;
  CopyMemory(@iColumnCnt, @_pc[4], 4);
  CopyMemory(@iRowCnt,    @_pc[8], 4);

  SetLength(FaryColumnType, iColumnCnt);
  SetLength(FaryColumnName, iColumnCnt);
  SetLength(FaryaryRecord, iRowCnt, iColumnCnt);


  // 列类型
  for i:=0 to (iColumnCnt-1) do
    CopyMemory(@FaryColumnType[i], @_pc[12 + i * 4], 4);

  iOffset := 12 + iColumnCnt * 4;
  // 列名称
  for i:=0 to (iColumnCnt-1) do
  begin
    CopyMemory(@iLenColumnName, @_pc[iOffset], 4);
    SetLength(FaryColumnName[i], iLenColumnName);
    CopyMemory(@((FaryColumnName[i])[1]), @_pc[iOffset + 4], iLenColumnName);

    Inc(iOffset, 4 + iLenColumnName);
  end;

  
// [2]、字段值(长度 + 内容)
  for iRowIdx:=0 to iRowCnt-1 do
    for iColIdx:=0 to iColumnCnt-1 do
    begin
      CopyMemory(@iLenRecord, @_pc[iOffset], 4);
      FaryaryRecord[iRowIdx, iColIdx] := iOffset + 4;

      Inc(iOffset, 4 + iLenRecord);
    end;
end;

function TdrDataSet.GetRecord(_iRowIdx, _iColumnIdx: integer): TdrRecord;
begin
  if not Assigned(Ffield) then
    Ffield := TdrRecord.Create(Self, _iRowIdx, _iColumnIdx)
  else
  begin
    Ffield.FiRowIdx := _iRowIdx;
    Ffield.FiColumnIdx := _iColumnIdx;
  end;
  Ffield.FenXi;

  Result := Ffield;
end;

function TdrDataSet.GetFieldType(_iColumnIdx: integer): Integer;
begin
  Result := FaryColumnType[_iColumnIdx];
end;

function TdrDataSet.GetFieldTypeName(_iColumnIdx: integer): string;
begin
  FieldTypeName_f(GetFieldType(_iColumnIdx), Result);
end;

function TdrDataSet.GetFieldName(_iColumnIdx: integer): string;
begin
  Result := FaryColumnName[_iColumnIdx];
end;

function TdrDataSet.GetRowCount: Integer;
begin
  Result := Length(FaryaryRecord);
end;

function TdrDataSet.GetColumnCount: Integer;
begin
  Result := Length(FaryColumnType);
end;

{ TdrRecord }

constructor TdrRecord.Create(_dataset: TdrDataSet; _iRowIdx, _iColumnIdx: Integer);
begin
  Fdataset := _dataset;
  FiRowIdx := _iRowIdx;
  FiColumnIdx := _iColumnIdx;
end;

procedure TdrRecord.FenXi;
var iOffset :Integer;
begin
  iOffset := Fdataset.FaryaryRecord[FiRowIdx, FiColumnIdx];
  FpcRecord := @Fdataset.FpcBuf[iOffset];
end;

function TdrRecord.asByteArray(_pByte: PByte; out _iLen: Integer): Integer;
begin
  CopyMemory(@_iLen, FpcRecord-4, 4);
  if (_pByte <> nil) then
    CopyMemory(_pByte, FpcRecord, _iLen);
end;

function TdrRecord.asInteger: Integer;
begin
  CopyMemory(@Result, FpcRecord, 4);
end;

function TdrRecord.asSingle: Single;
begin
  CopyMemory(@Result, FpcRecord, SizeOf(Single));
end;

function TdrRecord.asDouble: Double;
begin
  CopyMemory(@Result, FpcRecord, SizeOf(Double));
end;

function TdrRecord.asString: string;
var iLen :Integer;
    iFieldType :Integer;
    d :Double;
    iUnicodeLen, iAnsiLen :Integer;
    pwc :PWideChar;
    pc :PChar;
begin
  iFieldType := GetFieldType;
  case iFieldType of
    DR_NUMERIC :
    begin
      CopyMemory(@iLen, FpcRecord - 4, 4);
      CopyMemory(@d, FpcRecord, iLen);
      Result := FloatToStr(d);
    end;
    DR_LONGNVARCHAR, DR_NCHAR, DR_NVARCHAR, DR_LONGVARCHAR, DR_CHAR, DR_VARCHAR :
    begin
      pwc := nil;
      pc := nil;
      try
        CopyMemory(@iLen, FpcRecord - 4, 4);
        //PostMessage(g_callbackWnd.FhWnd, WM_TCP_RECV, 5, iLen);
        if (iLen = 0) then
        begin
          Result := 'Empty string .';
          Exit;
        end;

        SetLength(Result, iLen);
        CopyMemory(@Result[1], FpcRecord, iLen);
      {
        iUnicodeLen := MultiByteToWideChar(CP_UTF8, 0, FpcRecord, iLen, nil, 0);
        GetMem(pwc, iUnicodeLen * 2);
        MultiByteToWideChar(CP_UTF8, 0, FpcRecord, iLen, pwc, iUnicodeLen);

        iAnsiLen := WideCharToMultiByte(CP_ACP, 0, pwc, iUnicodeLen, nil, 0, nil, nil);
        GetMem(pc, iAnsiLen + 1);
        WideCharToMultiByte(CP_ACP, 0, pwc, iUnicodeLen, pc, iAnsiLen, nil, nil);
        pc[iAnsiLen] := Char(0);
        Result := pc;     }
      finally
        if (pwc <> nil) then FreeMem(pwc, iUnicodeLen * 2);
        if (pc <> nil) then  FreeMem(pc, iAnsiLen + 1);
      end;
    end;
  else     
    FieldTypeName_f(iFieldType, Result);
  end;
end;

function TdrRecord.GetFieldType: Integer;
begin
  Result := Fdataset.GetFieldType(FiColumnIdx);
end;

function TdrRecord.GetFieldTypeName: string;
begin
  Result := Fdataset.GetFieldTypeName(FiColumnIdx);
end;

function TdrRecord.GetFieldName: string;
begin
  Result := Fdataset.GetFieldName(FiColumnIdx);
end;

initialization
  g_bufferPool := TdrBufferPool.create;
  g_drRecvBuffer := TdrRecvBuffer.Create;
  g_callbackWnd := TcallbackWnd.Create;
  g_strDestIp := '';
  g_iDestPort := 0;

finalization
  if Assigned(g_bufferPool) then g_bufferPool.Free;
  if Assigned(g_drRecvBuffer) then g_drRecvBuffer.Free;

end.

 

3、

 

转载于:https://www.cnblogs.com/CodeHouseZ/p/6144149.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值