Try/Except not catching UnboundLocalError on KeyboardInterrupt - python

I have a simple file transfer server that uses socket, It has an infinite listening to clients loop in the Main() func, so i surrounded it with Try/Except with KeyboardInterrupt so i would be able to properly close all the sockets and connections when CTRL+C-ing out
def Main():
try:
#various variable initations
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind((host,port))
print 'Socket bound to host - {0} and port {1}'.format(host,port)
sock.listen(5)
print 'Waiting for connections...'
while True:
conn, addr = sock.accept()
print 'Client IP:',str(addr)
#getting data from client and making the server do the appropriate functions
conn.close()
sock.close()
except(KeyboardInterrupt): # redundancy to make sure that a keyboard interrupt to close the program also closes the sockets and connections
conn.close()
sock.close()
print 'Manual Close'
sys.exit()
Now when a client connects and does whatever and i close it via keyboard interrupt it works fine, printing me the 'Manual Close'
But when i close via keyboardinterrupt before a client connects it gives me this error: UnboundLocalError: local variable 'conn' referenced before assignment
I understand the conn doesn't get assigned if a client doesn't connect but i thought that any errors under except get ignored

You can just put the functions within the except block inside another try/except and tell it to ignore the exception with pass
except(KeyboardInterrupt): # redundancy to make sure that a keyboard interrupt to close the program also closes the sockets and connections
try:
conn.close()
sock.close()
except:
pass
print 'Manual Close'
sys.exit()

Related

Getting a ConnectionResetError when client script is stopped on UDP socket

I am making a script that will allow multiple clients to see live camera footage from the server script, this all works fine until one of the client scripts is closed, then a ConnectionResetError is raised, To avoid this I used a try and except block to catch the ConnectionResetError but the same error is raised every time after a connection is lost. Using just socket.recv stops the ConnectionResetError but socket.recv does not return the senders address which is required for the script to send the video stream back to the client.
Server:
host = "0.0.0.0"
port = 5000
buffer_size = 1024
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(("", port))
listeners = list() # the addresses of the clients that want the video stream
def handle_queue(sock):
while True:
try:
message, address = sock.recvfrom(buffer_size) # block the thread until a packet arrives
print(address)
message = str(message, "utf-8") # decode the message
if message == "join":
listeners.append(address) # add the list of listeners
else:
print("unknown queue msg: ", message)
except ConnectionResetError:
print("The connection was forcefully quit")
queue_handler_thread = Thread(target=handle_queue, args=(sock,), daemon=True)
queue_handler_thread.start() # start the queue
the script then uses sock.sendto() for each address in the listeners list
Client:
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(bytes("join","utf-8"), (host, port))
while True:
data, address = sock.recvfrom(max_length) # block main thread until a packet is received
I believe what you are looking for is socket.getpeername().
This will return the remote address to which the socket is connected.
Your approach in handling the data in the wire whilst the connection is closed/lost is the right way. Handling via try/catch or using a recv() or recvfrom() method to wait for a response before closing the socket.
Since posting this i have discovered that it is infact TCP and this will not work for the project I am trying to do. Any more guidance and help would be appreciated, I cant seem to find any examples of Multiclient UDP Servers that run through one socket.
I partially resolved my system, after using the Example provided by AztCrw4282.
I was able to find a solution, I instead used the socket.accept() aproach, I am unsure as to whether this is UDP or TCP (I want UDP but with the connect system I think that is the handshake for TCP, but I am not 100% sure), but it works for now.
The client has to connect to the server then the server will accept or deny the connection, upon accepting a thread will be created for that clients connection to be managed. If any errors are thrown when interacting with that client their connection will be closed.
Server
try:
ServerSocket.bind((host, port))
except socket.error as e:
print(str(e))
print('Waiting for a Connection..')
ServerSocket.listen(5)
connections = list()
def threaded_client(connection):
connection.send(str.encode('Welcome to the Server\n'))
while True:
try:
data = str(connection.recv(2048),"utf-8") # this needs to be try catched
print("Packet Recv: ", data)
if data == "join":
print("Client Joined")
if data == "quit":
break
if not data:
break
except ConnectionResetError:
break
print("Closing a connection") # need to handle leaving the stream
connection.close()
def handle_stream():
for connection in connections:
try:
connection.send(bytes(json.dumps(frame_info) ,"utf-8"))
except:
print("Packet send failure, kicking client")
connections.remove(connection)
while True:
Client, address = ServerSocket.accept()
print('Connected to: ' + address[0] + ':' + str(address[1]))
connections.append(Client)
Thread(target=threaded_client, args=(Client, ), daemon=True).start()
ThreadCount += 1
print('Thread Number: ' + str(ThreadCount))
The only part that changes for the client is the part where it connects to the server
try:
ClientSocket.connect((host, port))
except socket.error as e:
print(str(e))

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.

