Chapter 5
|
|
5.1
|
The duration of the TIME_WAIT state should be
between 1 and 4 minutes, giving an MSL between 30 seconds and 2
minutes.
|
|
|
5.2
|
Our client/server programs do not work with a
binary file. Assume the first 3 bytes in the file are binary 1,
binary 0, and a newline. The call to fgets in Figure
5.5 reads up to MAXLINE-1 characters, or until a
newline is encountered, or up through the EOF. In this example, it
will read the first three characters and then terminate the string
with a null byte. But, our call to strlen in Figure
5.5 returns 1, since it stops at the first null byte. One byte
is sent to the server, but the server blocks in its call to
readline, waiting for a newline character. The client
blocks waiting for the server's reply. This is called a
deadlock: Both processes are
blocked waiting for something that will never arrive from the other
one. The problem here is that fgets signifies the end of
the data that it returns with a null byte, so the data that it
reads cannot contain any null bytes.
|
|
|
5.3
|
5.3 Telnet
converts the input lines into NVT ASCII (Section 26.4 of TCPv1),
which terminates every line with the two-character sequence of a CR
(carriage return) followed by an LF (linefeed). Our client adds
only a newline, which is actually a linefeed character.
Nevertheless, we can use the Telnet client to communicate with our
server as our server echoes back every character, including the CR
that precedes each newline.
|
|
|
5.4
|
No, the final two segments of the connection
termination sequence are not sent. When the client sends the data
to the server, after we kill the server child (the "another line"),
the server TCP responds with an RST. The RST aborts the connection
and also prevents the server end of the connection (the end that
did the active close) from passing through the TIME_WAIT state.
|
|
|
5.5
|
Nothing changes because the server process that
is started on the server host creates a listening socket and is
waiting for new connection requests to arrive. What we send in Step
3 is a data segment destined for an ESTABLISHED TCP connection. Our
server with the listening socket never sees this data segment, and
the server TCP still responds to it with an RST.
|
|
|
5.6
|
Figure
E.1 shows the program. Running this program under Solaris
generates the following:
solaris % tsigpipe 192.168.1.10
SIGPIPE received
write error: Broken pipe
The initial sleep of two seconds is to
let the daytime server send its reply and close its end of the
connection. Our first write sends a data segment to the
server, which responds with an RST (since the daytime server has
completely closed its socket). Note that our TCP allows us to write
to a socket that has received a FIN. The second sleep lets
the server's RST be received, and our second write
generates SIGPIPE. Since our signal handler returns,
write returns an error of EPIPE.
|
|
|
5.7
|
Assuming the server host supports the
weak end system model (which we
describe in Section 8.8),
everything works. That is, the server host will accept an incoming
IP datagram (which contains a TCP segment in this case) arriving on
the leftmost datalink, even though the destination IP address is
the address of the rightmost datalink. We can test this by running
our server on our host linux (Figure 1.16) and then
starting the client on our host solaris, but specifying
the other IP address of the server (206.168.112.96) to the client.
After the connection is established, if we run netstat on
the server, we see that the local IP address is the destination IP
address from the client's SYN, not the IP address of the datalink
on which the SYN arrived (as we mentioned in Section
4.4).
Figure E.1
Generate SIGPIPE.
tcpcliserv/tsigpipe.c
1 #include "unp.h"
2 void
3 sig_pipe(int signo)
4 {
5 printf("SIGPIPE received\n");
6 return;
7 }
8 int
9 main(int argc, char **argv)
10 {
11 int sockfd;
12 struct sockaddr_in servaddr;
13 if (argc != 2)
14 err_quit("usage: tcpcli <IPaddress>");
15 sockfd = Socket(AF_INET, SOCK_STREAM, 0);
16 bzero(&servaddr, sizeof(servaddr));
17 servaddr.sin_family = AF_INET;
18 servaddr.sin_port = htons(13); /* daytime server */
19 Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
20 Signal(SIGPIPE, sig_pipe);
21 Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
22 sleep(2);
23 Write(sockfd, "hello", 5);
24 sleep(2);
25 Write(sockfd, "world", 5);
26 exit(0);
27 }
|
|
|
5.8
|
Our client was on a little-endian Intel system,
where the 32-bit integer with a value of 1 was stored as shown in
Figure E.2.
The 4 bytes are sent across the socket in the
order A, A+1, A+2, and
A+3 where they are stored in the
big-endian format, as shown in Figure E.3.
This value of 0x01000000 is interpreted
as 16,777,216. Similarly, the integer 2 sent by the client will be
interpreted at the server as 0x02000000, or 33,554,432.
The sum of these two integers is 50,331,648, or
0x03000000. When this bigendian value on the server is
sent to the client, it is interpreted on the client as the integer
value 3.
The 32-bit integer value of - 22 is represented
on the little-endian system as shown in Figure E.4, assuming a two's-complement
representation of negative numbers.
This is interpreted on the big-endian server as
0xeaffffff, or - 352,321,537. Similarly, the little-endian
representation of - 77 is 0xffffffb3, but this is
represented on the big-endian server as 0xb3ffffff, or -
1,275,068,417. The addition on the server yields a binary result of
0x9efffffe, or - 1,627,389,954. This big-endian value is
sent across the socket to the client where it is interpreted as the
little-endian value 0xfeffff9e, or - 16,777,314, which is
the value printed in our example.
|
|
|
5.9
|
The technique is correct (converting the binary
values to network byte order), but the two functions htonl
and ntohl cannot be used. Even though the l in
these functions once meant "long," these functions operate on
32-bit integers (Section 3.4). On a
64-bit system, a long will probably occupy 64 bits and
these two functions will not work correctly. One might define two
new functions, hton64 and ntoh64, to solve this
problem, but this will not work on systems that represent
longs using 32 bits.
|
|
|
5.10
|
In the first scenario, the server blocks forever
in the call to readn in Figure 5.20 because
the client sends two 32-bit values but the server is waiting for
two 64-bit values. Swapping the client and server between the two
hosts causes the client to send two 64-bit values, but the server
reads only the first 64 bits, interpreting them as two 32-bit
values. The second 64-bit value remains in the server's socket
receive buffer. The server writes back one 32-bit value and the
client will block forever in its call to readn in
Figure 5.19, waiting
to read one 64-bit value.
|
|
|
5.11
|
IP's routing function looks at the destination
IP address (the server's IP address) and searches the routing table
to determine the outgoing interface and next hop (Chapter 9 of
TCPv1). The primary IP address of the outgoing interface is used as
the source IP address, assuming the socket has not already bound a
local IP address.
|
|