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 }
|