In this Python 3 client server example, the client cannot send more than one message
This is a simple client-server example, where the server returns whatever the client sends but canceled.
Server:
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024)
print('RECEIVED: ' + str(self.data))
self.request.sendall(str(self.data)[::-1].encode('utf-8'))
server = socketserver.TCPServer(('localhost', 9999), MyTCPHandler)
server.serve_forever()
Customer:
import socket
import threading
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('localhost',9999))
def readData():
while True:
data = s.recv(1024)
if data:
print('Received: ' + data.decode('utf-8'))
t1 = threading.Thread(target=readData)
t1.start()
def sendData():
while True:
intxt = input()
s.send(intxt.encode('utf-8'))
t2 = threading.Thread(target=sendData)
t2.start()
I took the server from an example I found on Google, but the client was written from scratch. The idea was that the client could continue to send and receive data from the server indefinitely.
Sending the first message to the client. But when I try to send the second message, I get this error:
ConnectionAbortedError: [WinError 10053] An established connection was interrupted by your host computer software
What am I doing wrong?
source to share
For, TCPServer
the handler method handle
is called once to process the entire session. This may not be entirely clear from the documentation, but socketserver
like many libraries in stdlib, it is intended to serve as a clear sample of code as well as being used directly, so the docs link to the source where you can clearly see what it will only call handle
once per connection ( TCPServer.get_request
defined as just a call accept
on a socket).
So, your server receives one buffer, sends a response, and then exits by closing the connection.
To fix this, you need to use a loop:
def handle(self):
while True:
self.data = self.request.recv(1024)
if not self.data:
print('DISCONNECTED')
break
print('RECEIVED: ' + str(self.data))
self.request.sendall(str(self.data)[::-1].encode('utf-8'))
A few notes:
First, by using it BaseRequestHandler
yourself, you can handle one client connection at a time. As the introduction in the docs says:
These four classes process requests synchronously; each request must be completed before the next request can be started. This is not suitable if each request takes a long time, because it is computationally intensive, or because it returns a lot of data that the client is slow to process. The solution is to create a separate process or thread to handle each request; support classes
ForkingMixIn
andThreadingMixIn
can be used to support asynchronous behavior.
These mixin classes are described later in the rest of the introduction, and down the page , and down , with a good example at the end. The docs don't give a clear idea, but if you need to do any cpu work in your handler, you want ForkingMixIn
; if you need to exchange data between handlers, you want ThreadingMixIn
; otherwise, it doesn't really matter which one you choose.
Note that if you are trying to handle a large number of concurrent clients (more than a couple dozen), neither multiplying nor threads nor slicing is really suitable, which means it TCPServer
is not really suitable. In that case, you probably want asyncio
either a third party library (Twisted, gevent, etc.).
A challenge str(self.data)
is a bad idea. You just get the original byte string coded representation for example b'spam\n'
. Do you want to decode the byte string to a Unicode string equivalent to: self.data.decode('utf8')
.
There is no guarantee that everyone sendall
on one side will match one recv
on the other. TCP is a stream of bytes, not a stream of messages; it is quite possible to receive half a message in one recv
and two and a half messages in the next. When tested with a single connection on a localhost with a system under light load, it will probably work, but as soon as you try to expand any code that assumes everyone recv
receives exactly one message, your code breaks . See Sockets are streams of bytes rather than message streams for more details. Note that if your posts are just lines of text (as in your example), using StreamRequestHandler
both its attribute rfile
instead of BaseRequestHandler
its attributerequest
solves this problem trivially.
You probably want to install server.allow_reuse_address = True
. Otherwise, if you log out of the server and start it again too quickly, it will fail, eg OSError: [Errno 48] Address already in use
.
source to share