Send and receive messages from an udp socket with python¶
This tutorial explains how to send and receive messages to and from an udp socket.
We will make a program that wait for messages and print them. If the message received is a PING message it answers with a PONG message. Furthermore it will have the option to send PING messages every two seconds.
The tutorial code will be written in the UDPTutorial
class so as to keep state information.
Creating the pprzlink.udp.UdpMessagesInterface
¶
First of all we need to create a pprzlink.udp.UdpMessagesInterface
object to handle sending and receiving data.
import pprzlink.messages_xml_map as messages_xml_map
self.udp = pprzlink.udp.UdpMessagesInterface(
self.proccess_msg, # Callback function
uplink_port = pong_port, # Port we send messages to
downlink_port = ping_port, # Port used to receive messages
interface_id = my_receiver_id # Numerical id of the interface (ac_id)
Here, we create an interface that will receive message on port ping_port
and send its messages toward port pong_port
. It will call self.proccess_msg()
when a message is received (see Receiving messages) and filter messages that are sent to id my_receiver_id
.
Receiving messages¶
When a message arrives on the udp port specified as downlink_port
in the creation of the interface, the id of the destination of the message is checked. If the interface_id
specified in the interface creation is the same as the destination id of the message or if it was specified as None
, then the callback function will be called.
# Function used as callback when messages are received
def proccess_msg(self,sender,address,msg,length,receiver_id=None, component_id=None):
print("Received message from %i %s [%d Bytes]: %s" % (sender, address, length, msg))
Here the callback function just prints the message.
Sending messages¶
To send a message, we just need to call the pprzlink.udp.UdpMessagesInterface.send()
function.
# Create a PING message
ping = message.PprzMessage('datalink', 'PING')
# Send PING message
self.udp.send(
ping, # The message to send
my_sender_id, # Our numerical id
remote_address, # The IP address of the destination
my_receiver_id # The id of the destination
)
Filtering messages on type¶
In order to filter the messages according to their type, we will use the pprzlink.message
API. It can be as simple as testing the name
attribute of the message.
if msg.name=="PING":
print("Received PING from %i %s [%d Bytes]" % (sender, address, length))
Complete file¶
The complete file for this tutorial including waiting for keyboard interuption and selecting who sends the PING is here
.
It can be tested by running it twice, one time without the -s switch and one with it.
#!/usr/bin/env python
import threading
import time
import pprzlink.udp
import pprzlink.messages_xml_map as messages_xml_map
import pprzlink.message as message
# Some constants of the program
ping_port = 2010 # The port to which the PING are sent
pong_port = 2011 # The port to which the PONG are sent
remote_address = "127.0.0.1"
my_sender_id = 1
my_receiver_id = 2
class UDPTutorial:
"""
Class UDPTutorial that uses udp.UdpMessagesInterface to listen to incoming messages.
If a PING message arrives, it will answer with a PONG message back to the sender.
It can also send PING every 2 seconds if constructed with the parameter ping_sender
to True.
"""
# Construction of the UDPTutorial object
def __init__(self,ping_sender = False):
self.ping_sender = ping_sender
if ping_sender:
# If we should send the pings, use ping_port as uplink_port (the port we send to)
# and pong_port as the downlink (the port we listen to)
self.udp = pprzlink.udp.UdpMessagesInterface(
self.proccess_msg, # Callback function
uplink_port = ping_port, # Port we send messages to
downlink_port = pong_port, # Port used to receive messages
interface_id = my_sender_id # Numerical id of the interface (ac_id)
)
else:
# If we should not send the pings, use pong_port as uplink_port (the port we send to)
# and ping_port as the downlink (the port we listen to)
self.udp = pprzlink.udp.UdpMessagesInterface(
self.proccess_msg, # Callback function
uplink_port = pong_port, # Port we send messages to
downlink_port = ping_port, # Port used to receive messages
interface_id = my_receiver_id # Numerical id of the interface (ac_id)
)
# Function used as callback when messages are received
def proccess_msg(self,sender,address,msg,length,receiver_id=None, component_id=None):
# If it is a PING send a PONG, else print message information
if msg.name=="PING":
print("Received PING from %i %s [%d Bytes]" % (sender, address, length))
pong = message.PprzMessage('telemetry', 'PONG')
print ("Sending back %s to %s:%d (%d)" % (pong,address[0],address[1],sender))
self.udp.send(pong, receiver_id, address[0], receiver = sender)
else:
print("Received message from %i %s [%d Bytes]: %s" % (sender, address, length, msg))
# Activity function of this object
def run(self):
try:
# Start the UDP interface
self.udp.start()
if self.ping_sender:
# Create a PING message
ping = message.PprzMessage('datalink', 'PING')
# Wait for a ^C
while True:
if self.ping_sender:
# Send PING message
print ("Sending %s to %s:%d (%d)" % (ping,remote_address,ping_port,my_receiver_id))
self.udp.send(
ping, # The message to send
my_sender_id, # Our numerical id
remote_address, # The IP address of the destination
my_receiver_id # The id of the destination
)
time.sleep(2)
except KeyboardInterrupt:
self.udp.stop()
if __name__ == '__main__':
from argparse import ArgumentParser
# Parse arguments looking for the -s switch telling us we should send the PING
parser = ArgumentParser(description="UDP Tutorial for pprzlink")
parser.add_argument("-s","--send_ping",dest="send",default=False, action='store_true', help="Send the PING messages")
args = parser.parse_args()
# Run the UDPTutorial
UDPTutorial(args.send).run()