簡體   English   中英

Indy TCP和Activex連接到服務器問題

[英]indy TCP and activex connect to server issues

我正在嘗試將delphi項目從VCL轉換為ActiveX。 我的客戶端線程有問題。 這是我的客戶端線程類型:

type
  TClientThread = class(TThread)
  private
    Command: string;
    procedure HandleInput;
  protected
    procedure Execute; override;
  end;

這是實現:

procedure TClientThread.HandleInput;
begin
  activext.ProcessCommands(Command);
  Command := '';
end;

procedure Tactivextest.ProcessCommands(Command: string);
var
  Params: array [1 .. 10] of String;
  ParamsCount, P: Integer;
  PackedParams: TPackedParams;
  PStr: String;
  IdBytes: TIdBytes;
  Ms: TMemoryStream;
  ReceiveParams, ReceiveStream: Boolean;
  Size: Int64;
begin
  Ms := TMemoryStream.Create;
  ReceiveParams := False;
  ReceiveStream := False;

  if Command[1] = '1' then // command with params
  begin
    Command := Copy(Command, 2, Length(Command));
    ReceiveParams := True;
  end
  else if Command[1] = '2' then // command + memorystream
  begin
    Command := Copy(Command, 2, Length(Command));
    ReceiveStream := True;
    Ms.Position := 0;
  end
  else if Command[1] = '3' then // command with params + memorystream
  begin
    Command := Copy(Command, 2, Length(Command));
    ReceiveParams := True;
    ReceiveStream := True;
  end;

  if ReceiveParams then // params incomming
  begin
    TCPClient.Socket.ReadBytes(IdBytes, SizeOf(PackedParams), False);
    BytesToRaw(IdBytes, PackedParams, SizeOf(PackedParams));
    ParamsCount := 0;
    repeat
      Inc(ParamsCount);
      P := Pos(Sep, String(PackedParams.Params));
      Params[ParamsCount] := Copy(String(PackedParams.Params), 1, P - 1);
      Delete(PackedParams.Params, 1, P + 4);
    until PackedParams.Params = '';
  end;
  if ReceiveStream then // stream incomming
  begin
    Size := TCPClient.Socket.ReadInt64;
    TCPClient.Socket.ReadStream(Ms, Size, False);
    Ms.Position := 0;
  end;

  if Command = 'SIMPLEMESSAGE' then
  begin
    MessageDlg(Params[1], mtInformation, [mbOk], 0);
  end;
  if Command = 'INVALIDPASSWORD' then
  begin
    TCPClient.Disconnect;
    MessageDlg('Invalid password!', mtError, [mbOk], 0);
  end;
  if Command = 'SENDYOURINFO' then // succesfully loged in
  begin
    UniqueID := StrToInt(Params[1]);
    Panel1.Caption := 'connect ' + namewithicon + ')';
    PStr := namewithicon + Sep;
    SendCommandWithParams(TCPClient, 'TAKEMYINFO', PStr);
  end;
  if Command = 'DISCONNECTED' then
  begin
    if TCPClient.Connected then
    TCPClient.Disconnect;
  end;
  if Command = 'TEXTMESSAGE' then
  begin
    memo1.Lines.Add(Params[1] + ' : ' + Params[2] )
  end;
end;

procedure TClientThread.Execute;
begin
  inherited;
  while not Terminated do
  begin
    if not activext.TCPClient.Connected then
      Terminate
    else
    begin
      if activext.TCPClient.Connected then
        Command := activext.TCPClient.Socket.ReadLn('', 5);
      if Command <> '' then
        Synchronize(HandleInput);
    end;
  end;
end;

initialization
  TActiveFormFactory.Create(
    ComServer,
    TActiveFormControl,
    Tactivextest,
    Class_activextest,
    0,
    '',
    OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL,
    tmApartment);
end.

這是我使用Indy的TCP OnConnected事件啟動客戶端線程的方式:

procedure Tactivextest.TCPClientConnected(Sender: TObject);
begin
  ClientThread := TClientThread.Create(True);
  ClientThread.Start;
  SendCommandWithParams(TCPClient, 'LOGIN', namewithicon + Sep);
end;

這是我在表單的OnCreate事件上連接到服務器的方式:

