2

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
    
Ollie Pugh
  • 373
  • 2
  • 15

2 Answers2

1

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.

AzyCrw4282
  • 7,222
  • 5
  • 19
  • 35
  • Could it be that I am communicating to multiple clients through one socket? Because once one Client disconnects, the error is thrown every time the code loops, the only way to fix this is to close the socket, but then I can not communicate with all the other clients that are still connected. – Ollie Pugh Aug 05 '20 at 23:08
  • I am unsure of that to be honest since I haven't worked with sockets for a while, but I know for sure that a single socket can handle multiple connections. You can see [this](https://stackoverflow.com/a/43526140/6505847) on how you can open multiple sockets connections and see if that fixes. Can you also not disconnect a client when the error is thrown ,i.e. remove their `address` from server? Also see [this](https://codezup.com/socket-server-with-multiple-clients-model-multithreading-python/) as an example – AzyCrw4282 Aug 05 '20 at 23:24
  • Unfortunately that example is TCP – Ollie Pugh Aug 06 '20 at 15:29
0

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))
Ollie Pugh
  • 373
  • 2
  • 15