Define writable method in asyncore client makes sending data very slow

血红的双手。 提交于 2019-12-07 12:18:27

问题


I wrote a asynchorous client using python asyncore, and met some problems. I have solved this with the help of this:

Asyncore client in thread makes the whole program crash when sending data immediately

But now I meet some other problem.

My client program:

import asyncore, threading, socket
class Client(threading.Thread, asyncore.dispatcher):
    def __init__(self, host, port):
        threading.Thread.__init__(self)
        self.daemon = True
        self._thread_sockets = dict()
        asyncore.dispatcher.__init__(self, map=self._thread_sockets)
        self.host = host
        self.port = port
        self.output_buffer = []
        self.start()

    def send(self, msg):
        self.output_buffer.append(msg)
    def writable(self):
        return len("".join(self.output_buffer)) > 0
    def handle_write(self):
        all_data = "".join(self.output_buffer)
        bytes_sent = self.socket.send(all_data)
        remaining_data = all_data[bytes_sent:]
        self.output_buffer = [remaining_data]
    def handle_close(self):
        self.close()
    def handle_error(self):
        print("error")
    def handle_read(self):
        print(self.recv(10))
    def run(self):
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.connect((self.host, self.port))
        asyncore.loop(map = self._thread_sockets)

mysocket = Client("127.0.0.1",8400)
while True:
    a=str(input("input"))
    mysocket.send("popo")

And my server program:

import socket
HOST="127.0.0.1"
PORT=8400
s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("socket created")
s.bind((HOST, PORT))
s.listen(1)
print("listen")
conn,addr = s.accept()
print("Accepted. Receiving")
while True:
    data = conn.recv(20)
    print("Received: ")
    print(data)
    data = input("Please input reply message:\n").encode("utf-8")
    conn.send(data)
    print("Data sended. Receiving")

My problem is sending data from client to server is very slow, about 20 to 30 seconds! But it could always send data successfully. And if I comment out the writable method in client, the sending process becomes very fast. Why does it behave like this? How to fix it if I want to use the writable method? Thanks!

I start the server with python3, and client with python 2. I use ubuntu 14.04.


回答1:


The asyncore loop calls writable() when it is ready to do something with the socket. If the method writable() tells that there is something to write then handle_write() is called. The default writable() always returns True, so in that case there is busy loop calling handle_write() and writable().

In the above implementation the method writable() is called immediately when the client loop is started. At that moment there is nothing in the buffer, so writable() tells that there is nothing to write.

The asyncore loop calls select(). Now the loop is in "standby" state. It can be wakened only when some data is received by the socket or by timeout event. After any of those events the loop again checks writable().

The server sends nothing to the client and the client waits for timeout. The default timeout is 30 seconds, so that is why it is needed to wait up to 30 seconds before something is sent. It is possible to reduce the timeout during starting asyncore.loop():

    asyncore.loop(map = self._thread_sockets, timeout = 0.5)

Another idea that may come here is to check if the buffer is empty in send() and if it is empty send it immediately. However, it is a bad idea. The send() is called in the main thread, but the socket is managed by the asyncore loop in another thread.

For the same reason it makes sense to protect usage of output_buffer for concurrent access from different threads. The lock object threading.Lock() can be used here:

def __init__(self, host, port):
    #...
    self.lock = threading.Lock()

def send(self, msg):
    self.lock.acquire()
    try:
        self.output_buffer.append(msg)
    finally:
        self.lock.release()

def writable(self):
    is_writable = False;
    self.lock.acquire()
    try:
        is_writable = len("".join(self.output_buffer)) > 0
    finally:
        self.lock.release()

    return is_writable

def handle_write(self):
    self.lock.acquire()
    try:
        all_data = "".join(self.output_buffer)
        bytes_sent = self.socket.send(all_data)
        remaining_data = all_data[bytes_sent:]
        self.output_buffer = [remaining_data]
    finally:
        self.lock.release()

There is no thread safe mechanism to waken asyncore from another thread. So, the only solution is to reduce loop timeout, although too small timeout increases CPU usage.



来源:https://stackoverflow.com/questions/27427960/define-writable-method-in-asyncore-client-makes-sending-data-very-slow

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!