begin
  if not TCPClient.Connected then
  begin
    TCPClient.Host := 'localhost';
    TCPClient.Port := 31000;
    try
      TCPClient.Connect;
    except
      on E: Exception do
      begin
        MessageDlg('Cannot connect to server!', mtInformation, [mbOk], 0);
        Application.Terminate;
      end;
    end;
  end
  else
  begin
    SendCommand(TCPClient, 'DISCONNECTED');
    if TCPClient.Connected then
      TCPClient.Disconnect;
  end;
end;

發送命令

procedure Tactivextest.SendBuffer(TCPClient: TIdTCPClient; Buffer: TIdBytes;
  BufferSize: Cardinal);
begin
  if not TCPClient.Connected then
    Exit;
  TCPClient.Socket.WriteLn('AUDIO');
  TCPClient.Socket.Write(BufferSize);
  TCPClient.Socket.Write(Buffer, BufferSize);
end;

procedure Tactivextest.SendCommand(TCPClient: TIdTCPClient; Command: string);
begin
  if not TCPClient.Connected then
    Exit;
  TCPClient.Socket.WriteLn(Command);
end;

procedure Tactivextest.SendCommandWithParams(TCPClient: TIdTCPClient;
  Command, Params: String);
var
  PackedParams: TPackedParams;
begin
  if not TCPClient.Connected then
    Exit;
  TCPClient.Socket.WriteLn('1' + Command);
  PackedParams.Params := ShortString(Params);
  TCPClient.Socket.Write(RawToBytes(PackedParams, SizeOf(PackedParams)));
end;

procedure Tactivextest.SendStream(TCPClient: TIdTCPClient; Ms: TMemoryStream);
begin
  if not TCPClient.Connected then
    Exit;
  Ms.Position := 0;
  with TCPClient.Socket do
  begin
    Write(Ms.Size);
    WriteBufferOpen;
    Write(Ms, 0);
    WriteBufferClose;
  end;
end;

procedure Tactivextest.SendCommandAndStream(TCPClient: TIdTCPClient; Command: String;
  Ms: TMemoryStream);
begin
  if not TCPClient.Connected then
    Exit;
  TCPClient.Socket.WriteLn('2' + Command);
  Ms.Position := 0;
  with TCPClient.Socket do
  begin
    Write(Ms.Size);
    WriteBufferOpen;
    Write(Ms, 0);
    WriteBufferClose;
  end;
end;

procedure Tactivextest.SendCommandWithParamsAndStream(TCPClient: TIdTCPClient;
  Command, Params: String; Ms: TMemoryStream);
var
  PackedParams: TPackedParams;
begin
  if not TCPClient.Connected then
    Exit;
  SendCommand(TCPClient, '3' + Command);
  PackedParams.Params := ShortString(Params);
  TCPClient.Socket.Write(RawToBytes(PackedParams, SizeOf(PackedParams)));
  Ms.Position := 0;
  with TCPClient.Socket do
  begin
    Write(Ms.Size);
    WriteBufferOpen;
    Write(Ms, 0);
    WriteBufferClose;
  end;
end;

我能夠連接到服務器,但是客戶端線程無法與VCL相同地啟動,因此由於我無法使用ActiveX內的客戶端線程,因此我已斷開連接,因此無法調用SendCommands() 我已經搜尋了很多天有關如何解決的信息,但找不到解決該問題的方法。 我知道ActiveX已死,但這是出於教育目的。

如果Connect()成功,則不可能觸發TIdTCPClient.OnConnected ,因此必須創建客戶端線程。 如果Start()沒有引發異常,則線程將開始運行。

然而,你的線程代碼的一個主要問題是, HandleInput()被主線程的上下文中運行通過TThread.Synchronize()並不在DLL中工作(ActiveX或以其他方式),而主線程的額外合作托管EXE。 HandleInput()根本不應該同步,但是一旦解決, ProcessCommands()就會執行非線程安全的操作(使用MessageDlg() ,並直接訪問Panel1Memo1 ),這確實需要同步。

因此,您需要重新編寫線程邏輯以避免這些陷阱。 嘗試更多類似這樣的方法:

type
  TClientThread = class(TThread)
  protected
    procedure Execute; override;
  end;

procedure TClientThread.Execute;
begin
  activext.SendCommandWithParams(activext.TCPClient, 'LOGIN', activext.namewithicon + activext.Sep);

  while (not Terminated) and activext.TCPClient.Connected do
  begin
    Command := activext.TCPClient.Socket.ReadLn('', 5);
    if Command <> '' then
      activext.ProcessCommands(Command);
  end;
