在Delphi中连接了WebSocket,但没有数据

huangapple go评论69阅读模式
英文:

Connected Websocket in Delphi, but no data

问题

以下是您要翻译的代码部分:

unit unitDemoTestWS;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  webSocketClientUnit, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    btn1: TButton;
    mmo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  private
    procedure lSWC1DataEvent(Sender: TObject; const Text: string);
    procedure onmyconnected(Sender: Tobject);
    { Private declarations }
  public
    { Public declarations }
    lSWC: TIdSimpleWebSocketClient;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.lSWC1DataEvent(Sender: TObject; const Text: string);
begin
  mmo1.Lines.Add(text);
end;

procedure TForm1.onmyconnected(Sender: Tobject);
begin
  mmo1.Lines.Add('We are connected');
end;

procedure TForm1.btn1Click(Sender: TObject);
begin
  try
    lSWC.Host := '192.168.0.134';
    lswc.Port := 55902;
    lSWC.Connect;
  except
    mmo1.Lines.Add('Refused Connect!');
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  try
    lSWC := TIdSimpleWebSocketClient.Create(self);
    lSWC.onDataEvent := lSWC1DataEvent;
    lswc.OnConnected := onmyconnected;
    mmo1.Clear;
    lSWC.AutoCreateHandler := False;
  finally
  end;
end;

end.

请注意,我已经移除了代码中的HTML转义字符 ',以使代码更易于阅读。如果您需要将这些字符还原,请添加它们回到代码中。如果您有任何其他问题或需要进一步的帮助,请随时提出。

英文:

I am making first steps with websockets in a Delphi program, connected to an industrial device.

The device can be connected to using ws:// protocol, and will every second return a 500+/- byte JSON string. This is what I would like to listen to and display in a GUI. I will not have any HTML / JS frontend for this.

I have used the library IdWebsocketSimpleClient, with Delphi 10.4 Community Edition.

I have the following code. It will connect just fine, I get the message about connection. But there is never any data returned. I would have thought that DataEvent would trigger if any is received.

So what am I missing in order to receive something ? Making feet wet here with D10 for the first time.

I do have F.Piettes / OverByte components installed as well.

unit unitDemoTestWS;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
  webSocketClientUnit, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    btn1: TButton;
    mmo1: TMemo;
    procedure FormCreate(Sender: TObject);
    procedure btn1Click(Sender: TObject);
  private
    procedure lSWC1DataEvent(Sender: TObject; const Text: string);
    procedure onmyconnected(Sender: Tobject);
    { Private declarations }
  public
    { Public declarations }
    lSWC:TIdSimpleWebSocketClient;
  end;

var
  Form1: TForm1;


implementation

{$R *.dfm}

procedure Tform1.lSWC1DataEvent(Sender: TObject; const Text: string);
begin
  mmo1.Lines.Add(text);
end;

procedure TForm1.onmyconnected(Sender: Tobject);
begin
   mmo1.lines.add ('We are connected');
end;

procedure TForm1.btn1Click(Sender: TObject);
begin
    try
      lSWC.Host := '192.168.0.134';
      lswc.Port := 55902;
      lSWC.Connect;
    except
      mmo1.lines.add('Refused Connect!');
    end;
end;

procedure TForm1.FormCreate(Sender: TObject);
  begin
      try
        lSWC := TIdSimpleWebSocketClient.Create(self);
        lSWC.onDataEvent := lSWC1DataEvent;
        lswc.OnConnected := onmyconnected;
        mmo1.Clear;
        lSWC.AutoCreateHandler := False;

      finally
      end;
  end;
end.

The following Python code works and continously prints the received data - but I would like to display parts of the data using Delphi controls.

from websocket import create_connection
ws = create_connection("ws://192.168.0.134:55902")

while (True):     
  r =  ws.recv()
  print (r)
  
ws.close()


答案1

得分: 2

你没有正确地将TIdSimpleWebSocketClient连接到服务器。

TIdSimpleWebSocketClient继承自TIdTCPClient,它有一个公共的无参数Connect()方法。你直接调用了TIdTCPClient.Connect()方法,这只会打开TCP连接,不会执行其他操作。这就是你没有接收到任何数据的原因。

TIdSimpleWebSocketClient有自己的Connect()方法(声明为overload,但实际上应该是reintroduce,参见此链接),该方法接受WebSocket URL作为输入参数:

procedure Connect(pURL:String);overload;

这是你需要调用的方法,例如:

{lSWC.Host := '192.168.0.134';
lswc.Port := 55902;
lSWC.Connect;}
lSWC.Connect('ws://192.168.0.134:55902');

