Indy icon indicating copy to clipboard operation
Indy copied to clipboard

Add a WebSocket client component

Open rlebeau opened this issue 7 years ago • 49 comments

See RFC 6455 (http://tools.ietf.org/html/rfc6455).

rlebeau avatar Mar 06 '18 20:03 rlebeau

Hi, so I`m building a WebSocket client inherited from TIdTCPClient, how can I contribute to Indy project? I named it TIdSimpleWebSocketClient (I'm accepting suggestions to it's name) main methods:

Type
  TSWSCDataEvent = procedure(Sender: TObject; const Text: string) of object;
...
Type TIdSimpleWebSocketClient = class(TIdTCPClient)
...
    onDataEvent:TSWSCDataEvent;

    property HeartBeatInterval: Cardinal read FHeartBeatInterval write FHeartBeatInterval;
    property AutoCreateHandler: Boolean read FAutoCreateHandler write FAutoCreateHandler;
    procedure writeText(pMsg:String);
    procedure sendText(pMsg:String);
    function ConnectToAndUpgrade(pURL:String):Boolean;overload;
...

It already works fine for ws and wss servers, and I'm using in an production environment

Thanks

arvanus avatar Mar 07 '18 18:03 arvanus

What's the difference between writeText() and sendText()? What about sending binary data? Why doesn't onDataEvent differentiate between text and binary data? Why ConnectToAndUpgrade() instead of just overriding Connect()? In any case, to contribute, you can send the code to me privately, or submit a pull request on GitHub. But from the looks of it, your implementation might be a little too simple and will likely need tweaking before it can be committed.

rlebeau avatar Mar 07 '18 23:03 rlebeau

Basically writeText() and sendText() do the same thing, its because I was working with Blackbox trial, and wanted to keep compatible with. I`ll change somethings, and send you an copy.

arvanus avatar Mar 08 '18 00:03 arvanus

Hi can you please give a look? https://github.com/arvanus/Indy/blob/WebSocketImpl/Lib/Core/IdWebSocketSimpleClient.pas Any suggestions are welcome so that I can pull-request

arvanus avatar Jun 07 '18 21:06 arvanus

Sorry, I just haven't had any free time to look at it yet, but it is on my todo list to get to it. You didn't have to retract your pull request.

rlebeau avatar Jun 08 '18 19:06 rlebeau

Ok, I just need one suggestion from you, to keep it consistent with Indy patterns. Websocket protocol accepts both binary and text messages, How do you suggests me to implement the methods? Implement 2 methods "write" with different parameters (overloaded), one receiving strings directly, and the other receiving TIdBytes array (or stream array)? Or declare "writeString" and "writeBinary" (or something like this)? Because both methods has different headers inside websocket frame.

Thanks!

arvanus avatar Jun 11 '18 17:06 arvanus

The only difference between a "text" frame and a "binary" frame is the opcode used and the format of the payload, the rest of the frame data is the same between them.

It is really up to you to decide what you want to name the methods. You could use Write() overloads, like TIdIOHandler does. Or you could use separate names like SendString() and SendBuffer(), like TIdUDPBase does. I would probably stay away from using "binary" though, as nothing in Indy uses that term.

rlebeau avatar Jun 27 '18 22:06 rlebeau

Easy WebSocket support with existing TidHTTPServer component :)

TWebSocket turn a idHTTPServer GET request into a WebSocket dialog.

https://github.com/tothpaul/Delphi/tree/master/idWebSocket

tothpaul avatar Sep 08 '18 21:09 tothpaul

Hello,

Any update of IdWebSocketSimpleClient ? And and process of the websocket client of Indy?

Thanks

wqmeng avatar Nov 14 '19 09:11 wqmeng

@arvanus Hello, When I use IdWebSocketSimpleClient, and connect to a ws:// URI, sometimes I get a warning message of "duplicates not allowed". Do you know what's the matter?

Thank you.

wqmeng avatar Nov 14 '19 09:11 wqmeng

Hi @wqmeng, can you give me some details of when this error occurs? Exactly at connection, is it right?

arvanus avatar Nov 14 '19 12:11 arvanus

@arvanus Mostly, it will happen at the first time the websocket connect to the URI. Then I close, and do connect again, it will not happen again. But if I exit the app or Kill it from the Android system, again reopen the App to connect the websocket server again, It will mostly show the "Duplicates not allowed" again.

Sometimes, I can see the exception can be out from the OnError event.

Thanks

wqmeng avatar Nov 14 '19 17:11 wqmeng

Question: Are you using Apache as a reserve proxy? I've got some problems because Apache reused the socket from the last connection disconnected by another client. If so you'll need to disallow socket reuse, read here: https://github.com/ratchetphp/Ratchet/issues/645 and here: https://httpd.apache.org/docs/2.4/mod/mod_proxy.html

arvanus avatar Nov 14 '19 17:11 arvanus

@arvanus Hello, no, I use https://github.com/swoole/swoole-src as a PHP websocket server.

