How to handle KeyboardException raised in threading module? - python

I'm creating a server using the socket and threading module. The server allows for multiple connections to be made. I've been doing some error handling (as shown in the code below). However, there is one error that I cannot figure out how to handle.
Code (server.py):
import socket
import threading
# import pyaudio
# import json
from os import system
system('clear')
host = socket.gethostbyname(socket.gethostname())
port = 5000
buffer_size = 2048
connected_clients = {}
# Thread for listening and accepting new connections
def client_listener():
while True:
try:
# Accepts new client
connection, address = server_socket.accept()
# Creates a new thread for each connected client
handle_client_thread = threading.Thread(target=handle_client, args=(connection, address))
handle_client_thread.start()
except ConnectionAbortedError:
pass
except OSError:
pass
except KeyboardInterrupt:
pass
# Handles each connected client in a separate thread
def handle_client(client, client_address):
try:
# Receives room number
room_number = client.recv(buffer_size).decode('utf-8')
print(f'\nConnection from {client_address[0]}:{client_address[1]} ({room_number})')
# Adds new client to 'connected_clients'
connected_clients[room_number] = client
print(connected_clients)
while True:
data = client.recv(buffer_size).decode('utf-8')
# If no data is received, close the socket
if not data:
print(f'{client_address[0]}:{client_address[1]} ({room_number}) Disconnected')
connected_clients.pop(room_number)
break
# Print received data
else:
print(f'{client_address[0]} ({room_number}): {data}')
except KeyboardInterrupt:
client.close()
connected_clients.pop(room_number)
with socket.socket() as server_socket:
try:
# Prevents [Errno 48: Address already in use]
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Binds socket to host and port
server_socket.bind((host, port))
server_socket.listen()
print(f'Server hosted on {host}:{port}')
# Starts new thread for listening for new clients
client_listener_thread = threading.Thread(target=client_listener, name=client_listener)
client_listener_thread.start()
# Prevents main thread from closing
while True:
pass
except socket.error as error_message:
print(str(error_message))
except KeyboardInterrupt:
print(': Closing server...\n')
pass
The error message is raised when I close the server with a keyboard interrupt (^C). When I first press ^C, the server is closed. However, the second ^C seems to raise an exception within the threading module.
Error message:
Server hosted on 10.108.249.108:5000
^C: Closing server...
^CException ignored in: <module 'threading' from '/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py'>
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1273, in _shutdown
t.join()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1032, in join
self._wait_for_tstate_lock()
File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/threading.py", line 1048, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
How would I go about handling this KeyboardInterrupt in a seamless way such that the program is closed without errors on a single ^C? The desired result would be having the server close the connections with each connected client, close the threads, then finally shut down the server itself.

When you press Ctrl-C on your keyboard while your script is running on the console, you send a SIGINT to the script process. You can use the signal python module to handle such signals:
import signal
import sys
def my_sig_handler(sig, frame):
print('Received interrupt signal')
sys.exit(0)
signal.signal(signal.SIGINT, my_sig_handler) # Register your signal handler for SIGINT
Once you do this, any time your running script receives an interrupt signal, my_sig_handler will be called. There are several kinds of signals defined in Linux, and it is generally a good idea to add signal handlers in your code for some of the common ones, so that it can clean up and exit gracefully.
More information about signals here.

Related

How to implement signal handling?

I am trying to develop a TCP server which listens to a port and implements signals to make sure it shuts down after a preconfigured duration. I am using a Windows 10 machine and after execution I am getting an error. Here is my code:
import socket
import signal
import sys
# create the signal handler
def SigAlarmHandler(signal, frame):
print("Received alarm... Shutting Down server...")
sys.exit(0)
signal.signal(signal.SIGALRM, SigAlarmHandler)
signal.alarm(100)
print("Starting work... waiting for quiting time...")
while True:
# define the target host and port
host = '127.0.0.1'
port = 444
# define and create the server socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# bind the server to incoming client connection
server.bind((host, port))
# start the server listener
server.listen(3)
# establish connection with the client
while True:
client_socket, address = server.accept()
print("connection received from %s" % str(address))
message = 'thank you for connecting to the server' + "\r\n"
client_socket.send(message.encode('ascii'))
client_socket.close()
pass
Here is the error:
Traceback (most recent call last):
File "C:/Users/user/Desktop/pythons/tcpserver.py", line 19, in <module>
signal.signal(signal.SIGALRM, SigAlarmHandler)
AttributeError: module 'signal' has no attribute 'SIGALRM'
Notice that signal.SIGALRM is only available on Unix.
Since you are using a Windows machine notice what is stated on the signal documentation:
On Windows, signal() can only be called with SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM, or SIGBREAK. A ValueError will be raised in any other case. Note that not all systems define the same set of signal names; an AttributeError will be raised if a signal name is not defined as SIG* module level constant.

