Writing a Datagram Program
A peer-to-peer program has two roles, a sender and a receiver. The sender transmits the message, and the receiver accepts the message. In its simplest form, the two roles are separated into two source listings. More complicated forms often use multitasking to help split the roles while maintaining one code base. This section shows you how to write a simple transmitter/receiver and assumes that you know C fairly well.
As in TCP programs, UDP uses the standard C system call socket(). The socket() system call returns a socket descriptor (an index into the various I/O channels to which the program is connected), through which you can send and receive datagram messages. Unlike TCP sockets, the socket descriptor is not like a file descriptor, because file descriptor functions require streaming connections.
The sender's initialization looks like this:
#include <sys/socket.h> #include <sys/types.h> #include <resolv.h> int sd; struct sockaddr_in addr; sd = socket(PF_INET, SOCK_DGRAM, 0);/* create datagram socket */ memset(&addr, 0, sizeof(addr)); /* clear dest's addr */ addr.sin_family = AF_INET; /* indicate IP */ addr.sin_port = htons(DEST_PORT); /* set dest's port number */ inet_aton(DEST_ADDR, &addr.sin_addr); /* set dest's address */
Every network program, like this one, uses the socket() system call. This code fragment shows you how to create the socket and populate the destination address structure, addr. In filling the structure, the example uses the library call htons() to help with converting the byte-orders. This library call is from the abbreviation "host to network short," meaning that it converts two bytes in the right order for network use. For a complete list of system and library calls, see Linux Socket Programming (Sams Publishing, 2001, ISBN 0-672-31935-7).
After creating the socket and getting the address ready, the next step is sending a message. POSIX-compliant operating systems include a large set of tools to work with sockets. Two tools for sending and receiving messages are sendto() and recvfrom(). Expanding the fragment above, you add a sendto() system call:
/* send the data in buf (buflen bytes) to dest at addr */ sendto(sd, buf, buflen, &addr, sizeof(addr));
Of course, these code fragments don't include any form of error checking—which you may want to do for program integrity. Some really nasty defects can come from not checking the return codes of system calls that interface with the network.