第7章 揭密

 

 

 

7.4 TCP Socket Life Cycle

 

7.4.1 Connecting

When the client calls connect() with the server’s Internet address, W.X.Y.Z, and port, Q, the underlying implementation creates a socket instance; it is initially in the Closed state.

The implementation copies the local and remote addresses and ports into the underlying socket structure, and initiates the TCP connection establishment handshake.
The TCP opening handshake is known as a three-way handshake because it typically involves three messages: a connection request from client to server, an acknowledgment from server to client, and another acknowledgment from client back to server.The client TCP considers the connection to be established as soon as it receives the acknowledgment from the server.

The server needs to bind to the particular TCP port known to the client. Typically, the server specifies only the port number (here, Q) in the bind() call and gives the special wildcard address INADDR_ANY for the local IP address. In case the server host has more than one IP address, this technique allows the socket to receive connections addressed to any of its IP addresses.

Note that any client connection request that arrives at the server before the call to
listen() will be rejected, even if it arrives after the call to bind().

When the request for a connection arrives from the client, a new socket structure is created for the connection. The new socket’s addresses are filled in based on the arriving packet.

 

The server TCP does not consider the handshake complete until the third message of the three-way handshake is received from the client.

 

The client can send data as soon as it receives the second message of the opening handshake—which may be long before the server accepts the client connection!

 

7.4.2 Closing a TCP Connection

 

Although most applications use close(), shutdown() actually provides more flexibility. A call to close() terminates both directions of transfer and causes the file descriptor associated with the socket to be deallocated. Any undelivered data remaining in RecvQ is discarded, and the flow control mechanism prevents any further transfer of data from the other end’s SendQ.(此时可能导致死锁,对方套接字可能阻塞在send()函数上)

In contrast, shutdown() allows the sending and receiving streams to be terminated independently. It takes an additional argument, one of SHUT_RD, SHUT_WR,or SHUT_RDWR, indicating which stream(s) are to be shut down.

 

In view of the fact that both close() and shutdown()return without waiting for the closing handshake to complete, you may wonder how the sender can be assured that sent data has actually made it to the receiving program (i.e., to Delivered). In fact, it is possible for an application to call close() or shutdown()and have it complete successfully (i.e., not return −1) while there is still data in SendQ. If either end of the connection then crashes before the data makes it to RecvQ, data may be lost without the sending application knowing about it! 
The best solution is to design the application protocol so that whichever side closes first, does so only after receiving application-level assurance that its data was received. (首先发起关闭的一方,在接收到应用程序级的保证--它的数据已经被接收后,才发起关闭)

The other solution is to modify the semantics of close()by setting the SO_LINGER socket option before calling it. The SO_LINGER option specifies an amount of time for the TCP implementation to wait for the closing handshake to complete. The setting of SO_LINGER and the specification of the wait time are given to setsockopt()using the linger structure:

struct linger { 
  int  l_onoff;  // Nonzero to linger
  int  l_linger;  // Time (secs.) to linger
};

To use the linger behavior, set l_onoff to a nonzero value and specify the time to linger in l_linger. When SO_LINGER is set, close() blocks until the closing handshake is completed or until the specified amount of time passes. If the handshake does not complete in time, an error indication (ETIMEDOUT) is returned. Thus, if SO_LINGER is set and close() returns no error, the application is assured that everything it sent reached RecvQ.

The TCP specification requires that when a connection terminates, at least one of the sockets persists in the Time-Wait state for a period of time after both closing handshakes complete. This requirement is motivated by the possibility of messages being delayed in the network.

The most important consequence of Time-Wait is that as long as the underlying socket structure exists, no other socket is permitted to bind to the same local port.

 

7.5 Demultiplexing Demystified

 

The fact that different sockets on the same machine can have the same local address and port number.

 

Clearly, the process of deciding to which socket an incoming packet should be delivered—that is, the demultiplexing process—involves looking at more than just the packet’s destination address and port. Otherwise there could be ambiguity about which socket an incoming packet is intended for. The process of matching an incoming packet to a socket is actually the same for both TCP and UDP, and can be summarized by the following points:
1.The local port in the socket structure must match the destination port number in the incoming packet.
2.Any address fields in the socket structure that contain the wildcard value (*) are considered to match any value in the corresponding field in the packet.(端口号能使用通配符?)

3.If more than one socket structure matches an incoming packet for all four address fields, the one that matches using the fewest wildcards gets the packet.

 

The call to bind() will fail and set EADDRINUSE if any socket matches the local port and local IP address (if any)
specified in the argument to bind().

There are two ways around this. One is to wait until the underlying structure leaves the Time-Wait state. The other is for the server to set the SO_REUSEADDR socket option before calling bind(). That lets bind() succeed in spite of the existence of any sockets representing earlier connections to the server’s port. There is no danger of ambiguity, because the existing connections (whether still in the Established or Time-Wait state) have remote addresses filled in, while the socket being bound does not. 

In general, the SO_REUSEADDR option also enables a socket to bind to a local port to which another socket is already bound, provided that the IP address to which it is being bound (typically the wildcard INADDR_ANY address) is different from that of the existing socket. The default bind() behavior is to disallow such requests.

 

 

 

 

 

 

 

 

 

posted on 2010-09-20 19:13  龍蝦  阅读(420)  评论(0编辑  收藏  举报