简体   繁体   中英

websockets closing unexpectedly

Update 13. Apr: dart code works consistently

Background:

My TV (Samsung 2019 RU7000) offers a secure websocket connection with which json packets can be send to remote control it. For example

"method":"ms.remote.control", 
"params":{ "Cmd": "Click", "DataOfCmd":"KEY_MUTE" }

can be send to mute the TV.

To enable access, a handshake is done. The client connects to wss://ip:8002/api/v2/samsung.remote.control?name=value where the value of name is a base64 encoded string. After a successful connection, a popup window appears on the TV which needs to be accepted. If the user accepts the request, the TV sends a JSON response containing a token:

"data":{
    "clients":[ ... ],
    "id":"...",
    "token":"28852140"
},
"event":"ms.channel.connect"

This token is used for authenticating connections with the same name by attaching &token=value to the URL. Sending commands before the user accepts the popup is ignored.

What works

Command line approaches with wscat and curl are working. The TV shows a pop-up and sends a response:

$ wscat -n -c https://192.168.0.227:8002/api/v2/channels/samsung.remote.control?name=aW9Ccm9rZXI=
Connected (press CTRL+C to quit)
< {"data":{"clients":[...], "id":"...", "token":"57940060"}, "event":"ms.channel.connect"}

dart - dart.io.WebSocket

The following code triggers the popup and gets a response as well.

  WebSocket ws = await WebSocket.connect(url,
      compression: CompressionOptions.compressionOff);
  ws.pingInterval = Duration(seconds: 10000);

  ws.listen(print, onError: print, onDone: () {
    print("done");
    print(ws.closeCode);
    print(ws.closeReason);
  });

  await Future.delayed(Duration(seconds: 30));
  ws.close();

Problem

I want to use python to connect to the websocket, authenticate my session and send remote control commands. This does not work for the following implementations.

python - websocket liris

The popup does not appear and there is no response from the TV. The socket does not close.

from websocket import create_connection
from ssl import CERT_NONE 

sock = create_connection(url, sslopt={"cert_reqs": CERT_NONE})

print(sock.recv())

python - websockets aaugustin

The pop-up does not appear as well and it seems the connection is closed by the TV.

from websockets.client import connect, WebSocketClientProtocol
import ssl
import asyncio

async def connect():
    async with connect(url, ssl=ssl.CERT_NONE) as websocket:
        res = await websocket.recv()
        print(res)

asyncio.get_event_loop().run_until_complete(connect())

The wireshark log shows that it's sending an HTTP GET asking for a websocket upgrade. The TV responds by closing the connection with FIN and RST.


Observations

Connections to publicly available websocket servers do not share the same issues I am experiencing.

Many popular remote controls available for python have the two websocket libraries as the implemented solution. None of these were working for me.

Comparing the packets sent by the python implementations and the command line tools do not show a noticeable difference to me.

wscat sends an 80 byte long packet to the TV every four seconds. I suppose this is ping/pong.

How can I investigate further?

What about trying Tornado's websocket implementation?

async def samsung_ws():
    ws_req = HTTPRequest("wss://ip:8002/api/v2/samsung.remote.control?name=value", validate_cert=False)
    ws = await websocket_connect(ws_req)
    ws.write_message("hello")
    while True:
        msg = await ws.read_message()
        if not msg:
            break
        print(msg)

Depending on how the webserver is built, you might need to request the webpage before connecting to the websocket because in your wscat example I noticed you are using https:// instead of wss:// .

Can you post a sample of a dart run (with the url used)?

Um.... I have some experience on websockets connection using python websockets. In article, you indicate that your websockets connections will automatically disconnected from the server.

I think is the mechanism call "ping-pong" in the websockets module cause this problem.

The mechanism default status is true, that means you will send a ping sign to the websocket server periodically, if the server doesn't send pong back to you, module will consider that server have been shutdown.So you need to do is just set the "ping-pong"

status to False.

    async def connect():
    async with connect(url, ssl=ssl.CERT_NONE,close_timeout = None,ping_interval = None) as websocket:
        res = await websocket.recv()
        print(res)

asyncio.get_event_loop().run_until_complete(connect())

This is all my personal opinion, you can try this out.

Why not using a higher level Python module like requests ? You could try the following:

import requests

params = (
    ('name', 'aW9Ccm9rZXI='),
)

response = requests.get('https://192.168.0.227:8002/api/v2/channels/samsung.remote.control', params=params)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM