Question

I have this server draft that forks new children after a new client connection. Then depends on a client’s command child server does some work inside the function handler(connection).

In the meantime, I want to stop the parent server and before that let the parent wait for all working children.

The question is where shall I place this signal function for the Ctrl+C keyboard interrupt option. signal.signal(signal.SIGINT, signal_handler)

children_list = []
conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
conn.bind((HOST, PORT))
conn.listen(5)
print("Listening on TCP port %s" % PORT)

def reaper(pids):   

        while children_list:                        
        pid,stat = os.waitpid(0, os.WNOHANG)   

        if not pid: 

            break

        pids.remove(pid)


def handler(connection):                    

    cmd = connection.recv(socksize)

def signal_handler(signal, frame):
    print 'You pressed Ctrl+C!'
    sys.exit(0)

def accept():                                

while 1:   
        global connection                                  
          connection, address = conn.accept()
          print "welcome new client!"
        reaper(children_list)
          pid = os.fork()                     
          if pid:#parent
              children_list.append(pid)
              connection.close()

         else:#child    
               handler(connection)


accept()
Was it helpful?

Solution

The question is where shall I place this signal function for the Ctrl+C keyboard interrupt option. signal.signal(signal.SIGINT, signal_handler)

Anywhere you want, as long as it's after you define signal_handler and before you call accept.

I'm not sure that signal handler is actually what you want. What you're actually doing will, on most platforms, exit immediately, just as you're asking it to, causing the children to be reparented to init or launchd or similar. But you want to actually wait for all children. So, you can't call exit.

In POSIX, you're not allowed to call waitpid inside a signal hander. Python signal handlers aren't real signal handlers, so this might not be relevant—but I don't think that fact is guaranteed anywhere. If you want to take the risk, you could try waiting for all of the children right there in signal_handler before calling exit and see if it works on your platform.

However, the standard POSIX way to do it is to have the signal handler set some global flag and return. The main program will then wake up from accept or whatever other blocking call it's waiting on with an EINTR error, at which point it can check the flag—if true, time to wait on the children and quit; otherwise, go back into the loop and accept again.

But all of this shouldn't be necessary anyway. The default SIGINT handler should just raise a KeyboardInterrupt, which means that—if you don't install a custom signal handler—all you really need to do is catch that exception. In other words, just replace the last line with this:

try:
    accept()
except KeyboardInterrupt: # or maybe BaseException
    # wait for children, blocking instead of WNOHANG of course
    sys.exit(0)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top