JuliaWeb / WebSockets.jl

A WebSockets library for Julia
MIT License
157 stars 58 forks source link

create_connection, send and recv instead of simply open / avoid do statements #185

Open femtotrader opened 1 year ago

femtotrader commented 1 year ago

Hello,

Sorry I'm new to websockets. I want to convert the following Python code to Julia

from websocket import create_connection
import orjson

ws = create_connection("wss://ws.kraken.com/")
message = {"event":"subscribe", "subscription":{"name":"trade"}, "pair":["XBT/USDT","XBT/USD"]}

ws.send(orjson.dumps(message))

while True:
    message = ws.recv()
    message = orjson.loads(message)
    print(message)
    if isinstance(message, list):
        print(message[2], message[3])

I did using doc

using WebSockets
import JSON

const url = "wss://ws.kraken.com/"

WebSockets.open(url) do ws_client  
    # msg = "{\"event\":\"subscribe\", \"subscription\":{\"name\":\"trade\"}, \"pair\":[\"XBT/USD\",\"XRP/USD\"]}"

    msg = Dict(
        "event" => "subscribe", 
        "subscription" => Dict("name" => "trade"),
        "pair" => ["XBT/USDT", "XBT/USD"]
    )
    msg = JSON.json(msg)

    writeguarded(ws_client, msg)

    while (true)
        data, success = readguarded(ws_client)
        if success
            println(stderr, ws_client, " received:", String(data))
        end

    end
end;

but I don't like Julia do statements.

Moreover I would prefer to have separate create_connection, send and recv call like in the Python example but in the Julia example open takes handle function as parameter which is a mix of all this.

How can I achieve that? (ie having a Julia code nearer than the Python code)

Kind regards

hustf commented 1 year ago

Hello, femtotrader. I think I understand your dislike of do-functions. You find remnants of my own reluctance in this repository. They may mature on you, as they did for me, but in the meantime, I suggest you rewrite:

function handle_open_connection(ws_client)  
    msg = Dict(
        "event" => "subscribe", 
        "subscription" => Dict("name" => "trade"),
        "pair" => ["XBT/USDT", "XBT/USD"]
    )
    msg = JSON.json(msg)
    writeguarded(ws_client, msg)
    while (true)
        data, success = readguarded(ws_client)
        if success
            println(stderr, ws_client, " received:", String(data))
        end
    end
end

const url = "wss://ws.kraken.com/"

WebSockets.open(handle_open_connection, url)

What you ask for is possible, although it breaks with a couple of practices that are common in Julia although they also increase your chance to 'monitor as things happen'. You are going to make ws a global, and you'll have to remember to close(ws) yourself. I don't think either is a sin. I recognize your user name - you would know the pitfalls and workarounds.

You can basically dissect the function WebSockets.open and do everything in the REPL line by line, more closely resembling Python. I suggest the REPL because we added some colouring that makes monitoring the state of the connection a bit friendlier.

Your thread of execution can of course easily get stuck in waiting forever for the other side (while the other side may be waiting for you). If you don't want that to happen, execute functions with tsk = @asynchandle_open_connection(ws).

I believe using this package as you do is ok for easing the transition. If you plan on doing something larger or permanent, though, I actually recommend you have a look at HTTP.jl. That one is the long-term solution for websockets in Julia, although we maintain this in order to keep other useful, old packages alive. I'm pinging @fonsp here, as this issue relates to a discussion we had recently.

femtotrader commented 1 year ago

Thanks @hustf for your very detailed answer.

I was expecting something like this would be possible:

const url = "wss://ws.kraken.com/"

ws_client = WebSockets.open(url)
msg = Dict(
    "event" => "subscribe", 
    "subscription" => Dict("name" => "trade"),
    "pair" => ["XBT/USDT", "XBT/USD"]
)
msg = JSON.json(msg)
writeguarded(ws_client, msg)
while (true)
    data, success = readguarded(ws_client)
    if success
        println(stderr, ws_client, " received:", String(data))
    end
end

close(ws_client)

because with such a rewrite it would be easier to reuse WebSockets.jl in a quite complex project.

I will try using REPL and WebSockets.open code https://github.com/JuliaWeb/WebSockets.jl/blob/811d94bcd23a6a5249f5b74b9989021fbe034629/src/HTTP.jl#L68

hustf commented 1 year ago

Good luck!