Hi
The UDP demo in the demo project shows the method of setting up a UDP socket on a particular port (which is necessary to receive UDP frames) and sending data. The demo waits for a UDP frame to be received and then sends it back (echo). Although the demo doesn't generate any frames of its own, the method for sending is the same if it were to.
Here is a more complete guide to the use of UDP.
1. When no UDP socket is available
If there is no UDP socket opened on a particular port, all received UDP frames on that port are discarded. However it is possible to cause an ICMP DESTINATION UNREACHABLE to be returned (this happens irrespective of UDP support) by configuring #define ICMP_DEST_UNREACHABLE in the USE_ICMP configuration group in config.h.
Note that the ICMP destination unreachable is often disabled on PCs by default since it is considered a security risk - it confirms that there is a PC/device at the destination IP address, even if there is no UDP based service running on it.
2. Opening a UDP socket
A UDP socket must be set to listening state for UDP frames to be received.
When the UDP demo starts it creates a UDP socket and binds it as shown by the following code:
if (!((MyUDP_Socket = fnGetUDP_socket(TOS_MINIMISE_DELAY, fnUDPListner, (UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS))) < 0)) {
fnBindSocket(MyUDP_Socket, MY_UDP_PORT); // Bind socket
ptrUDP_Frame = uMalloc(sizeof(UDP_MESSAGE)); // get some memory for UDP frame
}
else {
return; // no socket - this must never happen (ensure that enough user UDP sockets have been defined - USER_UDP_SOCKETS in config.h)!!
}
USOCKET fnGetUDP_socket(unsigned char ucTOS, int (*fnListener)(USOCKET, unsigned char, unsigned char *, unsigned short, unsigned char *, unsigned short), unsigned char ucOpts);
the first parameter TOS_MINIMISE_DELAY can in fact be ignored - it is not used.
fnUDPListner() is the UDP call back routine which is called when a UDP frame is received or other event occur relevant to the socket.
(UDP_OPT_SEND_CS | UDP_OPT_CHECK_CS) are options which define whether the UDP checksum of transmitted frames is generated and whether the UDP check sum of received frames is checked. UDP allows frames to be sent with or without check sums.
static USOCKET MyUDP_Socket; is the socket used for communicating
#define MY_UDP_PORT 9999 // Test UDP port
fnBindSocket(MyUDP_Socket, MY_UDP_PORT); This call binds the new socket to a port number.
In the demo project the port number is defined to be 9999, but it could of course be a variable value.
As the comment says, ensure that there are enough UDP sockets available in the UDP socket pool. For each socket required by the application increase the value of USER_UDP_SOCKETS in config.h.
From this point on, the UDP socket is in listening for received farmes and any activity will result in the call back being executed.
3. Receiving UDP events
// UDP data server - reception call back function
//
extern int fnUDPListner(USOCKET SocketNr, unsigned char ucEvent, unsigned char *ucIP, unsigned short usPortNr, unsigned char *data, unsigned short usLength)
{
switch (ucEvent) {
case UDP_EVENT_RXDATA:
//if (usPortNr != MY_UDP_PORT) break; // ignore false ports
//if (uMemcmp(ucIP, ucUDP_IP_Address, IPV4_LENGTH)) break; // ignore if not from expected IP address
//if (usLength <= UDP_BUFFER_SIZE) { // ignore frames which are too large
//uMemcpy(&ptrUDP_Frame->ucUDP_Message, data, usLength); // Send the received UDP frame back
//fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, usLength, OWN_TASK);
//}
fnSendUDP(MyUDP_Socket, ucIP, usPortNr, (data - sizeof(UDP_HEADER)), usLength, OWN_TASK); // echo back from transmitting IP and port
break;
case UDP_EVENT_PORT_UNREACHABLE: // we have received information that this port is not available at the destination so quit
break;
}
return 0;
}
There are in two possible events which can arrive:
UDP_EVENT_PORT_UNREACHABLE - this means that an ICMP DISTINATION UNREACHABLE frame was received which corresponds to the souce port of this UDP socket. Generally it will be returned if the user sends a UDP frame to a destination which does not (or no longer has) a listening UDP socket operating.
UDP_EVENT_RXDATA - this means that a UDP frame has been received.
ucIP is a pointer to the IP address fo the sender
usPortNr is the port number of the sender
data is a pointer to the data in the UDP frame
usLength is the length of the UDP data
As shown in the example (where the comments can be changed to suit) this allows various things to be quite simply performed:
if (usPortNr != MY_UDP_PORT) break; // this will reject UDP frames from destinations which are not using the same UDP port number as their source.
if (uMemcmp(ucIP, ucUDP_IP_Address, IPV4_LENGTH)) break; // this will reject any data from IP addresses not equal to
static unsigned char ucUDP_IP_Address[IPV4_LENGTH] = {192, 168, 0, 102};
This is used as a form of trusted IP address.
fnSendUDP(MyUDP_Socket, ucIP, usPortNr, (data - sizeof(UDP_HEADER)), usLength, OWN_TASK); // This is the simplest form of echo message. It sends the received data back to the destination port and IP address without copying it from the receive Ethernet frame buffer. The reason for the pointer shift of sizeof(UDP_HEADER) is due to the fact that the data is passed with space for the UDP header which the UDP transmission routine uses when constructing the UDP frame. This should become clear in the next section.
4. Transmission of UDP frames
fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, usLength, OWN_TASK);
This is the general transmission method. The destintion UDP port number and destination IP address determine where the UDP frame will be sent to. MY_UDP_PORT is passed since it is also required in the UDP frame.
The data is passed in this example as a struct, where the data length is defined (usLength) and a reference to the sending task OWN_TASK is also given.
In the demo the following struct is defined for sending a UDP frame.
typedef struct stUDP_MESSAGE
{
unsigned short usLength;
UDP_HEADER tUDP_Header; // reserve header space
unsigned char ucUDP_Message[UDP_BUFFER_SIZE]; // reserve message space
} UDP_MESSAGE;
This defines the message buffer as consisting of a UDP_HEADER plus UDP data, where the buffer size UDP_BUFFER_SIZE is adequate for the application use.
When UDP data is sent, the data content is set to the buffer ucUDP_Message. When sent, the complete object is passed so that the UDP transmission routine can simply fill it out with the necessary UDP header and send it on to the IP layer. This results in improved efficiency since the UDP frame construction routine doesn't have to copy any UDP data content. It does however mean that the user must always pass the header buffer space - which is easily done when such a struct is used.
5. UDP frame retransmissions
UDP is a connectionless protocol and has also no retransmission at the UDP layer. This means that it is not a secure protocol unless a higher layer is responsible for resending lost data if it has detected a communication problem.
However there are cases where transmission is not possible and a repeat of the data must be foreseen. This is due to the ARP process and can be handled as follows - this is also built into the UDP demo code in application.c as reference.
Consider the following transmission case:
static UDP_MESSAGE *ptrUDP_Frame = uMalloc(sizeof(UDP_MESSAGE)); // get some memory for UDP frame
const unsigned char test_data[] = {1,2,3,4,5,6,7,8};
uMemcpy(&ptrUDP_Frame->ucUDP_Message, test_data, sizeof(test_data)); // Send the received UDP frame back
fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, sizeof(test_data), OWN_TASK);
Here a test frame is put to the UDP buffer and send using fnSendUDP().
Note that the UDP frame buffer has been created on the heap and maintains a backup of this frame. This is important if the data is to be retransmitted at a later point in time.
Although the return value of fnSendUDP() is not being checked, it can have the following values:
0 means that no data could be transmitted due to a problem with the Ethernet interface. In this case the data has been lost - it is an exceptional case which is generally not expected.
> 0 means that this amount of data was transmitted (including IP and UDP headers). It means that the transmission to the destination address via the Ethernet interface was successful but it doesn't mean that the UDP frame will ever arrive. However, if there is no higher level protocol being executed the job has in fact been completed.
INVALID_SOCKET_HANDLE will be returned if the socket is invalid or
SOCKET_CLOSED if the socket is not bound.
INVALID_DEST_IP will be returned if the user tries to send to the IP address 0.0.0.0.
INVALID_REMOTE_PORT if the user tries to send to the destination UDP port number 0 or
ZERO_PORT if the user source port is declared as 0.
NO_ARP_ENTRY will be returned if the IP frame (consisting of IP and UDP header plus UDP data) could not be sent due to a missing MAC <-> IP entry in the ARP table. This is not an error but a natural occurance in an Ethernet LAN, which starts the ARP resolve process. ARP sends a broadcast request to attempt to resolve the destination IP address in the local network, which can be either successful or fail. Since this ARP resolve process can take some time the result is sent to the sending task (remember the parameter OWN_TASK) as soon as it is known. In the case of the fail it can take several seconds until ARP actually gives up and declares the process as a failed attempt.
The demo project example shows how the ARP result messages are processed:
case TASK_ARP:
fnRead( PortIDInternal, ucInputMessage, ucInputMessage[MSG_CONTENT_LENGTH]); // read the contents
switch (ucInputMessage[ 0 ]) { // ARP sends us either ARP resolution success or failed
case ARP_RESOLUTION_SUCCESS: // IP address has been resolved (repeat UDP frame).
fnSendUDP(MyUDP_Socket, ucUDP_IP_Address, MY_UDP_PORT, (unsigned char*)&ptrUDP_Frame->tUDP_Header, UDP_BUFFER_SIZE, OWN_TASK);
break;
case ARP_RESOLUTION_FAILED: // IP address could not be resolved...
break;
}
break;
This is in the task's input queue read, where the ARP task is identified as the message source.
The message type ARP_RESOLUTION_FAILED informs that the destination IP address is not reachable on the network. In this demo example nothing is undertaken but the message could be of interest in an application to generate a user message informing of the reason for the failure.
The ARP_RESOLUTION_SUCCESS message informs that the destination IP address has now been resolved and a retransmission of the original message should now be performed. This retransmission is not necessarily an application layer retranmission but is rather necessary to be performed due to the ARP process which can and will take place in practice. This means that its handling MUST be resolved if UDP transmission is to operate correctly.
As can be seen the backed up UDP frame is simply resent, which will in this case almost certainly be successful.
Note that in an application where a lot of UDP data is to be transmitted it is probably best to not allow further transmissions to be attempted until the ARP resolution process has completed. This will avoid having to back up lots of UDP data frames. If several tasks are sending UDP frames, each task can use the same mechanism but will have to back up its own message in each case.
If you have studied the TCP implementation you will see that the handling of the ARP RESOLUTION is resolved by the TCP_EVENT_REGENERATE event in the TCP call back routine, This call back routine has however been dispatched from the same ARP message reception in the TCP task.
That is about all that is needed to be known to be able to successfully use UDP in the project.
Good luck!!
Regards
Mark