0

I'm currently working on an internship for a college subject in which I need to use two UDP sockets in order to send and receive video data from a computer.

To achieve this, I have created the following class called UDPConnection. It basically uses two UDP sockets, one for receiving and one for sending the data, along with two threads. The first one reads all the data received from the first UDP socket and stores it in a buffer, so that I can get it at any moment. The second one reads data from an output buffer and writes it on the sending socket, that way I just need to push data into the output socket in order to void possible blockages.

Both threads use Events to know whether they have to stop/resume.

The problem I have is that sometimes, there's a big delay (even more that 10 seconds with the computers at 20cm distance) between the data sent and received. When this delays occurs, the output buffer begins to fill, while almost no packages are received. It looks like the sending thread is not able to push the data stored on output buffer into the network at sufficient speed, as it even get Timeout exceptions, but as I'm using a UDP socket, I don't know why this could be happening. Isn't the socket supposed to push the data or discard it if it's unable to do so?

Maybe there's another error on my code that I have not notice?

I have checked all the events in order to make sure none of the threads are being paused or something, but they are working perfectly.

Here is my code (the functions used by the threads are _sendData and _rcvData):

class UDPConnection(object):
    def __init__(self, orIP, dstIp, orPort, dstPort, logger, maxTimeout = 2, rcvDataLen = 65535):
        super(UDPConnection, self).__init__()
        self.buffer = []
        self.outputBuffer = queue.Queue(maxsize=120)
        self.orIP = orIP
        self.dstIp = dstIp
        self.orPort = orPort
        self.dstPort = dstPort
        self.maxTimeout = maxTimeout
        self.logger = logger
        self.rcvDataLen = rcvDataLen

        self.pausedEvent = Event()
        self.closedEvent = Event()
        self.rcvThread = None
        self.sendThread = None

    # Initialize the connection and the send/receive threads
    def start(self):
        # Evento que indica que la conexion debe estar cerrada
        self.closedEvent.clear()
        # Inicializamos para que no se detengan los hilos
        self.pausedEvent.set()

        self.sendSocket = None
        self.rcvSocket = None
        # Receive socket - To receive information
        try:
            self.rcvSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.rcvSocket.bind((self.orIP, self.orPort))
            self.rcvSocket.settimeout(self.maxTimeout)
        except socket.error as e:
            self.logger.exception('UDPConnection - Receive socket {}:{}.'.format(self.orIP, self.orPort))
            return False

        time.sleep(2)
        self.logger.debug('UDPConnection - Escuchando en {}:{}'.format(self.orIP, self.orPort))

        self.rcvThread = Thread(target=self._rcvData)
        self.rcvThread.start()

        self.sendThread = Thread(target=self._sendData)
        self.sendThread.start()


        # Send socket - To send information
        try:
            self.sendSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            self.sendSocket.connect((self.dstIp, self.dstPort))
            self.sendSocket.settimeout(self.maxTimeout)
        except socket.error as e:
            self.logger.exception('UDPConnection - Send socket {}:{}.'.format(self.dstIp, self.dstPort))
            return False

        self.logger.debug('UDPConnection - Enviando en {}:{}'.format(self.orIP, self.orPort))
        return True

    # Pauses the connection
    def pause(self):
        self.pausedEvent.clear()

    # Resumes the connection
    def resume(self):
        self.pausedEvent.set()

    # Closes the connection
    def quit(self):
        # In case it's paused, we prevent it from getting stuck
        self.pausedEvent.set()

        self.closedEvent.set()
        time.sleep(self.maxTimeout) 
        self.sendSocket.close()
        self.rcvSocket.close()
        return True


    def isClosed(self):
        return self.closedEvent.is_set()

    def isPaused(self):
        return not self.pausedEvent.is_set()

    def isRunning(self):
        return self.pausedEvent.is_set() and not self.closedEvent.is_set()

    # Auxiliar function that pushes an object into the reception buffer.
    # Used for inherited classes
    def _bufferPush(self, data):
        self.buffer.append(data)

    # Auxiliar function that gets an element from the reception buffer.
    # Used for inherited classes
    def _bufferPop(self):
        r = self.buffer[0]
        del self.buffer[0]

    # Auxliar function that allows us to know the number of elements 
    # on the reception buffer. 
    def _bufferLen(self):
        bufLen = len(self.buffer)
        return bufLen

    # Function that empties the output buffer
    def _emptyOutputBuffer(self):
        while True:
            try:
                self.outputBuffer.get(False)
            except queue.Empty:
                break

    # Used to get data from the output socket and push it into the network.
    # If the connection is paused, it removes all the data from the output buffer and waits till it gets resumed

    def _sendData(self):
        while not self.isClosed():
            if self.isPaused():
                self._emptyOutputBuffer()

            self.pausedEvent.wait()
            try:
                data = self.outputBuffer.get(True, self.maxTimeout)
                self.sendSocket.send(data)
            except queue.Empty as e:
                continue
            except socket.error as e:
                # Socket timeout, perdemos el frame.
                print('Timeout while sending the frame')
                self._emptyOutputBuffer()
                continue

    # Function used to get data from the network and push it into the received buffer.
    def _rcvData(self):
        while not self.isClosed():
            # Comprobamos si la conexion esta o no parada
            self.pausedEvent.wait()

            try:
                request = self.rcvSocket.recv(self.rcvDataLen) 
                self._bufferPush(request)
            except socket.error as e:
                continue

    # Function used by the user to send data
    def send(self, data):
        if self.isRunning():
            if not self.outputBuffer.full():
                self.outputBuffer.put_nowait(data)

    # Function used by the user to receive data
    def rcv(self, timeout=5):
        start = time.time()
        bufLen = 0
        while (time.time()-start) < timeout:
            bufLen = self._bufferLen()
            if bufLen != 0:
                break

        if bufLen == 0:
            raise BlockingIOError('Timeout passed.')

        return self._bufferPop()

Then,

Could somebody help me please?

Thanks a lot!

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Javierd98
  • 708
  • 9
  • 27
  • Could you possibly switch to using TCP instead of UDP? That will give you all the benefits of things like slow start, transmit pacing, and the like. The lack of these things can cause you all kinds of problems. – David Schwartz May 12 '19 at 23:46
  • You don't need two sockets for this. You don't even need the sending queue or thread. And please don't call it t `UDPConnection`. This is a contradiction in terms. – user207421 Nov 03 '19 at 23:24

0 Answers0