Delphi 10.4:Indy TIdTCPClient 从 websocket 和 webserver 读取数据

问题描述

我正在尝试读取每 4 毫秒从服务器发送的实时数据。我在 Delphi 10.4 和 Indy 10 中使用 TCPclinet 作为跨平台应用程序。客户端和服务器之间的所有通信都是基于文本和 html 的。服务器正在运行一个 web 服务器(端口 80)和一个 websocket(端口 81)。服务器显示一个页面,等待发送 1,然后开始通过套接字发送数据。这在 Twebbrowser1 中有效,我看到了数据。我还使用 chrome 扩展测试了我的 websocket,它似乎按预期发送数据 (https://chrome.google.com/webstore/detail/websocket-test-client/fgponpodhbmadfljofbimhhlengambbn?hl=en)。但是,我没有使用以下代码在客户端的套接字中获取任何数据,这些代码基于 Delphi: Indy TIdTCPClient Reading Data 处的链接和 Remy 的编辑以及 https://en.delphipraxis.net/topic/1010-using-indy-for-cross-platform-tcpip/ 处的更新。同样为了获得线程结构,我在这里使用了 Remy 的答案 Indy TIdTCPClient receive text。基于更多阅读清理代码,以免调用主 UI 威胁进行调试阅读响应。

对我哪里出错的任何帮助将不胜感激。

unit Mainunit;

interface

uses
  System.SysUtils,System.Types,System.UITypes,System.Classes,System.Variants,FMX.Types,FMX.Controls,FMX.Forms,FMX.Graphics,FMX.Dialogs,FMX.Memo.Types,FMX.StdCtrls,FMX.Controls.Presentation,FMX.ScrollBox,FMX.Memo,IdCustomTransparentProxy,IdSocks,IdBaseComponent,IdComponent,IdioHandler,IdioHandlerSocket,IdioHandlerStack,IdTCPConnection,IdTCPClient,IdSync,FMX.Webbrowser,Web.HTTPApp,IdHTTP;

type

  TDataEvent = procedure(const Data: string) of object;

  TReadingThread = class(TThread)
    private
      FClient : TIdTCPClient;
      Fdata: string;
      FOnData: TDataEvent;
      procedure DataReceived;
    protected
      procedure Execute; override;
      procedure DoTerminate; override;
    public
      constructor Create(AClient: TIdTCPClient); reintroduce;
      property OnData: TDataEvent read FOnData write FOnData;
    end;

  TLog = class(TIdSync)
  protected
    FMsg: String;
    procedure DoSynchronize; override;
  public
    constructor Create(const AMsg: String);
    class procedure AddMsg(const AMsg: String);
  end;

  TMainForm = class(TForm)
    Memo1: TMemo;
    Connect_btn: TButton;
    IdTCPClient1: TIdTCPClient;
    disconnect_btn: TButton;
    Webbrowser1: TWebbrowser;
    refresh_btn: TButton;

    procedure DataReceived(const Data: string);
    procedure CreateTCPIPConnection;
    procedure Connect_btnClick(Sender: TObject);
    procedure disconnect_btnClick(Sender: TObject);
    procedure refresh_btnClick(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);

  private
    { Private declarations }
  public
    { Public declarations }
    request_count : integer;

  end;

var
  MainForm: TMainForm;
  ZPReadThread: TReadingThread = nil;

implementation

{$R *.fmx}


procedure TMainForm.CreateTCPIPConnection;
begin
  if not Assigned(ZPReadThread) then
  begin
    IdTCPClient1.Host := '192.168.11.5';
    IdTCPClient1.Port := 81;
    try
      ZPReadThread := TReadingThread.Create(IdTCPClient1);
      try
        ZPReadThread.OnData := DataReceived;
        ZPReadThread.Start;
      except
        FreeAndNil(ZPReadThread);
        raise;
      end;
    except
      on E: Exception do
      begin
        TLog.AddMsg('DEBUG: TCP/IP exception creating read thread : '+E.Message);
       end;
    end;
  end;
end;



constructor TReadingThread.Create(AClient: TIdTCPClient);
begin
  TLog.AddMsg('DEBUG: TReadingThread.Create');
  inherited Create(True);
  FClient := AClient;
end;


procedure TReadingThread.Execute;
begin
  try
    FClient.ConnectTimeout := 10000; // <-- use whatever you want...   was 10000
    FClient.Connect;
    if FClient.Connected then
      TLog.AddMsg('DEBUG: Threat execute connected to the client: ' + FClient.Host
                  + '   on port: '        + inttostr(FClient.Port)
                  + '   RecvBufferSize: ' + inttostr(FClient.IOHandler.RecvBufferSize)
                  );
  except
    on E: Exception do
    begin
      TLog.AddMsg('DEBUG: TCP/IP connect exception : '+E.Message);
      raise;
    end;
  end;

  try
    FClient.IOHandler.ReadTimeout := 1000; // <-- use whatever you want...  was 5000
    while not Terminated do
    begin
      try
        FData := FClient.IOHandler.ReadLn();
      except
        on E: Exception do
        begin
            TLog.AddMsg('DEBUG: TCP/IP IOHandler.ReadLn exception : '+E.Message);
          raise;
        end;
      end;
       //  if (FData <> '') and Assigned(FOnData) then Synchronize(DataReceived);
        Synchronize(DataReceived);
        SetLength (FData,0);
    end;
  finally
    FClient.disconnect;
  end;
end;


procedure TReadingThread.DataReceived;
begin
  if Assigned(FOnData) then FOnData(FData);
   Mainform.request_count :=  Mainform.request_count + 1;
   TLog.AddMsg('Debug: ' +  inttostr(MainForm.request_count)+ ' ) Inside proc DataRecieved of thread fdata is: ' + FData);
end;


procedure TReadingThread.DoTerminate;
begin
  TLog.AddMsg('Debug: Read thread terminating');
  inherited;
end;

constructor TLog.Create(const AMsg: String);
begin
  inherited Create;
  FMsg := AMsg;
end;

procedure TLog.DoSynchronize;
begin
  Mainform.Memo1.Lines.Add(FMsg);
end;

class procedure TLog.AddMsg(const AMsg: String);
begin
  with Create(AMsg) do
  try
    Synchronize;
  finally
    Free;
  end;
end;



procedure TMainForm.DataReceived(const Data: string);
begin
  Memo1.Lines.Add('Recevied data DataRecieved of form : ' + Data);
end;

procedure TMainForm.Connect_btnClick(Sender: TObject);

begin
  try
    Webbrowser1.Navigate('http://192.168.11.5/');
    CreateTCPIPConnection();
    request_count := 0;

  except
    on E: Exception do
      MainForm.Memo1.Lines.Add('Threat connection Error: ' + E.Message);
    end;
  end;


procedure TMainForm.disconnect_btnClick(Sender: TObject);
begin
  if Assigned(ZPReadThread) then
  begin
    TLog.AddMsg('Debug: Terminating Read thread');
    ZPReadThread.Terminate;
    TLog.AddMsg('Debug:Waiting for read thread termination');
    ZPReadThread.WaitFor;
    TLog.AddMsg('Debug:Finished waiting for read thread termination');
    FreeAndNil(ZPReadThread);
  end
  else TLog.AddMsg('Debug:Thread not active');
end;



procedure TMainForm.FormClose(Sender: TObject; var Action: TCloseAction);
begin
   disconnect_btnClick(Sender);
end;

procedure TMainForm.refresh_btnClick(Sender: TObject);
begin
    Webbrowser1.Reload;
    memo1.Lines.clear;
    request_count := 0;
end;


end.

解决方法

在上面回答我自己的问题,以防对其他人有帮助。如果我错了,有人会纠正我,但无法使用基本的 TidTCPClient 从 websocket 读取数据。但是,从 TIdTCPClient (https://github.com/arvanus/Indy/blob/WebSocketImpl/Lib/Core/IdWebSocketSimpleClient.pas) 继承的 WebSocket 客户端效果很好。我能够连接到我的套接字,我需要添加的只是一个读取数据的过程。