Skip to content
Advertisement

How to send and receive data the same time Python UDP socket

I just learned about #python recently, I have done a small project about rasberry pi. I want to transmit and receive data between Server is Laptop, client is Ras via UDP protocol, I use Python’s socket library. Transmit sensor data from ras to laptop, and transmit control commands from laptop to ras. Data from ras must always be transmitted, so I used a while true loop with time.sleep. The problem is again at this point, the control command from the laptop is not always sent, only sent when necessary, I receive the control command on Ras by recvfrom() is also in the while True loop, so when not transmitting the control command is the while true loop stuck here, so the data from the sensor cannot be transmitted anymore. m.n help me give advice or keywords for this article. Thank you.

server.py

import socket
import time
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
ip = ""
port = 5001
server_address = (ip, port)
s.bind(server_address)
while True:
    print("####### Server is listening #######")
    data, address = s.recvfrom(4096)
    print("nn 2. Server received: ", data.decode('utf-8'), "nn")
    s.sendto(send_data.encode('utf-8'), address)
    print("nn 1. Server sent : ", send_data, "nn")
    time.sleep(1)

cilent.py

import time
import socket
import random
UDP_IP = ""
UDP_PORT = 5001
s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM, 0)
while True:
    roll = random.randrange(1,100)
    a = str(roll)
    s.sendto(a.encode('utf-8'), (UDP_IP, UDP_PORT))
    print("data gui:",str(roll).encode('utf-8'))
    data, address = s.recvfrom(4096)
    print("data nhan",data)
    time.sleep(1)

Advertisement

Answer

If the sending of the data from the RasPi to the PC is a fairly independent thing that must always run, you could consider making a separate thread of execution for it so you can sleep() in there and generally do whatever you want without interfering with receiving occasional commands that you must respond to. There’s a great description of threads here but it might look like this:

#!/usr/bin/env python3

import sys
import time
import random
import logging
import threading, queue
 
def Transmitter():
    """Transmits readings to PC at 1 second intervals on a separate thread"""
    logging.debug(f'[Transmitter] Starting')

    # Start independent loop sending values
    i = 0
    while True:
       reading = random.randint(0,100)
       logging.debug(f'[Transmitter] Iteration: {i}, reading: {reading}')
       i += 1
       time.sleep(1)

if __name__ == '__main__':

    # Set up logging - very advisable with threaded code
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)s %(message)s')

    # Create independent thread to transmit readings
    thr = threading.Thread(target=Transmitter, args=())
    thr.start()

    # Main loop - waiting for commands from PC
    logging.debug('[Main] Starting main loop')
    i = 0
    while True:
       # Not what you want but just showing the other thread is unaffacted by sleeping here
       time.sleep(5)

       i += 1
       logging.debug(f'[Main] Iteration: {i}')

Here’s the output from a sample run:

DEBUG [Transmitter] Starting
DEBUG [Transmitter] Iteration: 0, reading: 82
DEBUG [Main] Starting main loop
DEBUG [Transmitter] Iteration: 1, reading: 84
DEBUG [Transmitter] Iteration: 2, reading: 45
DEBUG [Transmitter] Iteration: 3, reading: 47
DEBUG [Transmitter] Iteration: 4, reading: 97
DEBUG [Main] Iteration: 1
DEBUG [Transmitter] Iteration: 5, reading: 81
DEBUG [Transmitter] Iteration: 6, reading: 20
DEBUG [Transmitter] Iteration: 7, reading: 6
DEBUG [Transmitter] Iteration: 8, reading: 16
DEBUG [Transmitter] Iteration: 9, reading: 54
DEBUG [Main] Iteration: 2
DEBUG [Transmitter] Iteration: 10, reading: 67
DEBUG [Transmitter] Iteration: 11, reading: 91
DEBUG [Transmitter] Iteration: 12, reading: 37

Likewise, if you don’t want to hang/block on the RasPi waiting for commands from the PC, you could start another thread that sits in a tight loop, doing blocking reads from the UDP command port. It could then put the commands into a Python Queue for the main program to read whenever it wants, and it can do the read with a timeout which means the main thread doesn’t block when there are no incoming commands. Good description here, but the code might look something like this:

#!/usr/bin/env python3

import sys
import time
import random
import logging
import threading, queue

def Transmitter():
    """Transmits readings to PC at 1 second intervals"""
    logging.debug(f'[Transmitter] Starting')

    # Start independent loop sending values
    i = 0
    while True:
       reading = random.randint(0,100)
       logging.debug(f'[Transmitter] Iteration: {i}, reading: {reading}')
       i += 1
       time.sleep(1)

def Receiver(Q):
    """Waits for commands and queues them to the main process"""
    logging.debug(f'[Receiver] Starting')

    # Wait for commands from PC and place into queue
    while True:
       # We will actually wait a random number of seconds and then synthesize dummy command
       time.sleep(random.randint(3,7))
       cmd = f'command_{random.randint(100,200)}'
       logging.debug(f'[Receiver] Synthesizing cmd: {cmd}')
       Q.put(cmd)

if __name__ == '__main__':

    # Set up logging - very advisable with threaded code
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)s %(message)s')

    # Create independent thread to transmit readings
    tx = threading.Thread(target=Transmitter, args=())
    tx.start()

    # Create independent thread to receive commands and send to us via queue
    Q = queue.Queue()
    rx = threading.Thread(target=Receiver, args=(Q,))
    rx.start()

    # Main loop - processing commands from queue
    logging.debug('[Main] Starting main loop')
    msgNum = 0
    while True:
       # Wait for a message from queue...
       # ... you can either block like I am here
       # ... or use timeout and not block
       cmd = Q.get()
       msgNum += 1
       logging.debug(f'[Main] Received cmd: {cmd} {msgNum}')

Note: I don’t believe Python sockets are thread-safe, so you would probably want to send and receive on different ports with this approach – but that shouldn’t be a problem as there are 65,000 ports.

Advertisement