How to bypass the GIL and handle the KeyboardInterrupt?

I'm wanting to exit an UDP-server on KeyboardInterrupt, but this is not working immediately. when doing some research I stumbled on this question, where someone suggests that it is a issue with the GIL. Python processes calling into C APIs can block the Python interpreter from processing the interrupt. is there any way the unblock this in python?
the code I have is the following:
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt: #works after new data is send to server
print('Interrupted', file=sys.stderr)
try:
sys.exit(0)
except SystemExit:
os._exit(0)
main:
def main():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 2000)
s.bind(server_address)
running = True
print('Ready for connection')
while running:
try:
msg = ""
data, address = s.recvfrom(2000) #look for timeout and interrupt
msg = data.decode('utf-8')
if not data:
break
print(f'received {msg}')
if msg[:5] == "PING-":
print('sending PONG back to the client')
message = 'PONG-' + msg[5:len(msg)]
s.sendto(message.encode('utf-8'), address)
else:
print('bad format')
except socket.error as socketerror:
print(f'Error: {socketerror}', file=sys.stderr)
s.close()
When i'm not running the client and press ctrl-c nothing happens, the program just does nothing.
When I start up the client and send a UDP message, the interrupt is perfectly handled when the server gets a UDP message from the client. The UDP message then is dropped and the client handles it as a timeout. After the timeout the client has a lost connection.
The goal is to get the server exit even without the client running and without using ctrl-break.

Keyboard Interrupt not exiting cleanly

I am having sockets code in which I am handling clients through threads. The use case is like this,
1. Server runs and clients start connecting to it.
2. After their interaction clients issue exit message and server closes the client connection.
3. However what i want is in case something happens on server and if i manually hit ctrl-c on server it should exit all threads and main program as well.
Below is what i was trying but unfortunately not everything gets cleaned up and i have to hit ctrl-c twice.
import socket
import sys
from threading import Thread
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# This is to prevent the socket going into TIME_WAIT status and OSError
# "Address already in use"
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except socket.error as e:
print('Error occured while creating the socket {}'.format(e))
server_address = ('localhost', 50000)
sock.bind(server_address)
print('**** Server started on {}:{} ****'.format(*server_address))
sock.listen(5)
def client_thread(conn_sock, client_add):
while True:
client_msg = conn_sock.recv(1024).decode()
if client_msg.lower() != 'exit':
print('[{0}:{1}] {2}'.format(*client_add, client_msg))
serv_reply = 'Okay ' + client_msg.upper()
conn_sock.send(bytes(serv_reply, 'utf-8'))
else:
conn_sock.close()
print('{} exitted !!'.format(client_add[0]))
sys.exit()
try:
# Keep the server until there are incominmg connections
while True:
# Wait for the connctions to accept
conn_sock, client_add = sock.accept()
print('Recieved connection from {}:{}'.format(*client_add))
conn_sock.send(
bytes('***** Welcome to {} *****'.format(server_address[0]), 'utf-8'))
Thread(target=client_thread, args=(conn_sock, client_add)).start()
except Exception as e:
print('Some error occured \n {}'.format(e))
except KeyboardInterrupt as e:
print('Program execution cancelled by user')
sys.exit(0)
finally:
sock.close()
Below is the execution trace of my code
**** Server started on localhost:50000 ****
Recieved connection from 127.0.0.1:35910
[127.0.0.1:35910] ,msdasda
^CProgram execution cancelled by user
^CException ignored in: <module 'threading' from '/apps/nttech/rbhanot/anaconda3/lib/python3.6/threading.py'>
Traceback (most recent call last):
File "/apps/nttech/rbhanot/anaconda3/lib/python3.6/threading.py", line 1294, in _shutdown
t.join()
File "/apps/nttech/rbhanot/anaconda3/lib/python3.6/threading.py", line 1056, in join
self._wait_for_tstate_lock()
File "/apps/nttech/rbhanot/anaconda3/lib/python3.6/threading.py", line 1072, in _wait_for_tstate_lock
elif lock.acquire(block, timeout):
KeyboardInterrupt
So can anyone suggest how can i close everything when keyboardInterrupt occures.
I got it working by making the threads as daemon, so that main program will exit on receiving the KeyboardInterrupt and threads will keep on running in background.

