BHelman
BHelman

Reputation: 43

Python Socket - handling client disconnect

I've done a lot of research into working with the Sockets class in Python, but I'm having a difficult time understanding how to achieve a solution to my problem. Let me also be clear by saying that I have absolutely no experience with the Python language. The code that I've scraped together is from several other examples. It mainly works, I just have one last bug to figure out.

The idea is this script will run on a raspberry pi and that will be the Pi's sole purpose. Via TCP, you will request an image number, and the Pi will display that particular image full screen. Easy, right?

Again, everything works, except when the ethernet connection to the client is cut, or the client closes the connection. The script then enters a bad infinite loop, the CPU usage maxes out, and I have no choice but to physically power cycle the Pi. I need a way to ensure this won't happen and make a provision for the client to be able to reestablish a connection to this server in the event of a disconnect/network glitch/etc.

Here's what I have so far.

import sys
import pygame
import SocketServer

pygame.init()
screen = pygame.display.set_mode((0,0), pygame.FULLSCREEN, 0)
pygame.mouse.set_visible(False)
imagesequence = []
imagesequence.append(pygame.image.load("/home/pi/appfiles/image1.png")
imagesequence.append(pygame.image.load("/home/pi/appfiles/image2.png")

screensaver = []
screensaver.append(pygame.image.load("/home/pi/appfiles/screensaver1.png")

class MyTCPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        while True:
            self.data = self.rfile.readline().strip()

            isNum = False

            try:
                int(self.data)
                isNum = True
            except ValueError:
                isNum = False

            if isNum:
                framenumber = int(self.data)
                if framenumber >= 1 and framenumber <= len(imagesequence):
                    screen.blit(imagesequence[framenumber-1], (0,0))
                    pygame.display.flip()
                elif framenumber >= 901 and framenumber <= 900+len(screensaver):
                    screen.blit(screensaver[framenumber-901], (0,0))
                    pygame.display.flip()
            else:
                if self.data == "q":
                    pygame.quit()
                    server.shutdown()

if __name__ == "__main__":
    HOST, PORT = "192.168.1.51", 5050

    SocketServer.TCPServer.allow_reuse_address = True
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)

    server.serve_forever()

Quitting the script via TCP connection also doesn't work, but I'm not so concerned with that. Eventually, there will never be a reason to quit this.

Upvotes: 4

Views: 8757

Answers (2)

Mark Tolonen
Mark Tolonen

Reputation: 178419

self.rfile.readline() returns an empty string '' when the socket connection is closed. Make sure to check it before applying .strip(), though, since sending a whitespace-only line will look like an empty string after stripping whitespace.

import socketserver

class MyTCPHandler(SocketServer.StreamRequestHandler):
    def handle(self):
        while True:
            self.data = self.rfile.readline()
            if not self.data:
                break
            self.data = self.data.strip()
            ...

Upvotes: 2

Landon
Landon

Reputation: 180

After relentless code digging, I cannot answer the first part. It looks like there is no way at this high level for you to detect a socket closing. Look into the (out of favor, I think, but perfectly usable) asyncore and asynchat modules.

As for the second part, you need to break from the loop on receiving a 'q'.

Tongue in cheek code sample:

break

Upvotes: 0

Related Questions