[ Team LiB ] Previous Section Next Section

Chapter 8

8.1

Yes, read returns 4,096 bytes of data, but the recvfrom returns 2,048 (the first of the two datagrams). A recvfrom on a datagram socket never returns more than one datagram, regardless of how much the application asks for.

8.2

If the protocol uses variable-length socket address structures, clilen could be too large. We will see in Chapter 15 that this is acceptable with Unix domain socket address structures, but the correct way to code the function is to use the actual length returned by recvfrom as the length for sendto.

8.4

Running ping like this is an easy way to see ICMP messages that are received by the host on which ping is being run. We reduce the number of packets sent from the normal one per second just to reduce the output. If we run our UDP client on our host aix, specifying the server's IP address as 192.168.42.1, and also run the ping program, we get the following output:

aix % ping -v -i 60 127.0.0.1
PING 127.0.0.1: (127.0.0.1): 56 data bytes
64 bytes from 127.0.0.1: icmp_seq=0 ttl=255 time=0 ms
36 bytes from 192.168.42.1: Destination Port Unreachable
Vr HL TOS  Len   ID Flg  off TTL Pro  cks      Src      Dst Data
 4  5  00 0022 0007   0 0000  1e  11 c770 192.168.42.2  192.168.42.1
UDP: from port 40645, to port 9877 (decimal)

Note that not all ping clients print received ICMP errors, even with the -v flag.

8.5

It probably has a socket receive buffer size, but data is never accepted for a listening TCP socket. Most implementations do not preallocate memory for socket send buffers or socket receive buffers. The socket buffer sizes specified with the SO_SNDBUF and SO_RCVBUF socket options are just upper limits for that socket.

8.6

We run the sock program on the multihomed host freebsd, specifying the -u option (use UDP) and the -l option (specifying the local IP address and port).

freebsd % sock -u -l 12.106.32.254.4444 192.168.42.2 8888
hello

The local IP address is the Internet-side interface in Figure 1.16, but the datagram must go out the other interface to get to the destination. Watching the network with tcpdump shows that the source IP address is the one that was bound by the client, not the outgoing interface address.

14:28:29.614846 12.106.32.254.4444 > 192.168.42.2.8888: udp 6
14:28:29.615225 192.168.42.2 > 12.106.32.254: icmp: 192.168.42.2
                                           udp port 8888 unreachable

8.7

Putting a printf in the client should introduce a delay between each datagram, allowing the server to receive more datagrams. Putting a printf in the server should cause the server to lose more datagrams.

8.8

The largest IPv4 datagram is 65,535 bytes, limited by the 16-bit total length field in Figure A.1. The IP header requires 20 bytes and the UDP header requires 8 bytes, leaving a maximum of 65,507 bytes for user data. With IPv6 without jumbogram support, the size of the IPv6 header is 40 bytes, leaving a maximum of 65,487 bytes for user data.

Figure E.9 shows the new version of dg_cli. If you forget to set the send buffer size, Berkeley-derived kernels return an error of EMSGSIZE from sendto, since the size of the socket send buffer is normally less than required for a maximum-sized UDP datagram (be sure to do Exercise 7.1). But if we set the client's socket buffer sizes as shown in Figure E.9 and run the client program, nothing is returned by the server. We can verify that the client's datagram is sent to the server by running tcpdump, but if we put a printf in the server, its call to recvfrom does not return the datagram. The problem is that the server's UDP socket receive buffer is smaller than the datagram we are sending, so the datagram is discarded and not delivered to the socket. On a FreeBSD system, we can verify this by running netstat -s and looking at the "dropped due to full socket buffers" counter before and after our big datagram is received. The final solution is to modify the server, setting its socket send and receive buffer sizes.

Figure E.9 Writing the maximum-sized UDP/IPv4 datagram.

udpcliserv/dgclibig.c

 1 #include    "unp.h"

 2 #undef  MAXLINE
 3 #define MAXLINE 65507

 4 void
 5 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
 6 {
 7     int     size;
 8     char    sendline[MAXLINE], recvline[MAXLINE + 1];
 9     ssize_t n;

10     size = 70000;
11     Setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size));
12     Setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));

13     Sendto(sockfd, sendline, MAXLINE, 0, pservaddr, servlen);

14     n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);

15     printf("received %d bytes\n", n);
16 }

On most networks, a 65,535-byte IP datagram is fragmented. Recall from Section 2.11 that an IP layer must support a reassembly buffer size of only 576 bytes. Therefore, you may encounter hosts that will not receive the maximum-sized datagrams sent in this exercise. Also, many Berkeley-derived implementations, including 4.4BSD-Lite2, have a sign bug that prevents UDP from accepting a datagram larger than 32,767 bytes (line 95 of p.770 of TCPv2).


    [ Team LiB ] Previous Section Next Section