Home > Software design >  How to detect a closed client for UDP sockets?
How to detect a closed client for UDP sockets?

Time:12-27

I'm transmitting some live data from a camera to a client over ethernet where i'm using a python socket and opencv to transmit the images. It works fine and i can read the video at the client side.

The issue is that when i start the server that listens for users that want the feed and a socket stream is started, then after i close the connection at the client side the server side keeps transmitting the data. Furthermore, after closing the client side connection i can only reconnect by restarting the server.

I've though about implementing a kind of heartbeat protocol by sending a message from client to server for every 100th image i recieve to tell i'm still listening - and if not recieved then go back to the loop to listen for a new connection. The only issue is that the recvfrom is non-blocking so a direct implementation into the while loop would probably not work.

How can i detect that a/the client isn't reading the data anymore?

Here is my server side:

import cv2, imutils, socket 
import numpy as np
import time 
import base64 

BUFF_SIZE = 65536
server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, BUFF_SIZE)
host_name = socket.gethostname()

host_ip = 'xxx.xxx.xxx.xxx'
port = 4001
socket_address = (host_ip, port)
server_socket.bind(socket_address)
print("listening at", socket_address)

vid = cv2.VideoCapture(0)    
WIDTH=480

while True:
    msg,client_addr = server_socket.recvfrom(BUFF_SIZE)
    print(client_addr)
    while (vid.isOpened()):
        print("sending img")
        _,frame = vid.read()
        frame = imutils.resize(frame,width=WIDTH)
        encoded,buffer = cv2.imencode(".jpg",frame,[cv2.IMWRITE_JPEG_QUALITY,80])
        message = base64.b64encode(buffer)
        print(len(message))
        server_socket.sendto(message,client_addr)
        key = cv2.waitKey(1) & 0xFF
        if key == ord('q'):
                server_socket.close()

Here is my client side:

def video_socket_stream_client():

    BUFF_SIZE = 65536
    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, BUFF_SIZE)
    host_name = socket.gethostname()

    host_ip = 'xxx.xxx.xxx.xxx'
    port = 4001

    client_socket.sendto("connect".encode(),(host_ip, port)) # send signal



    while True:

        packet,_ = client_socket.recvfrom(BUFF_SIZE)
        data = base64.b64decode(packet,' /')
        npdata = np.frombuffer(data,dtype=np.uint8)
        frame = cv2.imdecode(npdata,1)
        time.sleep(0.016)
        yield(b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n'   data   b'\r\n')
 

CodePudding user response:

This is one of the tradeoffs with UDP vs TCP. With UDP, there is no long-term connection. The sender just blasts data and hopes someone is listening. The listener just picks up the phone and hopes someone is talking. There is no persistent state. If the sender stops sending, the listener will not hear anything. If you need to maintain a connection, then you need to use TCP.

You shouldn't ever have to restart the server. Neither side knows what the other side is doing. I wonder if you're getting bit by not using SO_REUSEADDR.

CodePudding user response:

The running status of the server can be judged by the return value of getattr(socket,'_closed').True is closed state, False is running.

e.g.

import socket

ip = 'localhost'
port = 5003

ws = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ws.bind((ip, port))
ws.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
ws.listen()

# 关闭服务
# ws.close()

print("The status is as follows:")
if(getattr(ws, '_closed') == False):
    print("Running")
elif(getattr(ws, '_closed') == True):
    print("closed")

You can try.

CodePudding user response:

You could have the client send a "goodbye" type of packet to the server just before the client exits, to let the server know the client is going away, but of course you can't rely on that packet actually making it to the server (the packet might get dropped, or the client might crash or get physically disconnected from the network or otherwise go away in an uncontrolled fashion), so in the end you'll need to implement some kind of heartbeat protocol.

In order to send() and recv() simultaneously, you can use non-blocking I/O and select(), or you can use multiple threads. I prefer the select() approach, but either way can be made to work. You might also consider upgrading your server's logic to support multiple clients simultaneously rather than just one client-at-a-time; even if you don't actually need to send to multiple clients at once, that would allow you server to handle the case where the user quits the client and then starts a new one (i.e. the server would be able to start sending packets to the new client even before it has timed-out the old/dead client).

  • Related