end;

type
  Tactivextest = class(TActiveForm)
    TCPClient: TIdTCPClient;
    ...
  private
    ...
    LineToAdd: string;
    procedure UpdatePanel;
    procedure AddLineToMemo;
    ...
  end;

procedure Tactivextest.FormCreate(Sender: TObject);
begin
  TCPClient.Host := 'localhost';
  TCPClient.Port := 31000;
  try
    TCPClient.Connect;
  except
    on E: Exception do
    begin
      MessageBox(0, 'Cannot connect to server!', 'Error', MB_OK);
      raise;
    end;
  end;
end;

// TTimer OnTimer event handler
procedure Tactivextest.Timer1Timer(Sender: TObject);
begin
  // needed for TThread.Synchronize() to work in a DLL...
  CheckSynchronize;
end;

procedure Tactivextest.TCPClientConnected(Sender: TObject);
begin
  ClientThread := TClientThread.Create(False);
end;

procedure Tactivextest.UpdatePanel;
begin
  Panel1.Caption := 'connect ' + namewithicon + ')';
end;

procedure Tactivextest.AddLineToMemo;
begin
  Memo1.Lines.Add(LineToAdd);
end;

procedure Tactivextest.ProcessCommands(Command: string);
var
  Params: array [1 .. 10] of String;
  ParamsCount, P: Integer;
  PackedParams: TPackedParams;
  IdBytes: TIdBytes;
  Ms: TMemoryStream;
  ReceiveParams, ReceiveStream: Boolean;
  Size: Int64;
begin
  ReceiveParams := False;
  ReceiveStream := False;

  Ms := TMemoryStream.Create;
  try            
    case Command[1] of
      '1': // command with params
      begin 
        Command := Copy(Command, 2, MaxInt);
        ReceiveParams := True;
      end;
      '2': // command + stream
      begin
        Command := Copy(Command, 2, MaxInt);
        ReceiveStream := True;
      end;
      '3': // command with params + stream
      begin
        Command := Copy(Command, 2, MaxInt);
        ReceiveParams := True;
        ReceiveStream := True;
      end;
    end;

    if ReceiveParams then // params incoming
    begin
      TCPClient.Socket.ReadBytes(IdBytes, SizeOf(PackedParams), False);
      BytesToRaw(IdBytes, PackedParams, SizeOf(PackedParams));
      ParamsCount := 0;
      repeat
        Inc(ParamsCount);
        P := Pos(Sep, String(PackedParams.Params));
        Params[ParamsCount] := Copy(String(PackedParams.Params), 1, P - 1);
        Delete(PackedParams.Params, 1, P + 4);
      until (PackedParams.Params = '') or (ParamsCount = 10);
    end;

    if ReceiveStream then // stream incoming
    begin
      Size := TCPClient.Socket.ReadInt64;
      if Size > 0 then
      begin
        TCPClient.Socket.ReadStream(Ms, Size, False);
        Ms.Position := 0;
      end;
    end;

    if Command = 'SIMPLEMESSAGE' then
    begin
      MessageBox(0, PChar(Params[1]), 'Message', MB_OK);
    end
    else if Command = 'INVALIDPASSWORD' then
    begin
      TCPClient.Disconnect;
      MessageBox(0, 'Invalid password!', 'Error', MB_OK);
    end
    else if Command = 'SENDYOURINFO' then // successfully logged in
    begin
      UniqueID := StrToInt(Params[1]);
      TThread.Synchronize(nil, UpdatePanel);
      SendCommandWithParams(TCPClient, 'TAKEMYINFO', namewithicon + Sep);
    end
    else if Command = 'DISCONNECTED' then
    begin
      TCPClient.Disconnect;
    end
    else if Command = 'TEXTMESSAGE' then
    begin
      LineToAdd := Params[1] + ' : ' + Params[2];
      TThread.Synchronize(nil, AddLineToMemo);
    end;
  finally
    Ms.Free;
  end;
end;

initialization
  TActiveFormFactory.Create(
    ComServer,
    TActiveFormControl,
    Tactivextest,
    Class_activextest,
    0,
    '',
    OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL,
    tmApartment);
end.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM