0% found this document useful (0 votes)
14 views12 pages

Unit IV-ElementaryUDPSocket2015

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
14 views12 pages

Unit IV-ElementaryUDPSocket2015

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 12

Unit VI

Elementary UDP SOCKET


recvfrom and sendto Functions

These two functions are similar to the standard read and write functions, but three
additional arguments are required.

#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buff, size_t nbytes, int flags, struct sockaddr *from,
socklen_t *addrlen);
ssize_t sendto(int sockfd, const void *buff, size_t nbytes, int flags, const struct
sockaddr *to, socklen_t addrlen);

Both return: number of bytes read or written if OK, 1 on error

The first three arguments, sockfd, buff, and nbytes, are identical to the first three arguments
for read and write: descriptor, pointer to buffer to read into or write from, and number of bytes to
read or write.
The to argument for sendto is a socket address structure containing the protocol address
(e.g., IP address and port number) of where the data is to be sent. The size of this socket address
structure is specified by addrlen.
The recvfrom function fills in the socket address structure pointed to by from with the
protocol address of who sent the datagram. The number of bytes stored in this socket address
structure is also returned to the caller in the integer pointed to by addrlen.
Note that the final argument to sendto is an integer value, while the final argument to
recvfrom is a pointer to an integer value (a value-result argument).
The final two arguments to recvfrom are similar to the final two arguments to
accept: The contents of the socket address structure upon return tell us who sent the datagram (in
the case of UDP) or who initiated the connection (in the case of TCP).
The final two arguments to sendto are similar to the final two arguments to connect:
the socket address structure is filled with the protocol address of where to send the datagram (in
the case of UDP) or with whom to establish a connection (in the case of TCP). Both functions
return the length of the data that was read or written as the value of the function. In the typical
use of recvfrom, with a datagram protocol, the return value is the amount of user data in the
datagram received. Writing a datagram of length 0 is acceptable. In the case of UDP, this results
in an IP datagram containing an IP header (normally 20 bytes for IPv4 and 40 bytes for IPv6), an
8-byte UDP header, and no data. This also means that a return value of 0 from recvfrom is
acceptable for a datagram protocol: It does not mean that the peer has closed the connection, as
does a return value of 0 from read on a TCP socket. Since UDP is connectionless, there is no
such thing as closing a UDP connection. If the from argument to recvfrom is a null pointer, then
the corresponding length argument ( addrlen) must also be a null pointer, and this indicates that
we are not interested in knowing the protocol address of who sent us data.
UDP Echo Server: main Function
Figure 1. Simple echo client/server using UDP.

UDP echo server


1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4{
5 int sockfd;
6 struct sockaddr_in servaddr, cliaddr;
7 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
8 bzero(&servaddr, sizeof(servaddr));
9 servaddr.sin_family = AF_INET;
10 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
11 servaddr.sin_port = htons(SERV_PORT);
12 Bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
13 dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
14 }
Create UDP socket, bind server's well-known port
Create a UDP socket by specifying the second argument to socket as SOCK_DGRAM (a
datagram socket in the IPv4 protocol). As with the TCP server example, the IPv4 address for
the bind is specified as INADDR_ANY and the server's well-known port is the constant
SERV_PORT The function dg_echo is called to perform server processing.
dg_echo function: echo lines on a datagram socket.
1 #include "unp.h"
2 void
3 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
4{
5 int n;
6 socklen_t len;
7 char mesg[MAXLINE];
8 for ( ; ; ) {
9 len = clilen;
10 n = Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
11 Sendto(sockfd, mesg, n, 0, pcliaddr, len);
12 }
13 }
Read datagram, echo back to sender
This function is a simple loop that reads the next datagram arriving at the server's port
using recvfrom and sends it back using sendto. Despite the simplicity of this function, there are
numerous details to consider. First, this function never terminates. Since UDP is a connectionless
protocol, there is nothing like an EOF as we have with TCP.
Next, this function provides an iterative server, not a concurrent server as we had with
TCP. There is no call to fork, so a single server process handles any and all clients. In general,
most TCP servers are concurrent and most UDP servers are iterative. There is implied
queuing taking place in the UDP layer for this socket. Indeed, each UDP socket has a receive
buffer and each datagram that arrives for this socket is placed in that socket receive buffer. When
the process calls recvfrom, the next datagram from the buffer is returned to the process in a first
in, first-out (FIFO) order. This way, if multiple datagrams arrive for the socket before the process
can read what's already queued for the socket, the arriving datagrams are just added to the socket
receive buffer. But, this buffer has a limited size. ( Discussed this size and how to increase it
with the SO_RCVBUF socket option ) .
UDP Echo Client: main Function
The UDP client main function is shown

1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4{
5 int sockfd;
6 struct sockaddr_in servaddr;
7 if(argc != 2)
8 err_quit("usage: udpcli <IPaddress>");
9 bzero(&servaddr, sizeof(servaddr));
10 servaddr.sin_family = AF_INET;
11 servaddr.sin_port = htons(SERV_PORT);
12 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
13 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
14 dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
15 exit(0);
16 }
Fill in socket address structure with server's address
An IPv4 socket address structure is filled in with the IP address and port
number of the server. This structure will be passed to dg_cli, specifying
where to send datagrams. A UDP socket is created and the function dg_cli
is called.

UDP Echo Client: dg_cli Function


The function dg_cli, which performs most of the client processing.

1 #include "unp.h"
2 void
3 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
4{
5 int n;
6 char sendline[MAXLINE], recvline[MAXLINE + 1];
7 while (Fgets(sendline, MAXLINE, fp) != NULL) {
8 Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
9 n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
10 recvline[n] = 0; /* null terminate */
11 Fputs(recvline, stdout);
12 }
13 }
There are four steps in the client processing loop:

 read a line from standard input using fgets


 send the line to the server using sendto
 read back the server's echo using recvfrom,
 and print the echoed line to standard output using fputs.

Our client has not asked the kernel to assign an ephemeral port to its socket. (With a TCP client,
we said the call to connect is where this takes place.) With a UDP socket, the first time the
process calls sendto, if the socket has not yet had a local port bound to it, that is when an
ephemeral port is chosen by the kernel for the socket. As with TCP, the client can
call bind explicitly, but this is rarely done. Notice that the call to recvfrom specifies a null
pointer as the fifth and sixth arguments. This tells the kernel that we are not interested in
knowing who sent the reply. There is a risk that any process, on either the same host or some
other host, can send a datagram to the client's IP address and port, and that datagram will be read
by the client, who will think it is the server's reply. As with the server function dg_echo, the
client function dg_cli is protocol-independent, but the client main function is protocol-
dependent. The main function allocates and initializes a socket address structure of some
protocol type and then passes a pointer to this structure, along with its size, to dg_cli.

Lost Datagrams
Our UDP client/server example is not reliable. If a client datagram is lost (say it is discarded by
some router between the client and server), the client will block forever in its call to recvfrom in
the function dg_cli, waiting for a server reply that will never arrive. Similarly, if the client
datagram arrives at the server but the server's reply is lost, the client will again block forever in
its call to recvfrom. A typical way to prevent this is to place a timeout on the client's call to
recvfrom.
Just placing a timeout on the recvfrom is not the entire solution. For example, if we do time out,
we cannot tell whether our datagram never made it to the server, or if the server's reply never
made it back. If the client's request was something like "transfer a certain amount of money from
account A to account B" (instead of our simple echo server), it would make a big difference as to
whether the request was lost or the reply was lost. So required to add reliability to a UDP
client/server.
Summary of UDP Example
Figure 1 shows as bullets the four values that must be specified or chosen when the client
sends a UDP datagram.
Figure 1. Summary of UDP client/server from client's perspective.

The client must specify the server's IP address and port number for the call to sendto.
Normally, the client's IP address and port are chosen automatically by the kernel, although
we mentioned that the client can call bind if it so chooses. If these two values for the client
are chosen by the kernel, we also mentioned that the client's ephemeral port is chosen
once, on the first sendto, and then it never changes. The client's IP address, however, can
change for every UDP datagram that the client sends, assuming the client does not bind a
specific IP address to the socket. The reason is shown in Figure 8.11: If the client host is
multihomed, the client could alternate between two destinations, one going out the datalink
on the left, and the other going out the datalink on the right. In this worst-case scenario, the
client's IP address, as chosen by the kernel based on the outgoing datalink, would change
for every datagram. What happens if the client binds an IP address to its socket, but the
kernel decides that an outgoing datagram must be sent out some other datalink? In this
case the IP datagram will contain a source IP address that is different from the IP address of
the outgoing datalink.

Figure 2. Summary of UDP client/server from server's perspective.


There are at least four pieces of information that a server might want to know from an
arriving IP datagram: the source IP address, destination IP address, source port number, and
destination port number. Figure 3 shows the function calls that return this information for a
TCP server and a UDP server.

Figure 3. Information available to server from arriving IP datagram

Lack of Flow Control with UDP


 Examine the effect of UDP not having any flow control. First, modify dg_cli function to
send a fixed number of datagrams. It no longer reads from standard input. Figure 1 shows
the new version. This function writes 2,000 datagrams of length 1,400-byte UDP
datagrams to the server.
 Next modify the server to receive datagrams and count the number received. This server
no longer echoes datagrams back to the client. Figure 2 shows the new dg_echo function.
When we terminate the server with terminal interrupt key (SIGINT), it prints the number
of received datagrams and terminates.

Figure 1 dg_cli function that writes a fixed number of datagrams to


the server.
1 #include "unp.h"
2 #define NDG 2000 /* datagrams to send */
3 #define DGLEN 1400 /* length of each datagram */
4 void
5 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
6{
7 int i;
8 char sendline[DGLEN];
9 for (i = 0; i < NDG; i++) {
10 Sendto(sockfd, sendline, DGLEN, 0, pservaddr, servlen);
11 }
12 }

Figure 2 dg_echo function that counts received datagrams.


1 #include "unp.h"
2 static void recvfrom_int(int);
3 static int count;
4 void
5 dg_echo(int sockfd, SA *pcliaddr, socklen_t clilen)
6{
7 socklen_t len;
8 char mesg[MAXLINE];
9 Signal(SIGINT, recvfrom_int);
10 for ( ; ; ) {
11 len = clilen;
12 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
13 count++;
14 }
15 }
16 static void
17 recvfrom_int(int signo)
18 {
19 printf("\nreceived %d datagrams\n", count);
20 exit(0);
21 }
Now run the server on the host freebsd, a slow SPARCStation. Run the client on the
RS/6000 system aix, connected directly with 100Mbps Ethernet. Additionally, Run netstat
-s on the server, both before and after, as the statistics that are output tells how many datagrams
were lost. Figure 3 shows the output on the server.

Figure 3 Output on server host.


freebsd % netstat -s -p udp
udp:
71208 datagrams received
0 with incomplete header
0 with bad data length field
0 with bad checksum
0 with no checksum
832 dropped due to no socket
16 broadcast/multicast datagrams dropped due to no socket
1971 dropped due to full socket buffers
0 not for hashed pcb
68389 delivered
137685 datagrams output

freebsd % udpserv06 start our server


Run the client here
^C type interrupt key after the client is finished
received 30 datagrams
freebsd % netstat -s -p udp
udp:
73208 datagrams received
0 with incomplete header
0 with bad data length field
0 with bad checksum
0 with no checksum
832 dropped due to no socket
16 broadcast/multicast datagrams dropped due to no socket
3941 dropped due to full socket buffers
0 not for hashed pcb
68419 delivered
137685 datagrams output

The client sent 2,000 datagrams, but the server application received only 30 of these, for a 98%
loss rate. There is no indication whatsoever to the server application or to the client application
that these datagrams were lost. As said, UDP has no flow control and it is unreliable. It is trivial,
as shown, for a UDP sender to overrun the receiver. If looked at the netstat output, the total
number of datagrams received by the server host (not the server application) is 2,000 (73,208 -
71,208). The counter "dropped due to full socket buffers" indicates how many datagrams were
received by UDP but were discarded because the receiving socket's receive queue was full.This
value is 1,970 (3,491 - 1,971), which when added to the counter output by the application (30),
equals the 2,000 datagrams received by the host. Unfortunately, the netstat counter of the number
dropped due to a full socket buffer is systemwide. There is no way to determine which
applications (e.g., which UDP ports) are affected.
The number of datagrams received by the server in this example is not predictable. It depends on
many factors, such as the network load, the processing load on the client host, and the
processing load on the server host. If run the same client and server, but this time with the
client on the slow side and the server on the faster RS/6000, no datagrams are lost.

aix % udpserv06
^? we type our interrupt key after the client is finished
received 2000 datagrams

Determining Outgoing Interface with UDP


A connected UDP socket can also be used to determine the outgoing interface that will be used
to a particular destination. This is because of a side effect of the connect function when applied
to a UDP socket: The kernel chooses the local IP address (assuming the process has not already
called bind to explicitly assign this). This local IP address is chosen by searching the routing
table for the destination IP address, and then using the primary IP address for the resulting
interface. Figure 1 shows a simple UDP program that connects to a specified IP address and then
calls getsockname, printing the local IP address and port.
Figure 1 UDP program that uses connect to determine outgoing interface.

1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4{
5 int sockfd;
6 socklen_t len;
7 struct sockaddr_in cliaddr, servaddr;
8 if (argc != 2)
9 err_quit("usage: udpcli <IPaddress>");
10 sockfd = Socket(AF_INET, SOCK_DGRAM, 0);
11 bzero(&servaddr, sizeof(servaddr));
12 servaddr.sin_family = AF_INET;
13 servaddr.sin_port = htons(SERV_PORT);
14 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
15 Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
16 len = sizeof(cliaddr);
17 Getsockname(sockfd, (SA *) &cliaddr, &len);
18 printf("local address %s\n", Sock_ntop((SA *) &cliaddr, len));
19 exit(0);
20 }

If the program is runned on the multihomed host freebsd, the following output:
freebsd % udpcli09 206.168.112.96
local address 12.106.32.254:52329
freebsd % udpcli09 192.168.42.2
local address 192.168.42.1:52330
freebsd % udpcli09 127.0.0.1
local address 127.0.0.1:52331.

The first time the program run, the command-line argument is an IP address that follows the
default route. The kernel assigns the local IP address to the primary address of the interface to
which the default route points.
The second time, the argument is the IP address of a system connected to a second Ethernet
interface, so the kernel assigns the local IP address to the primary address of this second
interface. Calling connect on a UDP socket does not send anything to that host; it is entirely a
local operation that saves the peer's IP address and port. Can see that calling connect on an
unbound UDP socket also assigns an ephemeral port to the socket. Unfortunately, this technique
does not work on all implementations, mostly SVR4-derived kernels. For example, this does not
work on Solaris 2.5, but it works on AIX, HP-UX 11, MacOS X, FreeBSD, Linux, and Solaris
2.6 and later.

You might also like