I'm trying to make use a websocket client in python 2.7. The client works fine when running with IDLE, as in the received messages are shown. However trying to insert the websocket messages in a tkinter text widget is not working. Messages are still being displayed in IDLE, but the tkinter window is not being displayed at all.
The package I'm using is called websocket-client 0.32.0 and I dowloaded it from here. I followed the instructions on how to make a javascript like api and somewhat adapted the code.
Here is my code:
from Tkinter import *
from websocket import *
master = Tk()
master.wm_title("Websocket Test")
minwidth = master.winfo_screenwidth()/4*3
minheight = master.winfo_screenheight()/4*3
master.minsize(width=minwidth, height=minheight)
master.resizable(0,0)
text = Text(master)
text.pack(expand=True,fill=BOTH)
def on_message(ws, message):
text.insert(END, message+"\n")
print "Received: "+message
return
def on_error(ws, error):
text.insert(END, error+"\n")
print error
return
def on_close(ws):
text.insert(END, "### closed ###\n")
print "### closed ###"
return
def on_open(ws):
ws.send("hi")
ws.send("test")
return
enableTrace(True)
ws = WebSocketApp("ws://echo.websocket.org/", on_message = on_message, on_error = on_error, on_close = on_close)
ws.on_open = on_open
ws.run_forever()
master.mainloop()
Here is what is shown in IDLE if that is also helpful:
--- request header ---
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: echo.websocket.org
Origin: http://echo.websocket.org
Sec-WebSocket-Key: tXWAVVlaRoq2S4+p/z12gg==
Sec-WebSocket-Version: 13
-----------------------
--- response header ---
HTTP/1.1 101 Web Socket Protocol Handshake
Connection: Upgrade
Date: Fri, 21 Aug 2015 05:16:54 GMT
Sec-WebSocket-Accept: LH12LFLFaek6HgCnGIugF0sg9lA=
Server: Kaazing Gateway
Upgrade: websocket
-----------------------
send: '\x81\x82{b\x97\xfc\x13\x0b'
send: '\x81\x84\xa5J\xecf\xd1/\x9f\x12'
Received: hi
Received: test
I've been stuck on this for a while and I cannot find any solutions to this sort of problem. Any help is appreciated as I'm a newbie.
Well, I do not use websocket , but the issue seems to be that , when you do -
ws.run_forever()
The application goes into an infinite loop , so the control never reaches master.mainloop() , which is required for the tkinter to show up , and which is again another infinite loop, which is only exited when you close the gui.
What you really want is to run two infinite loop simultaneously, which would require you to run them in two different threads, currently both of them are defined on the same thread, so one would only run after the other has exited.
A quick fix would be to use master.after() with a certain delay and giving a function, and then in the function start a new thread for a function in which websocket connects to the server. so that both the infinite loops are in different threads.
But the best fix would be to use a button, lets call it 'Connect' . Then the callback for that button would be a function like on_connect() below , which starts the function to connect -
def connect_to_socket():
enableTrace(True)
ws = WebSocketApp("ws://echo.websocket.org/", on_message = on_message, on_error = on_error, on_close = on_close)
ws.on_open = on_open
ws.run_forever()
def on_connect():
import threading
t = threading.Thread(target=connect_to_socket)
t.start()
The button would be something like -
button = Button(master, text="Connect", command=on_connect)
Then you can place the button as you wish.
Thanks to Anand's response, I was able to figure out the problem. The working and updated code is posted below:
from Tkinter import *
from websocket import *
from threading import *
master = Tk()
master.wm_title("Team Black Client")
master.withdraw()
minwidth = master.winfo_screenwidth()/4*3
minheight = master.winfo_screenheight()/4*3
master.minsize(width=minwidth, height=minheight)
x = (master.winfo_screenwidth() - minwidth)/2
y = (master.winfo_screenheight() - minheight)/2
master.geometry("+"+str(x)+"+"+str(y))
master.deiconify()
master.resizable(0,0)
text = Text(master)
text.pack(expand=True,fill=BOTH)
def on_message(ws, message):
text.insert(END, message+"\n")
print "Received: "+message
return
def on_error(ws, error):
text.insert(END, error+"\n")
print error
return
def on_close(ws):
text.insert(END, "### closed ###\n")
print "### closed ###"
return
def on_open(ws):
ws.send("hi")
ws.send("test")
return
def connection():
enableTrace(True)
ws = WebSocketApp("ws://echo.websocket.org/", on_message = on_message, on_error = on_error, on_close = on_close)
ws.on_open = on_open
ws.run_forever()
return
t = Thread(target=connection)
t.start()
master.mainloop()
Related
I need to add some code to send keepalive request every 5 seconds to the following program which is using websocket-client
def on_open(wsapp):
wsapp.send(json.dumps(reqlogin))
wsapp.send(json.dumps(reqsub))
def on_message(wsapp, msg):
handle(msg)
wsapp = websocket.WebSocketApp(url, on_open=on_open, on_message=on_message)
wsapp.run_forever()
I've looked at documentation but couldn't find anything appropriate.
I managed to solve the issue using threading:
def on_open(wsapp):
wsapp.send(json.dumps(reqlogin))
wsapp.send(json.dumps(reqsub))
def on_message(wsapp, msg):
handle(msg)
wsapp = websocket.WebSocketApp(url, on_open=on_open, on_message=on_message)
wsappthread = threading.Thread(target=wsapp.run_forever,
daemon=True)
wsappthread.start()
while True:
time.sleep(5)
wsapp.send(json.dumps(reqkeepalive))
I use websocket to make a long-live connection with the target wss-url successfully. But after receiving one message, the code caught an error named "rsv is not implemented, yet" and closed the connection.
It seems that few people have met this problem, which described as "rsv is not implemented, yet". And the API doc of websocket never mention this issue.
The main piece of my code:
def on_message(ws, message):
print(message)
def on_error(ws, error):
print("!!!find error!!!")
print(error)
def on_close(ws):
print("### why closed ???###")
websocket.enableTrace(True)
ws = websocket.WebSocketApp(url,
on_message = on_message,
on_error = on_error,
on_close = on_close,
header = header,
cookie = cookie,
)
ws.run_forever(origin = 'https://matters.news', skip_utf8_validation = True)
It will give me only one message, and then show that:
!!!find error!!!
rsv is not implemented, yet
send: b'\x88\x82\xd9\xe2\xcc\x8c\xda\n'
### why closed ???###
I received the same error and fixed it by removing:
'Sec-WebSocket-Extensions': 'permessage-deflate'
from my headers.
I'm trying to use websockets in python, and i previously asked a question about this. I quickly realized, though, that the way i was connecting to the server was meant for "one off" messages, while what i want to do needs to listen for notifications constantly.
In the documentation for the python websocket client i can see the following code:
import websocket
import 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("Hello %d" % i)
time.sleep(1)
ws.close()
print "thread terminating..."
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://echo.websocket.org/",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
The thing is, i'm still quite new to python, and i don't completely understand this code. Since it's example code, and does things that i don't need it to, i would like to understand how it works. I, for example, don't understand what the for loop is needed for, or what the __name__ and the __main__ at the bottom are.
Is there a better way?
Thanks,
Sas :)
The for loop is probably just an example since it will just print Hello 0, Hello 1 and Hello 2.
__name__ == "__main__" is true when the Python interpreter is running a module as the main program, which you can read more about here. Whenever that happens, it assigns what functions should be used on message, error and when the socket closes. And when that's done it runs the WebSocket forever.
So, to create your own long-lived WebSocket you can copy this example code and change the on_message, on_error, on_close and on_open functions to do what you want them to do whenever these events occour. on_message activates whenever a message is sent, on_error when an error occours, on_close when the WebSocket closes and on_open when the WebSocket opens.
I'm trying to implement a REST client in python that reacts to messages received from the server received through an opened websocket with the concerned server.
Here is the scenario:
client opens a websocket with the server
from time to time, the server sends a message to the client
when the client receives the messages, it gets some information from
the server
The current client I have is able to open the websocket and to receive the message from the server. However, as soon as it receives the messages, it gets the information from the server then terminates while I'd like to keep it listening for other messages that will make it get a new content from the server.
Here is the piece of code I have:
def openWs(serverIp, serverPort):
##ws url setting
wsUrl = "ws://"+serverIp+":"+serverPort+"/websocket"
##open ws
ws = create_connection(wsUrl)
##send user id
print "Sending User ID..."
ws.send("user_1")
print "Sent"
##receiving data on ws
print "Receiving..."
result = ws.recv()
##getting new content
getUrl = "http://"+serverIp+":"+serverPort+"/"+result+"/entries"
getRest(getUrl)
I don't know if using threads is appropriate or not, I'm not expert in that.
If someone could help, it'll be great.
Thanks in advance.
I finished with this code, doing what I'm expecting. Git it from here
import websocket
import 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("Hello %d" % i)
time.sleep(1)
ws.close()
print "thread terminating..."
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://localhost:5000/chat",
on_message = on_message,
on_error = on_error,
on_close = on_close)
ws.on_open = on_open
ws.run_forever()
I'm trying to send session id (I got it after authentication against http server) over a websocket connection (I'm using python websocket client), I need to pass it as a header parameter, where the server will read all the headers and get them checked.
The questions is: how can I add headers to using one of the existing client python Websocket implementations, I find none of them can do that, or am I following the wrong approach in the first place for authentication?
-- Update --, Below a template of the code I use:
def on_message(ws, message):
print 'message received ..'
print message
def on_error(ws, error):
print 'error happened .. '
print error
def on_close(ws):
print "### closed ###"
def on_open(ws):
print 'Opening Websocket connection to the server ... '
## This session_key I got, need to be passed over websocket header isntad of ws.send.
ws.send(session_key)
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp("ws://localhost:9999/track",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close,
)
ws.on_open = on_open
ws.run_forever()
It seems that websocket-client was updated to include websocket headers since this question was asked. Now you can simply pass a list of header parameters as strings:
custom_protocol = "your_protocol_here"
protocol_str = "Sec-WebSocket-Protocol: " + custom_protocol
ws = websocket.WebSocketApp("ws://localhost:9999/track",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close,
header = [protocol_str]
)
If you are interested in the complete list of valid headers, see the websocket RFC6455 document: https://www.rfc-editor.org/rfc/rfc6455#section-4.3
GitHub Source: https://github.com/liris/websocket-client/blob/master/websocket.py
Nothing is more amusing than reading the source code :))
I monkey patched the source code of the Websocket client library to make it able to receive a header as a normal parameter in the initializer, like this:
ws = websocket.WebSocketApp("ws://localhost:9999/track",
on_open = on_open,
on_message = on_message,
on_error = on_error,
on_close = on_close,
header = {'head1:value1','head2:value2'}
)
This can be done by editing 3 lines in the websocket.py source code of the library:
1- Add header parameter:
## Line 877
class WebSocketApp(object):
"""
Higher level of APIs are provided.
The interface is like JavaScript WebSocket object.
"""
def __init__(self, url,
on_open = None, on_message = None, on_error = None,
on_close = None, keep_running = True, get_mask_key = None, header = None):
self.url = url
self.on_open = on_open
self.on_message = on_message
self.on_error = on_error
self.on_close = on_close
self.keep_running = keep_running
self.get_mask_key = get_mask_key
self.sock = None
self.header = header
2- Then, pass the self.header to websocket connect method as a header parameter, like this:
## Line 732
self.sock.connect(self.url, header = self.header)
Actually I tried to import the WebSocketApp class, but it didn't work, as the whole websocket.py module is interdependent, that I found myself importing a lot of things to make it work, monkey patching is easier and more solid in this case.
That's all, enjoy using your patched library with all the headers you need.