Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this socket connection only allow 1 send and receive?

Background
I have a simple socket server setup that I am trying to allow simultaneous connections to and echo back the data. The client side launches several threads each making its own connection to the server. This works fine for the socket.send() call, but all subsequent calls cause either a "Connection reset by peer" or a "Broken pipe". Note that I have not found the change that toggles the reset and broken pipe. I have looked here on SO for a solution, but I'm afraid I may not know what to search for.

Am I going about this in the wrong manner, or am I overlooking something in my setup?

Server

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print "{} wrote: {}\n".format(self.client_address[0], self.data)
        self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

Client

import socket
import sys
import threading
import time

HOST, PORT = "localhost", 9999
def create_client():
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
        cur_thread = threading.current_thread()
        sock.connect((HOST, PORT))
        for x in range(55):
            msg = "{}: {}\n".format(cur_thread.name, str(x))
            # Connect to server and send data
            print cur_thread.name + ": sending message\n"
            sock.send(msg)
            # Receive data from the server and shut down
            received = sock.recv(2048)
            print "RX:" + received
    finally:
        cur_thread = threading.current_thread()
        response = "{}: Closing!\n".format(cur_thread.name)
        print response
        sock.close()

if __name__ == "__main__":
    print "testing single thread"
    #create_client()
    print "starting threads"
    client_1 = threading.Thread(target=create_client)
    client_1.daemon = True
    client_1.start()
    client_2 = threading.Thread(target=create_client)
    client_2.daemon = True
    client_2.start()

    time.sleep(20)
like image 701
Adam Lewis Avatar asked Feb 23 '23 02:02

Adam Lewis


1 Answers

When you return from handle the socket is closed. Use a while loop and return from handle only when self.data == ''. recv returns zero bytes when the client closes the connection. Also don't strip() the result until after testing the return value or you could get a false close. Finally, use ThreadingTCPServer or the server can only handle one connection at a time.

Example:

import SocketServer

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while True:
            self.data = self.request.recv(1024)
            if self.data == '':
                break
            self.data = self.data.strip()
            print "{} wrote: {}\n".format(self.client_address[0], self.data)
            self.request.send(self.data.upper())

if __name__ == "__main__":
    HOST, PORT = "localhost", 9999
    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

Also note the send() is not guaranteed to send all bytes of message, so use sendall() or check the return value. recv() can also be tricky. TCP/IP is a streaming protocol and has no concept of message boundaries, so it is up to you to implement a protocol to check that you have received a complete message. It is possible to send 10000 bytes and receive less than that, requiring multiple receives to get the whole message. It is also possible to make two sends and receive both in one receive, or even all of one send and part of another. For your example simply buffering all receives until there is a \n in the message would do for a simple protocol.

like image 192
Mark Tolonen Avatar answered Feb 24 '23 15:02

Mark Tolonen