0

I'm having an odd issue with Asyncio under Python 3.4.2 on a Debian 7 server. I'm trying to read from the client to detect if they disconnect, most of the examples on the web use this method where if you read the response and it is empty you break the loop and close the connection.

However after exactly nine iterations the script will die on the read line, I even tried to only make the read happen on every 10th iteration and it will then die after 90 iterations. If I don't try to detect the client disconnect the script will happily run forever without issue.

Original code was based off the server from this gist: https://gist.github.com/dbehnke/9627160

Any ideas what might be causing this?

(The script complete so you can run it to see for yourself)

import asyncio
import logging

import json
import time

log = logging.getLogger(__name__)

clients = {}

def accept_client(client_reader, client_writer):
    task = asyncio.Task(handle_client(client_reader, client_writer))
    clients[task] = (client_writer)

    def client_done(task):
        del clients[task]
        client_writer.close()
        log.info("End Connection")

    log.info("New Connection")
    task.add_done_callback(client_done)


@asyncio.coroutine
def handle_client(client_reader, client_writer):
    data = {'result':{'status':'Connection Ready'}}
    postmessage(data,client_writer)
    count = 0
    while True:
        data = (yield from asyncio.wait_for(client_reader.readline(),timeout=1.0))
        if not data: #client disconnected
            break

        data = yield from asyncio.wait_for(test1(),timeout=1.0)
        yield from postmessage(data,client_writer)

        data = yield from asyncio.wait_for(test2(),timeout=1.0)
        yield from postmessage(data,client_writer)

@asyncio.coroutine
def postmessage(data, client_writer):
        mimetype=('text/event-stream')
        response = ('data: {0}\n\n'.format(data).encode('utf-8'))
        client_writer.write(response)
        client_writer.drain()

@asyncio.coroutine
def test1():
        data = {'result':{
                        'test1':{ }
                    }
                }
        data = json.dumps(data)
        return data

@asyncio.coroutine
def test2():
        data = {'result':{ 
                    'test2':{ }
                    }
                }
        data = json.dumps(data)
        return data 

def main():
    loop = asyncio.get_event_loop()
    f = asyncio.start_server(accept_client, host=None, port=2991)
    loop.run_until_complete(f)
    loop.run_forever()

if __name__ == '__main__':
    log = logging.getLogger("")
    formatter = logging.Formatter("%(asctime)s %(levelname)s " +
                                  "[%(module)s:%(lineno)d] %(message)s")
    # log the things
    log.setLevel(logging.DEBUG)
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)

    ch.setFormatter(formatter)
    log.addHandler(ch)
    main()
KDM
  • 197
  • 5
  • 18
  • What does your client script look like? – dano Nov 20 '14 at 16:03
  • The client is either a Webapp or mobile app using a SSE library. I only need to broadcast from this script not receive so the readline() call quickly runs out of header to read and then timesout. I'm only reading to try and detect a client disconnect, it would be nice if there was a cleaner way to handle this case. – KDM Nov 21 '14 at 15:02
  • Looks like you figured out the reason for this behavior, based on the update for [this question](http://stackoverflow.com/questions/26964923/asyncio-detecting-disconnect-hangs). Should this question be closed? – dano Nov 25 '14 at 00:30

1 Answers1

0

You should run your program in debug mode to detect obvious asyncio bugs. You forgot "yield from" at different places in your code.

https://docs.python.org/dev/library/asyncio-dev.html#debug-mode-of-asyncio

vstinner
  • 1,274
  • 14
  • 7