Python - How to end a socket using a "with as" block

I'm learning about sockets and connections and trying to write a simple server Python script that echos a message to a client.
I began with just running a script that prints what it receives from a socket.
So i'm running what i wrote locally and using Putty as a client (so the message isn't printed to the putty session yet).
This is my code:
import socket
HOST = '127.0.0.1'
PORT = 65432
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT)) # bind accepts a tuple of a hostname or address and a port
s.listen()
conn, addr = s.accept() # returns a pair
with conn:
print("Connection started!", conn, addr)
data = conn.recv(1024)
while data:
data = data.decode("UTF-8")
print(data)
# if data == "exit":
# shutdown and close the connection,
# and ofcourse exit the two with as blocks gracefully
data = conn.recv(1024)
print("Connection is closed and the program continued")
Running this works but i have no way of terminating the connection other then killing the server.
I believe that data will always be true since i'm using putty and it seems that even when i'm hitting enter with no text to the connection window, it actually sends this:
b'\r\n'
So I tried placing this inside the commented if statement:
conn.shutdown(socket.SHUT_RDWR)
conn.close()
Hoping this will just make the socket be deleted - but it didn't work.
So what I want to do is to exit the with blocks and deleting the sockets without having to raise any exceptions or make the program stop. I tried doing so using python 3 change to socket that made it usable with with-as statements. I tried just calling the conn.__exit__ or s.__exit__ function but that didn't work also.
How can I close the socket and exit the two with blocks using an input from the user? Is there anything i'm confusing with the socket module here?

How to create a TCP Server in Python?

I started learning networking with python can somebody help me out with this code as I am unable to connect more than 5 clients at a time. Can someone please suggest me a solution for this?
def main():
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', listening_port))
s.listen(5)
print "[*] Initializing Sockets ... Done"
print "[*] Sockets Binded Successfully ..."
print("[*] Server Started Successfully [%d]\n" % (listening_port))
except Exception, e:
print e
sys.exit(2)
while 1:
try:
conn, addr = s.accept()
data = conn.recv(buffer_size)
start_new_thread(conn_string, (conn, data, addr))
except KeyboardInterrupt:
s.close()
print "\n[*] Proxy Server Shutting Down ..."
sys.exit(1)
s.close()
def conn_string(conn, data, addr):
print conn
print addr
print data
As described in the python socket API:
socket.listen(backlog)
Listen for connections made to the socket. The
backlog argument specifies the maximum number of queued connections
and should be at least 0; the maximum value is system-dependent
(usually 5), the minimum value is forced to 0.
Increase the number from 5 to the number of simultaneous connections you wish to have to the server.
The accepted answer is incorrect. I'll try to explain you why.
listen takes an argument of queued connections. That means, how many new connections can be put into the queue. That's why even in the documentation it's specified 5, while obviously servers serves more than 5 concurrent clients usually.
Once a socket is created (accepted) it is being forwarded to another thread, which performs the actual work. This allows to the listening thread moves back to listening mode and wait for more clients.
The problem with your code is, your main thread which is the listening thread actually reads data from buffer, where it shouldn't. The recv() function blocks (read more about I/O stream blocking)
You need to move the reading process from the listening thread to the worker; and make sure to close() the socket when it's done. The code would look something like this:
def main():
try:
...
s.listen(5)
except Exception, e:
...
while 1:
try:
conn, addr = s.accept()
start_new_thread(conn_string, (conn, addr))
except KeyboardInterrupt:
s.close()
print "\n[*] Proxy Server Shutting Down ..."
sys.exit(1)
s.close()
def conn_string(conn, addr):
data = conn.recv(buffer_size)
print conn
print addr
print data
conn.close()

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