[Tutor] TCP server/client application failing. Need some help with the basics!

Anubhav Yadav anubhav1691 at gmail.com
Mon May 11 22:07:03 CEST 2015


I am very new to python. I have been a very mediocre programmer, but now I
have decided that I want to level up as a programmer.

I wanted to write a simple TCP client/server (where in the server acts as a
simple TCP Listener). I searched on the forums and I saw that people
advised using frameworks like twisted and sync.io for client server
applications to take advantage of asynchronous model. But since I couldn't
visualize the problems that people faced when they implemented servers as
multithreaded model, so I decided to start by implementing a multithreaded
server, and then improve as I go on facing issues.

So I started with using socket library for both client and server:

Here is my listener.py:

    import socket
    import threading
    from time import sleep

    class Serve(threading.Thread):
        def __init__(self, client_socket, client_address):
            threading.Thread.__init__(self)
            self.socket = client_socket
            self.address, self.port = client_address
        def run(self):
            try:
                while True:
                    data = self.socket.recv(300)
                    if data:
                        print data
                        sleep(2)
                    else:
                        break
            except Exception as e:
                print e
            finally:
                self.socket.close()


    if __name__ == '__main__':
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.bind(('localhost', 5555))
        sock.listen(1)

        servers = []

        while True:
            connection, client_address = sock.accept()
            print "New Connection from {}".format(client_address)
            server = Serve(connection, client_address)
            servers.append(server)
            server.start()

        for server in servers:
            server.join()

Here is my sender.py. It is supposed to simulate clients connecting to the
server.

    import argparse
    import socket
    import threading
    from time import sleep


    parser = argparse.ArgumentParser(description="A simple TCP sender")
    parser.add_argument('-H', '--host', help='host to connect to',
default='localhost')
    parser.add_argument('-p', '--port', help='port of the host', type=int,
default=5555)
    parser.add_argument('-w', '--workers', help='number of threads to
create', default=1000, type=int)
    args = parser.parse_args()
    count = args.workers

    def send(id):
        lock = threading.Lock()
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        server_address = (args.host, args.port)
        try:
            sock.connect(server_address)
            while True:
                sock.sendall('@ABCDEF1234567890FABC#')
                sleep(1)
        except Exception as e:
            print "thread no {} killed".format(id)
            print e
            lock.acquire()
            count-=1
            lock.release()
            sleep(1)
            print "Total threads are {}".format(count)
        finally:
            sock.close()

    threads = []
    if __name__ == '__main__':
        for i in range(args.workers):
            t = threading.Thread(target=send, args=(i,))
            threads.append(t)
            t.start()

    for thread in threads:
        thread.join()

When I run the client and server together with 1000 clients, many threads
are killed on a machine with low memory, and only 210-220 clients are
connected with the server. The killed clients gave the following error:

`[Errno 104] Connection reset by peer`

Right now I am not on the low memory machine, but I remember the error also
had "broken pipe" in it.

So I decided to run the same code with 1000 clients on my laptop with 8 GB
memory. This time almost all clients where connected but one or two clients
got killed with the same above error (I could not see broken error in the
messages this time".

Then I ran the same code with 500 clients, and this is where the code broke
with the following errors.

    Exception in thread Thread-4999:
    Traceback (most recent call last):
      File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
      File "/usr/lib/python2.7/threading.py", line 763, in run
      File "sender.py", line 17, in send
      File "/usr/lib/python2.7/socket.py", line 187, in __init__
    error: [Errno 24] Too many open files

Total threads are 4998

These errors came a lot, but the count said that only two threads failed. I
think my logic to calculate the count is wrong.

Then I decided to use SocketServer for the server. Here is the new Server:

    import SocketServer
    from threading import Thread
    from time import sleep

    class service(SocketServer.BaseRequestHandler):
        def handle(self):
            print "Client connected with ", self.client_address
            while True:
                try:
                    data = self.request.recv(300)
                    if data:
                        print data
                        sleep(2)
                    else:
                        print "Client exited"
                        self.request.close()
                except Exception as e:
                    print e


    class ThreadedTCPServer(SocketServer.ThreadingMixIn,
SocketServer.TCPServer):
        pass

    t = ThreadedTCPServer(('localhost',5555), service)
    t.serve_forever()

I have used the same client. This time there is some durability when there
are 1000 clients, but if I disconnect the sender (send a `kill -KILL pid`
signal) the listener breaks with the following error

`[Errno 9] Bad file descriptor`

Now if I run the sender with 5000 clients, the errors are back.

    Exception in thread Thread-4999:
    Traceback (most recent call last):
      File "/usr/lib/python2.7/threading.py", line 810, in __bootstrap_inner
      File "/usr/lib/python2.7/threading.py", line 763, in run
      File "sender.py", line 17, in send
      File "/usr/lib/python2.7/socket.py", line 187, in __init__
    error: [Errno 24] Too many open files

I don't even know if this is the right way of writing the servers, I also
know that possibly there is something wrong with my code, and maybe I am
not doing things as they are meant to be done.

Is it even right to create many thousands clients using threads on the same
machine? Is that what is causing problems? Should I learn some library like
twisted or zmq?

Would love to understand the details of threading and sockets! Please do
leave your comments and criticize me. Sorry for this long post.


More information about the Tutor mailing list