1

I'm trying to receive websocket messages in a greenlet, but it doent seem to be working. I have this code:

import gevent
from geventwebsocket.handler import WebSocketHandler
from gevent.pywsgi import WSGIServer
from geventwebsocket import WebSocketServer, WebSocketApplication, Resource



def recvWs(ws):
    gevent.sleep(0)
    recvedData = ws.receive()
    rData = json.loads(recvedData)
    print(rData)

def app(environ, start_response):


    websocket = environ['wsgi.websocket']

    while True:
        gevent.spawn(recvWs,websocket)
        gevent.sleep(0)

if __name__ == '__main__':
    server = WSGIServer(("0.0.0.0", 80), app,handler_class=WebSocketHandler)
    server.serve_forever()

And when running, it returns this error:

<Greenlet "Greenlet-0" at 0x23fa4306148: 
recvWs(<geventwebsocket.websocket.WebSocket object at 0x0)> failed with 
RuntimeError

As well as:

line 197, in read_frame
header = Header.decode_header(self.stream)

How do I fix this?

Dup Dup
  • 55
  • 9
  • You are over complicating this. Each connection to the webservice is it's own async thread. There is no connection to the function if you are spawning a new greenlet with the web connection, so there is no way to get the data to that. Also there is no JSON import in your code, so not sure if how that would work. – eatmeimadanish Nov 07 '18 at 21:49
  • @eatmeimadanish, how can I easily modify the websocket objects/threads from my main code? "There is no connection to the function if you are spawning a new greenlet with the web connection, so there is no way to get the data to that", I don't understand this, can you elaborate? – Dup Dup Nov 09 '18 at 05:26

1 Answers1

0

Here is an example of what I have done that works very well.
Python with bottle and gevent:

from gevent import sleep as gsleep, Timeout
from geventwebsocket import WebSocketError
from bottle import Bottle, get, post, route, request, response, template, redirect, abort

@route('/ws/app')
def handle_websocket():
    wsock = request.environ.get('wsgi.websocket')
    if not wsock:
        abort(400, 'Expected WebSocket request.')
    # Send initial data here
    wsock.send(json.dumps(data))
    while 1:
        try:
            #Process incoming message.  2 second timeout to not block
            message = {}
            with Timeout(2, False) as timeout:
                message = wsock.receive()
            if message:
                message = json.loads(message)
                if isinstance(message, dict):
                     # Do something with data and return
                     wsock.send(json.dumps(result))
            # Add an additional second just for sanity.  Not necessarily needed
            gsleep(1)
        except WebSocketError:
            break
        except Exception as exc:
            traceback.print_exc()
            gsleep(2)

Then in your javascript you open a websocket connection and send and recieve the data as you would normally.

eatmeimadanish
  • 3,809
  • 1
  • 14
  • 20
  • 1
    Correct me if i'm wrong, but the Timeout gives 2 seconds for the websocket to respond? If so how can I have the websockets get received in a separate thread? (I'm aiming to have the websocket messages sent at consistent intervals) – Dup Dup Nov 09 '18 at 18:07
  • So the timeout is so that the websocket doesn't block forever waiting for an input. If you take it out, the loop will never continue as the wsock.recieve() will never end. I don't like this design since I may want the application to look for updated data that lives outside the loop. This does not impact the sending of the data to the client. – eatmeimadanish Nov 09 '18 at 19:30
  • Also the route is treated as a separate thread automatically from the main request. – eatmeimadanish Nov 09 '18 at 19:33
  • So basically if I do a timeout it wont block and wont affect the frequency of sent updates (if they were in the same loop)? – Dup Dup Nov 09 '18 at 19:37
  • Yes, if you get rid of the gsleep(1) it will react in real time to your messages. – eatmeimadanish Nov 09 '18 at 22:54