Not sure if it reuse the socket. I use IdWebSocketSimpleClient on a Android phone.

wqmeng avatar Nov 14 '19 17:11 wqmeng

So are you connecting directly? Swoole supports SSL connection directly ?

arvanus avatar Nov 14 '19 18:11 arvanus

Yes, I connect directly.

And I do not use SSL in my case. Just ws://

wqmeng avatar Nov 14 '19 18:11 wqmeng

Any update of IdWebSocketSimpleClient ? And and process of the websocket client of Indy?

No, not yet, sorry.

rlebeau avatar Nov 16 '19 18:11 rlebeau

Hi, I need to connect to a WebSockets server running in a Lazarus FPC application. The server is running locally in a single machine, I'm currently using it from a localhost web application that runs on any browser and works fine.

I want to run a WebSockets client for that server, in a Delphi Android application. I will try the idWebSocketSimpleClient today and then I will report if it works.

lainz avatar Jan 27 '20 15:01 lainz

Guten Tag,

vielen Dank für Ihre E-Mail.

Ich bin in den Skiferien von Montag, 27. Januar bis Sonntag, 2. Februar 2020. Die eingehenden E-Mails werden während dieser Zeit nicht bearbeitet.

Wenn es kein persönliches Anliegen ist, wenden Sie sich doch bitte an [email protected] oder an [email protected]

Herzlichen Dank für Ihr Verständnis.

Schöne Grüsse Elias Zurschmiede

delight software gmbh

ezurschmiede avatar Jan 27 '20 15:01 ezurschmiede

I can connect to the websocket, I'm receiving the connection changes with onConnectionDataEvent, but onDataEvent is not receiving nothing, also onError displays nothing...

lainz avatar Jan 27 '20 21:01 lainz

@lainz are you sure the server are sending you info? Try testing with https://websocket.org/echo.html demo service

arvanus avatar Mar 10 '20 12:03 arvanus

I'm sure, since I'm using it with a Lazarus application that's used every day, so the web application is sending the data...

lainz avatar Mar 11 '20 22:03 lainz

I get a connection, but after some time of no action (perhaps the heartbeat interval), the client doesn't receive the next message. If i send again a message, this works. (But the missing message didn't appear) Any suggestions?

Gregory667 avatar Feb 25 '21 20:02 Gregory667

I have not reviewed this code for errors, but that sounds like an issue with it not reading WebSocket frames correctly, leaving bytes in the InputBuffer until a subsequent message forces the reading code to finish what it started earlier. But I can't attest to that until I actually look at the code.

rlebeau avatar Feb 25 '21 21:02 rlebeau

As Info: Another Test. If i send every second messages over a period of 1 minute, all arrive. If i now wait 20 seconds, an identical message does not arrive.

Ps.: If i deactivate the heartbeat, the problem also occurs.

My last Text was not correct. startHeartBeat starts a Thread which triggers the heartbeat-event...But the heartbeat itself, where can i found it?

Gregory667 avatar Feb 25 '21 21:02 Gregory667

Hi @Gregory667 You need to define the heartbeat event: https://github.com/arvanus/Indy/blob/4b2213573a15007a7a8cf767700a5ac5a6b5ee03/Lib/Core/IdWebSocketSimpleClient.pas#L537 If you hasn't defined the event then it's doing nothing Also do you have any exception in your server or client? Thanks

arvanus avatar Mar 01 '21 20:03 arvanus

Now it works. I defined the Event and send a Ping to the server. I got no exceptions. But i assigned the onError event and react especially to forceDisconnect.

At the end of the dataevent i placed a TIdSimpleWebSocketClient(sender).Socket.InputBuffer.Clear;

Gregory667 avatar Mar 02 '21 20:03 Gregory667

I am able to connect to my websocket using TIdSimpleWebSocketClient. Any example code for how to read the data?

nader938 avatar Mar 07 '21 17:03 nader938

@arvanus

Hello, I am use this in a wss:// case in Android with Lib from OpenSSL, it is very odd that the text send out to server will always lost the last character, Such as, "HellO", the server receive only "Hell" .

But on windows, there is no problem. Only in Android FMX app will lost the last character, I also test this with another web-socket client from another Githuber, both has the same problem.

@rlebeau A bug from Indy socket with the Android implement?

Thanks

wqmeng avatar Mar 07 '21 17:03 wqmeng

answering my own question above in case anyone ends up here. The class works perfectly to connect to a websocket built on indy (thanks @arvanus and @rlebeau ), all you need to get the data out is a procedure for example: procedure TForm1.OnWebSocketMessage(Sender: TObject; const Str: string); begin TThread.Synchronize(nil, procedure begin Memo1.Lines.Add(str); end); end; to be assigned to lSWC.onDataEvent := OnWebSocketMessage; I am sure there are better ways of doing this but this worked for me

nader938 avatar Mar 08 '21 01:03 nader938