How to send data through TCPServer from another thread

I'm writing a server in Python, which should basicaly work as a pipe between some serial device (like Arduino) and any client connected to it.
import SocketServer
import threading
import errno
def SerialThread():
# Serial init is here
while (True):
msg = read_from_serial(blocking = True)
#print(msg)
try:
server.socket.sendall(str(msg))
except IOError, e:
if e.errno == errno.EPIPE:
print('Server not ready')
serial_daemon = threading.Thread(name='SerialThread', target=SerialThread)
serial_daemon.daemon = True
serial_daemon.start()
class TCPHandler(SocketServer.StreamRequestHandler):
def handle(self):
print('#')
server = SocketServer.TCPServer(('127.0.0.1', 4242), TCPHandler)
print('Starting server')
server.serve_forever()
But I keep getting Server not ready spammed in my console. So what's wrong with my code, and how can I fix it?
TCPServer.socket is in "listen" mode, you can not use it to trans data directly, you need "connect" to its address with a new socket object:
import socket
while 1:
s = socket.create_connection(('127.0.0.1', 4242))
s.sendall(str(msg))
depends on how your server handle connection, you could reuse the same socket or create a new one every loop.

Python long-lived socket connection weirdness

I've implemented some code that allows a client to connect to a socket server, introduces itself and the server then goes into an infinite loop which sends "commands" (strings) to the client from a Redis list. The server uses the Redis 'blpop' method to block until a string arrives which is then sent off to the client and the response awaited.
However, in testing (with a python client socket script on another local workstation) I find that if I break the client connection (Ctrl+c) to simulate an interruption in the connectivity, the server happily writes the next received string to the client, reports an empty response but ONLY throws the broken pipe exception when a second string is written :/ Thus, two writes are "lost" before anything is caught. Here's my code:
# Create global Redis resource
rds_cnx = redis.StrictRedis(host='localhost', port=6379, db=6)
def initialise_server():
""" Setup server socket """
try:
srv_skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv_skt.bind((IP, PORT))
srv_skt.listen(1)
print("Listening on:[{}]".format(IP, PORT))
return srv_skt
except socket.error as skt_err: # e.g. port in use
print("Could not initialise tcp server:[{}]".format(skt_err))
sys.exit(1)
except Exception as exp:
print("Unable to setup server socket:[{}]".format(exp))
sys.exit(1)
def main():
server_socket = initialise_server()
while True:
client_socket, remote_address = server_socket.accept()
try:
# Block and wait for connection and data
initial_data = client_socket.recv(1024).decode()
print("Connection from [{}] - Data:[{}]".format(remote_address, initial_data))
while True:
wait_for_queue_command(client_socket)
except (BrokenPipeError, socket.error, Exception) as sck_exp:
print("Exception in client loop:[{}]".format(sck_exp))
continue
except KeyboardInterrupt:
# Close client socket
client_socket.shutdown(2)
client_socket.close()
print('Caught Ctrl+c ... Shutting down.')
break
# Tear down context
server_socket.shutdown(2) # Param ref: 0 = done receiving, 1 = done sending, 2 = both
server_socket.close()
def wait_for_queue_command(client_skt):
""" Blocking while waiting for command for Redis list
:param client_skt: socket
:return: None
"""
print('Waiting for command...')
queue_cmd = rds_cnx.blpop('queuetest', 0)
print("Received something from the queue:")
pprint(queue_cmd)
try:
#client_skt.settimeout(15)
client_skt.send(queue_cmd[1])
# Block for response
response_data = client_skt.recv(1024).decode()
print("Response:[{}]".format(response_data))
except BrokenPipeError as brkn_p:
print('Outbound write detected "Broken Pipe":[{}]'.format(brkn_p))
''' Here one would decide to either re-schedule the command or
ignore the error and move on to the next command. A "pause"
(sleep) could also useful?
'''
raise
except socket.timeout as sck_tmo:
print('Socket timed out:[{}]'.format(sck_tmo))
except socket.error as sck_err:
print('Socket timed out:[{}]'.format(sck_err))
raise
print('Command handling complete.')
Is there any better way to handle such a situation? I've had a cursory look at Twisted but it seems very difficult to achieve the specific blocking behavior and other code that might be implemented to handle specific responses from the client.

Categories

Resources