[ Team LiB ] Previous Section Next Section

Chapter 15

15.1

unlink removes the pathname from the filesystem, and when the client calls connect at a later time, the connect will fail. The server's listening socket is not affected, but no clients will be able to connect after the unlink.

15.2

The client cannot connect to the server even if the pathname still exists, because for the connect to succeed, a Unix domain socket must be currently open and bound to that pathname (Section 15.4).

15.3

When the server prints the client's protocol address by calling sock_ntop, the output is "datagram from (no pathname bound)" because no pathname is bound to the client's socket by default.

One solution is to specifically check for a Unix domain socket in udp_client and udp_connect and bind a temporary pathname to the socket. This puts the protocol dependency in the library function where it belongs, not in our application.

15.4

Even though we force 1-byte writes by the server for its 26-byte reply, putting the sleep in the client guarantees that all 26 segments are received before read is called, causing read to return the entire reply. This is just to confirm (again) that TCP is a byte stream with no inherent record markers.

To use the Unix domain protocols, we start the client and server with the two command-line arguments /local (or /unix) and /tmp/daytime (or any other temporary pathname you wish to use). Nothing changes: 26 bytes are returned by read each time the client runs.

Since the server specifies the MSG_EOR flag for each send, each byte is considered a logical record and read returns 1 byte each time it is called. What is happening here is that Berkeley-derived implementations support the MSG_EOR flag by default. This is undocumented, however, and should not be used in production code. We use it here as an example of the difference between a byte stream and a record-oriented protocol. From an implementation perspective, each output operation goes into a memory buffer (mbuf) and the MSG_EOR flag is retained by the kernel with the mbuf as the mbuf goes from the sending socket to the receiving socket's receive buffer. When read is called, the MSG_EOR flag is still attached to each mbuf, so the generic kernel read routine (which supports the MSG_EOR flag since some protocols use the flag) returns each byte by itself. Had we used recvmsg instead of read, the MSG_EOR flag would be returned in the msg_flags member each time recvmsg returned 1 byte. This does not work with TCP because the sending TCP never looks at the MSG_EOR flag in the mbuf that it is sending, and even if it did, there is no way to pass this flag to the receiving TCP in the TCP header. (Thanks to Matt Thomas for pointing out this undocumented "feature.")

15.5

15.5 Figure E.13 shows an implementation of this program.

Figure E.13 Determine actual number of queued connections for different backlog values.

debug/backlog.c

 1 #include    "unp.h"

 2 #define PORT        9999
 3 #define ADDR        "127.0.0.1"
 4 #define MAXBACKLOG  100

 5             /* globals */
 6 struct sockaddr_in serv;
 7 pid_t   pid;                    /* of child */

 8 int     pipefd[2];
 9 #define pfd pipefd[1]           /* parent's end */
10 #define cfd pipefd[0]           /* child's end */

11             /* function prototypes */
12 void    do_parent(void);
13 void    do_child(void);

14 int
15 main(int argc, char **argv)
16 {
17     if (argc != 1)
18         err_quit("usage: backlog");

19     Socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd);

20     bzero(&serv, sizeof(serv));
21     serv.sin_family = AF_INET;
22     serv.sin_port = htons(PORT);
23     Inet_pton(AF_INET, ADDR, &serv.sin_addr);

24     if ( (pid = Fork()) == 0)
25         do_child();
26     else
27         do_parent();

28     exit(0);
29 }

30 void
31 parent_alrm(int signo)
32 {
33     return;                      /* just interrupt blocked connect() */
34 }

35 void
36 do_parent(void)
37 {
38     int     backlog,  j, k, junk, fd[MAXBACKLOG + 1];

39     Close(cfd);
40     Signal(SIGALRM, parent_alrm);

41     for (backlog = 0; backlog <= 14; backlog++) {
42         printf("backlog = %d: ", backlog);
43         Write(pfd, &backlog, sizeof(int));  /* tell child value */
44         Read(pfd, &junk, sizeof(int));  /* wait for child */

45         for (j = 1; j <= MAXBACKLOG; j++) {
46             fd[j] = Socket(AF_INET, SOCK_STREAM, 0);
47             alarm(2);
48             if (connect(fd[j], (SA *) &serv, sizeof(serv)) < 0) {
49                 if (errno != EINTR)
50                     err_sys("connect error, j = %d", j);
51                 printf("timeout, %d connections completed\n", j - 1);
52                 for (k = 1; k <= j; k++)
53                     Close(fd[k]);
54                 break;          /* next value of backlog */
55             }
56             alarm(0);
57         }
58         if (j > MAXBACKLOG)
59             printf("%d connections?\n", MAXBACKLOG);
60     }
61     backlog = -1;      /* tell child we're all done */
62     Write(pfd, &backlog, sizeof(int));
63 }

64 void
65 do_child(void)
66 {
67     int     listenfd, backlog, junk;
68     const int on = 1;

69     Close(pfd);

70     Read(cfd, &backlog, sizeof(int));      /* wait for parent */
71     while (backlog >= 0) {
72         listenfd = Socket(AF_INET, SOCK_STREAM, 0);
73         Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
74         Bind(listenfd, (SA *) &serv, sizeof(serv));
75         Listen(listenfd, backlog);  /* start the listen */

76         Write(cfd, &junk, sizeof(int)); /* tell parent */

77         Read(cfd, &backlog, sizeof(int));   /* just wait for parent */
78         Close(listenfd);        /* closes all queued connections, too */
79     }
80 }


    [ Team LiB ] Previous Section Next Section