8.13 Lack of Flow Control with
UDP
We now examine the effect of UDP not having any
flow control. First, we modify our dg_cli function to send
a fixed number of datagrams. It no longer reads from standard
input. Figure 8.19 shows
the new version. This function writes 2,000 1,400-byte UDP
datagrams to the server.
We next modify the server to receive datagrams
and count the number received. This server no longer echoes
datagrams back to the client. Figure 8.20 shows the new dg_echo
function. When we terminate the server with our terminal interrupt
key (SIGINT), it prints the number of received datagrams
and terminates.
Figure 8.19
dg_cli function that writes a fixed number of datagrams to
the server.
udpcliserv/dgcliloop1.c
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 8.20
dg_echo function that counts received datagrams.
udpcliserv/dgecholoop1.c
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 }
We now run the server on the host
freebsd, a slow SPARCStation. We run the client on the
RS/6000 system aix, connected directly with 100Mbps
Ethernet. Additionally, we run netstat -s on the server,
both before and after, as the statistics that are output tell us
how many datagrams were lost. Figure 8.21 shows the output on the server.
Figure 8.21
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
we run the client here
^C we type our 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 we have said, UDP has no flow control and
it is unreliable. It is trivial, as we have shown, for a UDP sender
to overrun the receiver.
If we look 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 (p. 775 of TCPv2). 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 we run the same client and server, but this
time with the client on the slow Sun 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
|
|
UDP Socket Receive Buffer
The number of UDP datagrams that are queued by
UDP for a given socket is limited by the size of that socket's
receive buffer. We can change this with the SO_RCVBUF
socket option, as we described in Section 7.5. The
default size of the UDP socket receive buffer under FreeBSD is
42,080 bytes, which allows room for only 30 of our 1,400-byte
datagrams. If we increase the size of the socket receive buffer, we
expect the server to receive additional datagrams. Figure 8.22 shows a modification to
the dg_echo function from Figure 8.20 that sets the socket receive buffer to
240 KB.
Figure 8.22
dg_echo function that increases the size of the socket
receive queue.
udpcliserv/dgecholoop2.c
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 int n;
8 socklen_t len;
9 char mesg[MAXLINE];
10 Signal(SIGINT, recvfrom_int);
11 n = 220 * 1024;
12 Setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n));
13 for ( ; ; ) {
14 len = clilen;
15 Recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
16 count++;
17 }
18 }
19 static void
20 recvfrom_int(int signo)
21 {
22 printf("\nreceived %d datagrams\n", count);
23 exit(0);
24 }
If we run this server on the Sun and the client
on the RS/6000, the count of received datagrams is now 103. While
this is slightly better than the earlier example with the default
socket receive buffer, it is no panacea.
Why do we set the receive socket buffer size to
220 x 1,024 in Figure
8.22? The maximum size of a socket receive buffer in FreeBSD
5.1 defaults to 262,144 bytes (256 x 1,024), but due to the buffer
allocation policy (described in Chapter 2 of TCPv2), the actual
limit is 233,016 bytes. Many earlier systems based on 4.3BSD
restricted the size of a socket buffer to around 52,000 bytes.
|