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?

+3


source to share


1 answer


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

and ThreadingMixIn

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

.

+3


source







All Articles