简体   繁体   中英

In a no-coroutine function, how do you get a return value from coroutine function of Tornado TCP?

I wrote 3 sections of code with Tornado TCP. I've encountered some difficulties.

My code is following:

client.py

'''tcp client'''


from socket import socket, AF_INET, SOCK_STREAM
s = socket(AF_INET, SOCK_STREAM)
s.connect(('localhost', 20000))
resp = s.recv(8192)
print('Response:', resp)
s.send(b'Hello\n')
s.close()

server.py

'''tcp server'''

#! /usr/bin/env python
#coding=utf-8

from tornado.tcpserver import TCPServer
from tornado.ioloop import IOLoop
from tornado.gen import *

clientDict=dict()        #save infomation of client

class TcpConnection(object):
    def __init__(self,stream,address):
        self._stream=stream
        self._address=address
        self._stream.set_close_callback(self.on_close)

    @coroutine
    def send_messages(self):
        yield self.send_message(b'world \n')
        response = yield self.read_message()

        return response


    def read_message(self):
        return self._stream.read_until(b'\n')

    def send_message(self,data):
        return self._stream.write(data)

    def on_close(self):
        global clientDict
        clientDict.pop(self._address)
        print("the monitored %d has left",self._address)

class MonitorServer(TCPServer):
    @coroutine
    def handle_stream(self,stream,address):
        global clientDict
        print("new connection",address,stream)
        clientDict.setdefault(address, TcpConnection(stream,address))



if __name__=='__main__':
    print('server start .....')
    server=MonitorServer()
    server.listen(20000)
    IOLoop.instance().start()

main.py

import time
from threading import Thread
import copy
from server import *

def watchClient():
    '''call the "send" function when a new client connect''

    global clientDict
    print('start watch')
    lastClientList=list()
    while True:
        currentClientList=copy.deepcopy([key for key in clientDict.keys()])
        difference=list(set(currentClientList).difference(set(lastClientList)))

        if len(difference)>0:
            send(difference)
            lastClientList=copy.deepcopy(currentClientList)
            time.sleep(5)
        else:
            time.sleep(5)
            continue

def send(addressList):
    '''send message to a new client and get response'''
    global clientDict
    for address in addressList:
        response=clientDict[address].send_messages()
        print(address," response :",response)

def listen():
    server=MonitorServer()
    server.listen(20000)
    IOLoop.instance().start()


if __name__=='__main__':
    listenThread=Thread(target=listen)
    watchThead=Thread(target=watchClient)
    watchThead.start()
    listenThread.start()

I want to get the "print information" when the main.py is run--- address,response:b'hello\\n'

But in fact I get the "print information" as ----

('127.0.0.1', 41233)  response :<tornado.concurrent.Future object at 0x7f2894d30518>

It can't return the b'hello\\n' .

Then I guess it can't get the response reasonably in the no-coroutine function( def send(addressList) ) from the coroutine function( @coroutine def send_messages(self) ).

How to solve this?

By the way, I want to know how to make the clientDict to be a property of the class MonitorServer ,not a global property. Please help me! Thank you.

In general, anything that calls a coroutine should be a coroutine itself. Mixing threads and coroutines can be very tricky; most coroutine-based code is deliberately not thread-safe.

The correct way to call a coroutine from a non-coroutine function on a different thread is something like this:

def call_coro_from_thread(f, *args, **kwargs):
    q = queue.Queue()
    def wrapper():
        fut = f(*args, **kwargs)
        fut.add_done_callback(q.put)
    IOLoop.instance().add_callback(wrapper)
    fut = q.get()
    return fut.result()

IOLoop.add_callback is necessary to safely transfer control to the IOLoop thread, and then the Queue is used to transfer the result back.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM