Bruce
Bruce

Reputation: 35213

How to keep a socket open until client closes it?

I have simple python server and client.

Server:

import SocketServer
import threading


class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print str(self.client_address[0]) + " wrote: "
        print self.data
        self.request.send(self.data.upper())


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

Client:

import socket
import sys
from time import sleep

HOST, PORT = "localhost", 3288
data = "hello"

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

try:
    sock.connect((HOST, PORT))
    sock.send(data + "\n")
    received = sock.recv(1024)

    sleep(10)

    sock.send(data + "\n")
    received = sock.recv(1024)

    sleep(10)

    sock.send(data + "\n")
    received = sock.recv(1024)

finally:
    sock.close()

Here is the output I get:

Server:

>python server.py
127.0.0.1 wrote:
hello

Client:

>python client.py
Traceback (most recent call last):
  File "client.py", line 18, in <module>
    received = sock.recv(1024)
socket.error: [Errno 10053] An established connection was aborted by the software in your host machine

I tried it on a linux machine as well. The server only receives one message and then I get an error on the recv statement of second message. I have just started learning networking on python but I think the server is closing the socket for some reason. How do I correct this?

Upvotes: 21

Views: 44671

Answers (3)

Mark Tolonen
Mark Tolonen

Reputation: 177461

A MyTcpHandler object is created for each connection, and handle is called to deal with the client. The connection is closed when handle returns, so you have to handle the complete communication from the client within the handle method:

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while 1:
            self.data = self.request.recv(1024)
            if not self.data:
                break
            self.data = self.data.strip()
            print str(self.client_address[0]) + " wrote: "
            print self.data
            self.request.send(self.data.upper())

NOTE: recv returns '' when the client closes the connection, so I moved .strip() after the recv so there is no false alarm due to the client sending only white space.

Upvotes: 33

user2290820
user2290820

Reputation: 2749

TO a similar problem here error: [Errno 10053]

I also tried the same thing and got the same error.

If there is a simple code like this to demonstrate this error:

import socket 

host = 'localhost' 
port = 5001 
size = 102400
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
s.connect((host,port))
for msg in ['Hello, world','Test','anything goes here']:
    s.send(msg)
    data = s.recv(size)
    print 'Received:', data 
s.close()  

If you create a socket object and the amt it can send and echo back from server to see how much it receivers, if you vary that, say 1024 to 102400(in this code); Which means the socket should not get closed but again in my Windows OS, the server side keeps listening and printing any data that client sends but on the Client side you get this error;

However if it is that the client can connect only once and send and receive only once, then that is how it was designed. Trying this works without any errors:

for msg in ['Hello, world','Test','anything goes here']:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.connect((host,port))
    s.send(msg)
    data = s.recv(size)
    s.close()
    print 'Received:', data

I am not sure if one socket object works only once to send and recieve data.

UPDATE I think the issue was the capacity per client socket to receive data as per the buffersize fixed; That's why the second code snippet above works thus creating new client connection sockets on the server. But that way lots of sockets are going to get used up.

Instead the following code fixed that problem by checking the amt of size being used up. If it exceeds the given amount, it creates a new socket at clients' but makes sure the message is sent; Actually the problem was with the server code but fixed it.

size = 10

This is a quick baby attempt at the code. I am sure you would understand and optimize it for the better!

client code:

messag = ['Hello, world', 'Test', 'anything goes here']

def client_to_server(messag,host,port,size):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((host, port))
    countmsg = 0
    restmsg = ''
    for msg in messag:
        strl = tmsg = msg
        if len(restmsg):
            tmsg = restmsg + ' ' + msg
        countmsg = len(tmsg)
        if countmsg <= size:
            pass
        else:
            restmsg = tmsg[size:]
            tmsg = tmsg[:size]
            #s.close()
            countmsg = len(tmsg)
            #s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
            #s.connect((host, port))
        print 'Sending to server msg {}'.format(tmsg)
        s.send(tmsg)
        # s.settimeout(1)
        try: 
            data = s.recv(size)
            print 'Received:', data
            if strl == data:
                print strl,data
                countmsg = 0
                restmsg = ''
        except (socket.error), e: 
            print e.args,e.message
            s.close()
            s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
            s.connect((host, port))
    s.close()
    if restmsg: 
        client_to_server([restmsg],host,port,size)
    return


client_to_server(messag,host,port,size)

Server Code:

size = 1024  #This has to be bigger than client buf size!
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((host, port)) 
s.listen(backlog)
while True:
        #this is what accepts and creates a P2P dedicated client socket per socket
        client, address = s.accept()
        try: 
            data = client.recv(size)
            while data or 0:
                print "Client sent {} | Server sending data to client address {}".format(data, address)
                client.send(data)
                data = client.recv(size)
            else: client.close()
        except (socket.error), e: 
            client.close()
            print e.args, e.message

Try it out. This uses the same socket.

Upvotes: 0

sarnold
sarnold

Reputation: 104020

I'll first admit that it's been years since I last used SocketServer, so there might be more idiomatic approaches to solve your problem.

Note that your client opens a single connection and sends three sets of data and receives three sets of data. (Hopefully the TCP stack will send buffered data once you call receive() on the socket.)

Your server is expecting to handle a client connection completely, from start to finish, when it is called from the SocketServer callback mechanism. Your current class does a little bit of IO and then quits. You just need to extend your server callback to do more:

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.data = self.request.recv(1024).strip()
        print str(self.client_address[0]) + " wrote: "
        print self.data
        self.request.send(self.data.upper())
        foo = self.request.recv(1024).strip()
        self.request.send(foo.lower())
        bar = self.request.recv(1024).strip()
        self.request.send("goodbye " + bar)

Upvotes: 1

Related Questions