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!