Let\'s say I start a thread to receive on a port. The socket call will block on recvfrom. Then, somehow in another thread, I close the socket.
On Windows, this will
When the socket is closed, I want recvfrom to unblock
recvfrom() is a function specific to UDP sockets on Python. Here's a brief summary of how I solved the problem using an idea referred to as "polling" (try running the program as well, the print statements will give you a solid idea of whats going on):
import socket
import threading
import signal
import time
# Custom class to create a socket, close the socket, and poll the socket
class ServerSocket():
def __init__(self, addresses):
# "Standard" way to create and preapare a working socket
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.bind(addresses)
def poll(self):
self.socket.settimeout(2)
modifiedMsg, senderAddress = self.socket.recvfrom(1024)
def close(self):
self.socket.close()
class ServiceExit(Exception):
"""
Custom exception which is used to trigger the clean exit
of all running threads and the main program.
"""
pass
def service_shutdown(signum, frame):
raise ServiceExit
# Custom class to create a UDP server on a separate thread.
# This server will know to close the blocking UDP socket when the user
# of the main program signals termination via typing CTRL-C into the terminal
class Server(threading.Thread):
def __init__(self, addresses):
threading.Thread.__init__(self)
self.mysocket = ServerSocket(addresses)
# This flag below will help us determine when to stop the "run" loop below
# The while loop below is where interrupt the blocking recvfrom() call by
# timing out every 2 seconds and checking to see if the flag has been set
# to discontinue the while loop
self.shutdown_flag = threading.Event()
def run(self):
while not self.shutdown_flag.is_set():
try:
print('socket blocking')
self.mysocket.poll()
except socket.timeout:
print('socket unblocked')
pass
# as a final step, we close the socket
self.mysocket.close()
print('socket closed')
def main():
# assign the methods that will be called when our main program receives a SIGTERM or SIGINT signal
# You can send this main problem such a signal by typing CTRL-C after you run this program
signal.signal(signal.SIGTERM, service_shutdown)
signal.signal(signal.SIGINT, service_shutdown)
# Start the server thread that will eventually block on recvfrom()
try:
print('starting udp server thread')
udp_server = Server(('localhost', 5000))
udp_server.start()
while True:
time.sleep(0.5)
# This server will accept UDP packets on the local host at port 5000
# Feel free to change these settings to fit your needs
except ServiceExit:
print('shutting down server thread')
udp_server.shutdown_flag.set()
udp_server.join()
print('server thread shut down')
if __name__ == '__main__':
main()