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.
The
asyncore
loop callswritable()
when it is ready to do something with the socket. If the methodwritable()
tells that there is something to write thenhandle_write()
is called. The defaultwritable()
always returnsTrue
, so in that case there is busy loop callinghandle_write()
andwritable()
.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, sowritable()
tells that there is nothing to write.The
asyncore
loop callsselect()
. 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 checkswritable()
.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 startingasyncore.loop()
: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. Thesend()
is called in the main thread, but the socket is managed by theasyncore
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 objectthreading.Lock()
can be used here: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.