How to write a simple Python3 websockets client ?
Given https://github.com/Pithikos/python-websocket-server (a multi-client server implementation) which has been tested with the authors provided web client (ie. javascript), I like to write a simple python3 client using standard python websockets module. I expect the client to send a text message and get an answer back (sort of like an echo server). Nothing fancy on the client, no concurrency, just connect, send, recv and close (or exit).
#!/usr/bin/python3
import sys
import websockets
req = 'joe'
if sys.argv[1:]:
req = sys.argv[1]
with websockets.connect("ws://localhost:9001") as ws:
print("req: %s" % (req) )
ws.send(req)
res = ws.recv()
print("res: %s" % (res) )
And when I run it ...
medi#medi:~/proto/python/d4> ./client 'hello Joe'
Traceback (most recent call last):
File "./client", line 10, in <module>
with websockets.connect("ws://localhost:9001") as ws:
AttributeError: __enter__
I was expecting to be able to send() and recv(). I am having difficulty finding python based code (have seen lots of JS clients) sample.
This is an async websocket client that supports asyncio. You need to use it like this:
import asyncio
import websockets
async def main():
async with websockets.connect(...) as websocket:
...
asyncio.run(main())
Related
I'm connecting to the same websocket address but for some reason I can't receive data using websockets+asyncio.
However, it does work when using websocket as well as using a raw websocket connection with Postman.
if SOCKET is the address
Using websockets + asyncio
import asyncio
import websockets
SOCKET = 'wss://api...'
async def handle(uri):
async with websockets.connect(uri) as websocket:
while True:
print(await websocket.recv())
asyncio.get_event_loop().run_until_complete(handle(SOCKET))
Using websocket
from websocket import create_connection
ws = create_connection(SOCKET)
while True:
try:
result = ws.recv()
print(result)
Does anyone know what can possibly be wrong with the first option? I'm actually following a python sample request suggested by a site's documentation when using websockets+asyncio.
I wrote a code in python 3.9.0 which opens a text file, read line by line and host it at web socket for clients to consume. It works just fine. The problem is, when I consume the file from the web socket server it is really slow. I can see that server sends it faster in thousands but client is receiving it slow. I am not sure what am I doing wrong with my client code. Here is my client code
async def consumeStream():
while True:
async with websockets.connect('ws://localhost:6800') as ws:
ingestCounter = 0
while True:
try:
consumedStream = await ws.recv()
except websockets.ConnectionClosed:
print(color.red+'Websocket connection closed, retrying !'+color.reset)
break
if ingestCounter % 100 == 0:
print('\r'+f'Consuming: {ingestCounter}', end='')
ingestCounter += 1
await asyncio.sleep(0)
Is streaming good idea with web sockets? Should I use Flask or FastAPI with yield ? What is the best practice for web sockets streaming and consuming ?
I'm new to websockets. I've been using the examples on the Getting Started page of the websockets docs, mainly the Synchronization Example.
In this use case, I have a sqlite3 database on localhost. I edit that database from a python GUI program on localhost which just imports the database code layer directly. The client then tells the websocket server to send out some extracted data to all clients.
(Eventually this will be on a LAN, with the server machine running a Flask API.)
This is working, with the code below, but it's not clear if I'm doing it correctly. Basically I want to send websockets messages when certain database activity takes place, and I'm confused about how to do a 'simple' non-async send, when invoked from code, ultimately in response to a GUI interaction, as opposed to doing a send in response to an incoming websocket message. In pseudo-code:
def send(ws,msg):
ws.send(msg)
send(ws,'OK!')
The way I'm accomplishing that is wrapping the async def that does the sending in a non-async 'vanilla' def.
The websocket server code:
# modified from https://websockets.readthedocs.io/en/stable/intro.html#synchronization-example
import asyncio
import websockets
USERS = set()
async def register(websocket):
print("register: "+str(websocket))
USERS.add(websocket)
async def unregister(websocket):
print("unregister: "+str(websocket))
USERS.remove(websocket)
# each new connection calls trackerHandler, resulting in a new USERS entry
async def trackerHandler(websocket, path):
await register(websocket)
try:
async for message in websocket:
await asyncio.wait([user.send(message) for user in USERS])
finally:
await unregister(websocket)
start_server = websockets.serve(trackerHandler, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
in the database interface code (on localhost, this file is just imported directly to the GUI app; but on the LAN server, this is the file specified in the WSGI call in Flask):
import asyncio
import websockets
# uri = "ws://localhost:8765"
# wrap the asynchronous send function inside a synchronous function
def wsSend(uri,msg):
async def send():
async with websockets.connect(uri) as websocket:
# await websocket.send(str.encode(str(msg)))
await websocket.send(json.dumps({"msg":msg}))
# print(f"> {msg}")
# greeting = await websocket.recv()
# print(f"< {greeting}")
asyncio.get_event_loop().run_until_complete(send())
...
...
def tdbPushTables(uri,teamsViewList=None,assignmentsViewList=None,teamsCountText="---",assignmentsCountText="---"):
# uri = "ws://localhost:8765"
if not teamsViewList:
teamsViewList=tdbGetTeamsView()
if not assignmentsViewList:
assignmentsViewList=tdbGetAssignmentsView()
if uri=='pusher':
pusher_client.trigger('my-channel','teamsViewUpdate',teamsViewList)
pusher_client.trigger('my-channel','assignmentsViewUpdate',teamsViewList)
else:
wsSend(uri,json.dumps({
"teamsView":teamsViewList,
"assignmentsView":assignmentsViewList,
"teamsCount":teamsCountText,
"assignmentsCount":assignmentsCountText}))
it's actually the client that initiates the call to tdbPushTables:
def buildLists(self):
self.teamsList=tdbGetTeamsView()
self.assignmentsList=tdbGetAssignmentsView()
self.updateCounts()
tdbPushTables('ws://localhost:8765',self.teamsList,self.assignmentsList,self.teamsCountText,self.assignmentsCountText)
Feels spooky. Is it spooky or is this actually the right way to do it? Should I be using the websockets module for the server, but a different module to do the 'simple'/synchronous sending of the websocket message to the server?
Two known side effects of this solution: 1) it opens and closes the websocket connection on every call - probably not really a problem...?, and 2) it results in non-fatal handled messages like this in the server transcript:
register: <websockets.server.WebSocketServerProtocol object at 0x041C46F0>
Task exception was never retrieved
future: <Task finished coro=<WebSocketCommonProtocol.send() done, defined at C:\Users\caver\AppData\Roaming\Python\Python37\site-packages\websockets\protocol.py:521> exception=ConnectionClosedOK('code = 1000 (OK), no reason')>
Traceback (most recent call last):
File "C:\Users\caver\AppData\Roaming\Python\Python37\site-packages\websockets\protocol.py", line 555, in send
await self.ensure_open()
File "C:\Users\caver\AppData\Roaming\Python\Python37\site-packages\websockets\protocol.py", line 812, in ensure_open
raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedOK: code = 1000 (OK), no reason
unregister: <websockets.server.WebSocketServerProtocol object at 0x041C46F0>
EDIT: looks like the websocket (singular) module has a synchronous interface, and the websockets (plural) docs explain that if you want to go synchronous you should use a different module; so, this works:
(instead of importing asyncio and websockets)
from websocket import create_connection
def wsSend(uri,msg):
ws=create_connection(uri)
ws.send(json.dumps({"msg":msg}))
ws.close()
It does still result in the same handled traceback showing up in the server transcript each time wsSend is called; there's probably a way to silence that traceback output, but, regardless, it still doesn't seem to affect anything.
Your code feels spooky, because you are mixing async code with synchronous code.
Based on personal experience, the code is simpler to follow if you keep most of the code asynchronous.
The structure will become something like:
import asyncio
import websockets
async def main():
# Create websocket connection
async with websockets.connect(uri) as websocket:
await your_function_that_does_some_processing(websocket)
asyncio.get_event_loop().run_until_complete(main())
Have in mind that big sections of blocking code can generate trouble.
I have 3 machines; A, B, C.
A is running a websocket server that C wants to connect to, but C can't connect to A directly. To solve this I want to essentially 'proxy' the websocket through machine B.
A is acting as a publisher and producing new packets every few seconds that I want to push through to C. C does not need to send anything to A (although may be required in the future).
I want to implement this proxy by using the websockets (https://pypi.org/project/websockets/) module. I'm currently trying to create a server on B that listens for connections and keeps websocket connections with any clients. I then want to asynchronously run a websocket client that connects to machine A. This is a subscriber connection and any updates from the websocket connection to A should be pushed through to C (or any other clients connecting to B).
Here is my current (minimal) implementation in which an error occurs.
sockets = set()
def register(websocket):
sockets.add(websocket)
def unregister(websocket):
sockets.remove(websocket)
async def update_clients(update):
if sockets:
await asyncio.wait([socket.send(update) for socket in sockets])
async def client_server(websocket, path):
register(websocket)
await websocket.send("Welcome!")
async def live_updates():
async with websockets.connect(LIVE_URL) as websocket:
async for update in websocket.recv():
await update_clients(update)
webserver = websockets.serve(client_server, "0.0.0.0", PORT)
loop = asyncio.get_event_loop()
loop.run_until_complete(webserver)
loop.run_until_complete(live_updates)
loop.run_forever()
The error is as follows
Traceback (most recent call last):
File "/usr/lib/python3.6/asyncio/base_events.py", line 1310, in call_exception_handler
self.default_exception_handler(context)
File "/usr/lib/python3.6/asyncio/base_events.py", line 1282, in default_exception_handler
value = repr(value)
File "/usr/lib/python3.6/asyncio/base_tasks.py", line 15, in _task_repr_info
coro = coroutines._format_coroutine(task._coro)
File "/usr/lib/python3.6/asyncio/coroutines.py", line 276, in _format_coroutine
assert iscoroutine(coro)
AssertionError
When I remove the second run_until_complete(live_updates) the code runs with no errors. So I removed the first run_until_complete, compared my code to the example code from https://websockets.readthedocs.io/en/stable/intro.html and realised that
run_until_complete(live_updates) should be run_until_complete(live_updates())
I'm leaving this question in case anyone runs up against the same error, as the error message is confusing for a beginner to async.
I have an activemq set up with a ton of api calls to Zendesk. I need to retrieve those calls and then remove them from the queue entirely. conn.ack doesn't seem to be working!
I'm using python3 and the most recent version of stomp. I used this to make the initial connection script: https://github.com/jasonrbriggs/stomp.py/wiki/Simple-Example
https://jasonrbriggs.github.io/stomp.py/api.html
In this doc, it looks like you have to label the "id" tag of the .subscribe method. You call conn.ack with that id, but you also add the message id as an argument. I found out that the headers of the message are retrieved with the listener function. I printed those out and they look like this:
ID:[my workstation id].local-49557-1560302581785-5:58:-1:1:61592
I tried regex'ing out the whole string after ID:, and then I tried regexing out just the number on the very end of the string (it looks like possibly a unique number), but when I do conn.ack (matchObj.group(1), 4), the queue count doesn't change and I get no feedback as to why it doesn't.
The connection works absolutely fine so far-- I just can't send those acks.
import stomp
import time
import re
class SampleListener(object):
def on_message(self, headers, msg):
regex = r"ID:.*1:1:(.*)"
print(msg)
print(headers['message-id'])
matchObj = re.match ( regex, headers['message-id'], re.M|re.I)
print (matchObj.group(1))
conn.ack (matchObj.group(1), 4)
conn = stomp.Connection10()
conn.set_listener('SampleListener', SampleListener())
conn.start()
conn.connect()
conn.subscribe('zendeskqueue', id=4, ack='client')
time.sleep(1) # secs
conn.disconnect()
The code above has no error, it just exists without output.
Acknowledging messages with stomp.py and ActiveMQ works using ack id headers['ack'].
And you can get more verbose output by implementing on_error on your listener, and enabling debug logging.
With both, your code will look like that:
import stomp
import time
import logging
class SampleListener(object):
def on_message(self, headers, msg):
conn.ack(headers['ack'])
def on_error(self, headers, body):
print("An error occurred: headers=" + str(headers) + " ; body=" + str(body))
logging.basicConfig(level=logging.INFO)
logging.getLogger('stomp').setLevel(logging.DEBUG)
conn = stomp.Connection12()
conn.set_listener('SampleListener', SampleListener())
conn.start()
conn.connect()
conn.subscribe('zendeskqueue', id=4, ack='client')
time.sleep(1) # secs
conn.disconnect()