在内部,TIdSimpleWebSocketClient.Connect()解析URL以设置HostPort属性,然后连接到服务器,但它还执行了其他几项操作:

  • 设置IOHandler以启用TLS,如果访问的是安全的WSS URL且AutoCreateHandler为true。
  • 执行WebSocket握手。
  • 启动一个工作线程来处理传入数据。
  • 启动一个工作线程来处理WebSocket的心跳。

TIdSimpleWebSocketClient.Connect()的使用甚至在IdWebsocketSimpleClient.pas的顶部有一个代码示例:

{
示例代码:
//var lSWC:TIdSimpleWebSocketClient;
...
begin
  lSWC := TIdSimpleWebSocketClient.Create(self);
  lSWC.onDataEvent := self.lSWC1DataEvent;  //TSWSCDataEvent
  lSWC.AutoCreateHandler := false; //在大多数带有SSL的Websockets中,你可以将其设置为true
  if not lSWC.AutoCreateHandler then
  begin
    if lSWC.IOHandler=nil then
      lSWC.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(lSWC);
    (lSWC.IOHandler as TIdSSLIOHandlerSocketOpenSSL).SSLOptions.Mode := TIdSSLMode.sslmClient;
    (lSWC.IOHandler as TIdSSLIOHandlerSocketOpenSSL).SSLOptions.SSLVersions := [TIdSSLVersion.sslvTLSv1, TIdSSLVersion.sslvTLSv1_1, TIdSSLVersion.sslvTLSv1_2];
  end;
  lSWC.Connect('wss://echo.websocket.org');
  lSWC.writeText('!!It worked!!');
end;
}

话虽如此,你的代码还存在另一个问题。TIdSimpleWebSocketClient.onDataEvent事件是在工作线程的上下文中触发的,因此在更新Memo时,你的处理程序必须与主UI线程同步,例如:

procedure Tform1.lSWC1DataEvent(Sender: TObject; const Text: string);
begin
  TThread.Synchronize(nil, // 或者使用 TThread.Queue()
    procedure
    begin
      mmo1.Lines.Add(text);
    end
  );
end;
英文:

You are not connecting the TIdSimpleWebSocketClient to the server correctly.

TIdSimpleWebSocketClient derives from TIdTCPClient, which has a public parameter-less Connect() method. You are calling the TIdTCPClient.Connect() method directly, which will simply open the TCP connection and do nothing else. That is why you are not receiving any data.

TIdSimpleWebSocketClient has its own Connect() method (declared as overload but it should have been reintroduce instead), which takes a WebSocket URL as an input parameter:

procedure Connect(pURL:String);overload;

That is the method you need to call instead, eg:

{lSWC.Host := '192.168.0.134';
lswc.Port := 55902;
lSWC.Connect;}
lSWC.Connect('ws://192.168.0.134:55902');

Internally, TIdSimpleWebSocketClient.Connect() parses the URL to setup the Host and Port properties, and then connects to the server, but it also does several other things, too:

  • setup the IOHandler to enable TLS if accessing a secure WSS url and AutoCreateHandler is true.
  • performs the WebSocket handshake.
  • starts a worker thread to process incoming data.
  • starts a worker thread to handle WebSocket heartbeats.

This use of TIdSimpleWebSocketClient.Connect() is even demonstrated in a code example located at the very top of IdWebsocketSimpleClient.pas:

{
Sample code:
//var lSWC:TIdSimpleWebSocketClient;
...
begin
  lSWC := TIdSimpleWebSocketClient.Create(self);
  lSWC.onDataEvent           := self.lSWC1DataEvent;  //TSWSCDataEvent
  lSWC.AutoCreateHandler := false; //you can set this as true in the majority of Websockets with ssl
  if not lSWC.AutoCreateHandler then
  begin
    if lSWC.IOHandler=nil then
      lSWC.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(lSWC);
    (lSWC.IOHandler as TIdSSLIOHandlerSocketOpenSSL).SSLOptions.Mode := TIdSSLMode.sslmClient;
    (lSWC.IOHandler as TIdSSLIOHandlerSocketOpenSSL).SSLOptions.SSLVersions := [TIdSSLVersion.sslvTLSv1, TIdSSLVersion.sslvTLSv1_1, TIdSSLVersion.sslvTLSv1_2];
  end;
  lSWC.Connect('wss://echo.websocket.org');
  lSWC.writeText('!!It worked!!');
end;
}

That being said, there is another issue with your code. The TIdSimpleWebSocketClient.onDataEvent event is triggered in the context of a worker thread, so your handler must synchronize with the main UI thread when updating your Memo, eg:

procedure Tform1.lSWC1DataEvent(Sender: TObject; const Text: string);
begin
  TThread.Synchronize(nil, // or TThread.Queue()
    procedure
	begin
      mmo1.Lines.Add(text);
	end
  );
end;

huangapple
  • 本文由 发表于 2023年3月3日 21:07:09
  • 转载请务必保留本文链接:https://go.coder-hub.com/75627455.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定