Thank you for checking in! I've got a question about the websocket connection.
Here's a websocket class I use to subscribe to an exchange's websocket, where the connection is established in self.connectWS_public() function.
from datetime import datetime as dt
import threading
import websocket
import json
import time
class Bybit_WS_test():
def __init__ (self):
self.api_url_public = 'wss://stream.bybit.com/realtime_public'
self.api_url_private = 'wss://stream-testnet.bybit.com/realtime_private'
self.api_key = ''
self.api_secret = ''
self.ping_interval = 20
self.ping_timeout = 10
self.ws_public = None
self.ws_private = None
return
def on_message(self, ws, message):
data = json.loads(message)
print('Received message:')
print(data)
def on_error(self, ws, error):
print(f'webscoket error: {error}')
def on_close(self, ws):
print("Closing websocket connection...")
def on_pong(self, ws, message):
print('Received pong')
print(message)
def on_open(self, ws):
print('Websocket opened')
def on_ping(self, message):
dt_string = dt.now().strftime("%d/%m/%Y %H:%M:%S")
print(message)
print(f'Received ping # {dt_string}')
def connectWS_public(self):
self.ws_public = websocket.WebSocketApp(
url = self.api_url_public,
on_message = self.on_message,
on_error = self.on_error,
on_ping= self.on_ping,
on_pong= self.on_pong,
on_open= self.on_open
)
self.wst_public = threading.Thread(target=lambda: self.ws_public.run_forever(ping_interval=self.ping_interval, ping_timeout=self.ping_timeout))
self.wst_public.daemon = True
self.wst_public.start()
However, when I test the connection in another file called test.py, I always encounter the following error:
File "/Users/rickycheng/Desktop/pair-trading-bot/venv/lib/python3.7/site-packages/websocket/_socket.py", line 143, in send
raise WebSocketConnectionClosedException("socket is already closed.")
websocket._exceptions.WebSocketConnectionClosedException: socket is already closed.
Below is my test.py used to test the websocket connection:
from Bybit_api.Bybit_ws import Bybit_WS_test
import json
if __name__ == '__main__':
x = Bybit_WS_test()
x.connectWS_public()
while (x.ws_public.sock):
print(True)
topic = "orderBookL2_25.BTCUSD"
x.ws_public.send(json.dumps({"op": "subscribe",'args':[topic]}))
You may check the exchange API documentation via
https://bybit-exchange.github.io/docs/linear/#t-heartbeat
Guys I have got the solution:
In general, if you have encountered a similar error like me, it's possible that you didn't sleep the program after establishing the connection.
Try to add: time.sleep(5) after calling ws.run_forever()
This allows the websocket connection to be successfully connected before sending any request to it.
Related
good morning.
I'm trying to make an API for Quotex.com brokerage where your communication is done via websocket --> wss://ws.qxbroker.com/socket.io/?EIO=3&transport=websocket
To connect to the broker I'm using Selenium. I can recover the session, but the difficulty is to send this data back to the websocket.
My code is this, where I try to connect and then send an order to broker.
Could someone help me please.
order = '42["orders/open",{"asset":"AUDCAD_otc","amount":6,"time":1637893200,"action":"put","isDemo":1,"requestId":1637892541,"optionType":1}]'
order2 = json.dumps(order)
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
print(message)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("""42["authorization",{"session":""" + session + ""","isDemo":1}]""")
time.sleep(1)
ws.send(order)
ws.send(order2)
ws.close()
print("thread terminating...")
thread.start_new_thread(run, ())
urlsocket = "wss://ws.qxbroker.com/socket.io/?EIO=3&transport=websocket"
ws = websocket.WebSocketApp(
urlsocket, on_message=on_message,
on_open=on_open)
ws.run_forever()
Example of analysis via google chrome devtools
Exemple Send Order for Broker
So I'm learning how to use websockets on Python and I copied the instructor's code. But it worked for him and I got an error. For some reason, the client object is recognized, then unrecognized later.
Here's the code:
import websockets
import asyncio
import pyodbc
class WebSocketClient():
def __init__(self):
self.cnxn = None
self.crsr = None
def database_connect(self):
# Define server and database
server = 'DESKTOP-O9QIKO1\SQLEXPRESS'
database = 'Database'
sql_driver = '{ODBC Driver 17 for SQL Server}'
# Define database connection
self.cnxn = pyodbc.connect(driver = sql_driver,
server = server,
database = database,
trusted_connection = 'yes')
self.crsr = self.cnxn.cursor()
def database_insert(self, query, data_tuple):
self.crsr.execute(query, data_tuple)
self.cnxn.commit()
self.cnxn.close()
print('Data has been successfully inserted into the database')
async def connect(self):
uri = "wss://" + userPrincipalsResponse['streamerInfo']['streamerSocketUrl'] + "/ws"
self.connection = await websockets.client.connect(uri)
if self.connection.open:
print("Connection established. Client correctly connected.")
return self.connection
async def sendMessage(self, message):
await self.connection.send(message)
async def receiveMessage(self, connection):
while True:
try:
# Decode message
message = await connection.recv()
message_decoded = json.loads(message)
# Prepare data for insertion, connect to database
query = "INSERT INTO td_service_data (service, timestamp, command) VALUES (?,?,?);"
self.database_connect()
if 'data' in message_decoded.keys():
# Grab data
data = message_decoded['data'][0]
data_tuple = (data['service'], data['timestamp'], data['command'])
# Insert data
self.database_insert(query, data_tuple)
print('-'*20)
print('Received message from server ' + str(message))
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed')
break
async def heartbeat(self, connection):
while True:
try:
await connection.send('ping')
await aysncio.sleep(5)
except websockets.exceptions.ConnectionClosed:
print('Connection with server closed')
break
import nest_asyncio
nest_asyncio.apply()
if __name__ == '__main__':
# Create client object
client = WebSocketClient()
# Define event loop
loop = asyncio.get_event_loop()
# Start a connection to the websocket
connection = loop.run_until_complete(client.connect())
# Define tasks to run
tasks = [asyncio.ensure_future(client.receiveMessage(connection)),
asyncio.ensure_future(client.sendMessage(login_encoded)),
asyncio.ensure_future(client.receiveMessage(connection)),
asyncio.ensure_future(client.sendMessage(data_encoded)),
asyncio.ensure_future(client.receiveMessage(connection))]
# Run tasks
loop.run_until_complete(asyncio.wait(tasks))
When I run this, I get:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-40-1d8d99c4eed9> in <module>
11
12 # Start a connection to the websocket
---> 13 connection = loop.run_until_complete(client.connect())
14
15 # Define tasks to run
AttributeError: module 'websockets' has no attribute 'client'
But when I ran client, I got <__main__.WebSocketClient at 0x232d7ce7048>.
And when I ran client.connect(), I got <coroutine object WebSocketClient.connect at 0x00000232D7DE3948>, so it does recognize client, doesn't it?
I'm so confused. Any input would be greatly appreciated.
Thanks.
I have set up websocket connections to multiple cryptocurrency exchanges but I'm having difficulty connecting to bitFlyer's.
My code is as follows:
import websocket
import json
def on_message(ws, message):
msg = json.loads(message)
print(msg)
def on_error(ws, error):
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
ws.send(json.dumps({"method":"subscribe", "channel":"lightning_executions_FX_BTC_JPY"}))
while True:
if __name__ == "__main__":
#websocket.enableTrace(True)
ws = websocket.WebSocketApp("wss://ws.lightstream.bitflyer.com/json-rpc",
on_message=on_message,
on_error=on_error,
on_close=on_close)
ws.on_open = on_open
ws.run_forever()
I have tried many many variations of my on_open() message and most result in a ### closed ###
Invalid close opcode. error.
Unfortunately their documentation does not contain a Python sample located HERE.
Any help in sending the correct message is much appreciated.
I believe the format of message you sent was wrong, check the following reference from https://lightning.bitflyer.jp/docs/playgroundrealtime, guess it will solve.
# pip install websocket-client
import websocket
import json
CHANNEL = "lightning_board_snapshot_<productCode>"
def on_message(ws, message):
message = json.loads(message)
if message["method"] == "channelMessage":
print("{} {}".format(message["params"]["channel"], message["params"]["message"]))
def on_open(ws):
ws.send(json.dumps({"method": "subscribe", "params": {"channel": CHANNEL}}))
if __name__ == "__main__":
// note: reconnection handling needed.
ws = websocket.WebSocketApp("wss://ws.lightstream.bitflyer.com/json-rpc",
on_message = on_message, on_open = on_open)
ws.run_forever()
I'm working on a project which uses python asyncio socket server. The problem is that the implementation of the server doesn't call .close() on the transport when the server stops. This seems to leave clients connected and causes crashes in other parts of the code.
Python documents say that transports need to be closed explicitly, but in this project I don't know where I can close them because there is no reference to the transports that are created for each client.
https://docs.python.org/3/library/asyncio-dev.html#close-transports-and-event-loops
Here is the code:
"""
Socket server forwarding request to internal server
"""
import logging
try:
# we prefer to use bundles asyncio version, otherwise fallback to trollius
import asyncio
except ImportError:
import trollius as asyncio
from opcua import ua
from opcua.server.uaprocessor import UaProcessor
logger = logging.getLogger(__name__)
class BinaryServer(object):
def __init__(self, internal_server, hostname, port):
self.logger = logging.getLogger(__name__)
self.hostname = hostname
self.port = port
self.iserver = internal_server
self.loop = internal_server.loop
self._server = None
self._policies = []
def set_policies(self, policies):
self._policies = policies
def start(self):
class OPCUAProtocol(asyncio.Protocol):
"""
instanciated for every connection
defined as internal class since it needs access
to the internal server object
FIXME: find another solution
"""
iserver = self.iserver
loop = self.loop
logger = self.logger
policies = self._policies
def connection_made(self, transport):
self.peername = transport.get_extra_info('peername')
self.logger.info('New connection from %s', self.peername)
self.transport = transport
self.processor = UaProcessor(self.iserver, self.transport)
self.processor.set_policies(self.policies)
self.data = b""
def connection_lost(self, ex):
self.logger.info('Lost connection from %s, %s', self.peername, ex)
self.transport.close()
self.processor.close()
def data_received(self, data):
logger.debug("received %s bytes from socket", len(data))
if self.data:
data = self.data + data
self.data = b""
self._process_data(data)
def _process_data(self, data):
buf = ua.utils.Buffer(data)
while True:
try:
backup_buf = buf.copy()
try:
hdr = ua.Header.from_string(buf)
except ua.utils.NotEnoughData:
logger.info("We did not receive enough data from client, waiting for more")
self.data = backup_buf.read(len(backup_buf))
return
if len(buf) < hdr.body_size:
logger.info("We did not receive enough data from client, waiting for more")
self.data = backup_buf.read(len(backup_buf))
return
ret = self.processor.process(hdr, buf)
if not ret:
logger.info("processor returned False, we close connection from %s", self.peername)
self.transport.close()
return
if len(buf) == 0:
return
except Exception:
logger.exception("Exception raised while parsing message from client, closing")
self.transport.close()
break
coro = self.loop.create_server(OPCUAProtocol, self.hostname, self.port)
self._server = self.loop.run_coro_and_wait(coro)
print('Listening on {}:{}'.format(self.hostname, self.port))
def stop(self):
self.logger.info("Closing asyncio socket server")
self.loop.call_soon(self._server.close)
self.loop.run_coro_and_wait(self._server.wait_closed())
As you can see when we call stop() on this server class the asyncio server calls it's close method. However if clients are connected the created transports never get closed.
The project repository is here https://github.com/FreeOpcUa/python-opcua/ , you can take a look at Issue 137.
What is the correct way to close the transport object?
I solve this by applying this approach:
#self.OPCUAServer - this is my opcua server
nodes = []
nodes.append(self.OPCUAServer.get_node("ns=0; s=Measurements")) #Adding two root nodes
nodes.append(self.OPCUAServer.get_node("ns=1; s=Calibrations")) #to the list
self.OPCUAServer.delete_nodes(nodes, True) # Recursively call delete_nodes with this list
self.OPCUAServer.stop()
I have Python code like this:
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
print "origin: " + origin
return True
# the client connected
def open(self):
print "New client connected"
self.write_message("You are connected")
# the client sent the message
def on_message(self, message):
print "message: " + message
self.write_message(message)
# client disconnected
def on_close(self):
print "Client disconnected"
socket = tornado.web.Application([(r"/wss", WebSocketHandler),])
if __name__ == "__main__":
socket.listen(8888)
tornado.ioloop.IOLoop.instance().start()
while True:
readmydata()
#send message to all connected clients
time.sleep(3)
How can I start the websocket server, but continue with the python code that sends the message to all connected clients? The script blocks at tornado.ioloop.IOLoop.instance().start(), so my while True loop never runs.
You can use tornado.ioloop.IOLoop.add_timeout to call a method every X number of seconds from within the Tornado event loop. To send a message to all conncected clients, you'll need to maintain a global list of each connected client, which you update on each connection/disconnection.
clients = []
class WebSocketHandler(tornado.websocket.WebSocketHandler):
def check_origin(self, origin):
print "origin: " + origin
return True
# the client connected
def open(self):
print "New client connected"
self.write_message("You are connected")
clients.append(self)
# the client sent the message
def on_message(self, message):
print "message: " + message
self.write_message(message)
# client disconnected
def on_close(self):
print "Client disconnected"
clients.remove(self)
def send_message_to_clients():
try:
read_my_data()
for client in clients:
# Do whatever
finally:
tornado.ioloop.IOLoop.instance().add_timeout(timedelta(seconds=3),
send_message_to_clients)
socket = tornado.web.Application([(r"/wss", WebSocketHandler),])
if __name__ == "__main__":
socket.listen(8888)
tornado.ioloop.IOLoop.instance().add_timeout(timedelta(seconds=3),
send_message_to_clients)
tornado.ioloop.IOLoop.instance().start()
A simpler approach:
import tornado.httpserver
import tornado.websocket
import tornado.ioloop
import tornado.web
import socket
import random
import threading
import time
'''
This is a simple Websocket Echo server that uses the Tornado websocket handler.
Please run `pip install tornado` with python of version 2.7.9 or greater to install tornado.
This program will echo back the reverse of whatever it recieves.
Messages are output to the terminal for debuggin purposes.
'''
clients = []
def updateClients():
while True:
for c in clients:
c.write_message('your update data')
time.sleep(1) #in seconds you can use float point
class WSHandler(tornado.websocket.WebSocketHandler):
def open(self):
print 'new connection'
clients.append(self)
def on_message(self, message):
self.write_message('echo ' + message)
def on_close(self):
clients.remove(self)
def check_origin(self, origin):
return True
application = tornado.web.Application([
(r'/ws', WSHandler),
])
if __name__ == "__main__":
t = threading.Thread(target = updateClients)
t.start()
#start the thread before tornado
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
myIP = socket.gethostbyname(socket.gethostname())
print '*** Websocket Server Started at %s***' % myIP
tornado.ioloop.IOLoop.instance().start()