Half-close and CLOSE_WAIT of TCP

Terminating a connection goes through a 4-way handshake. This is caused by TCP's half-close. Since a TCP connection is full-duplex (that is, data can be transmitted in both directions at the same time, which can be understood as two independent channels in opposite directions), each direction must be closed separately.

 

This principle is that when a party completes its data transmission task, it can send a FIN to terminate the connection in this direction. When one end receives a FIN, the kernel makes read return 0 to notify the application layer that the other end has terminated the data transfer to the local end. Sending a FIN is usually the result of a socket being closed by the application layer.

Example: A TCP client sends a FIN to close data transfer from client to server.

 

    How does half-shutdown affect the server? First look at the following TCP state transition diagram

 

 

                                  tcp state replacement diagram

 

    When the client actively closes, it sends a FIN packet, receives the ACK from the server, and the client stays in the FIN_WAIT2 state. The server receives the FIN, sends an ACK, and stays in the COLSE_WAIT state.

    This CLOSE_WAIT state is very annoying. It lasts for a very long time. If a large number of sockets in the COLSE_WAIT state are accumulated on the server side, the server resources may be exhausted and the service cannot be provided.

    So, how does the server generate a large number of sockets in the COLSE_WAIT state that are out of control? Let's track it down.

    A very simple reason is that the server does not continue to send FIN packets to the client.

    Why the server does not send FIN, it may be the need of business implementation, now is not the time to send FIN, because the server still has data to send to the client, after sending, it will naturally send FIN through the system call, this scenario is not the above us The mentioned persistent COLSE_WAIT state, this is under control.

    So what is the reason? We introduce two system calls close(sockfd) and shutdown(sockfd,how) and then analyze it down.

    Here, a concept needs to be clarified - a process opens a socket, and then when the process forks a child process, the socket's sockfd will be inherited. Socket is a system-level object. The result is that this socket is opened by two processes, and the reference count of this socket will become 2.

 

    Continue to talk about the closure of the socket by the above two system calls.

    When calling close(sockfd), the kernel checks the reference count on the socket corresponding to this fd. If the reference count is greater than 1, then decrement the reference count by 1 and return. If the reference count is equal to 1, then the kernel will actually close the TCP connection by sending a FIN.

    When calling shutdown(sockfd, SHUT_RDWR), the kernel does not check the reference count on the socket corresponding to this fd, and directly closes the TCP connection by sending FIN.

 

     The truth should be revealed now. Maybe there is a problem with the implementation of the server. The parent process opens the socket, and then uses the derived child process to handle the business. The parent process continues to monitor network requests and will never terminate. When the client sends FIN, the read of the child process processing the business returns 0. The child process finds that the peer has been closed, and directly calls close() to close the local. In fact, just decrementing the socket's reference count by 1, the socket is not closed. As a result, there is another CLOSE_WAIT socket in the system. . .

 

How to avoid this from happening?

The shutdown handling of the child process should be like this:

shutdown(sockfd, SHUT_RDWR);

close(sockfd);

In this way, the FIN of the server will be sent, the socket will enter the LAST_ACK state, and after the arrival of the final ACK, it will enter the initial state CLOSED.

 

Add the function description of shutdown()

The shutdown system call is used to control the closing method of the socket under the linux system

int shutdown(int sockfd,int how);

The parameter how allows the following options to be selected for the shutdown operation:

SHUT_RD: Close the read side of the connection. That is, the socket is no longer accepting data, and any data currently in the socket accept buffer will be discarded. The process will not be able to issue any read operations on this socket. Any data received after this call on the TCP socket will be acknowledged and then discarded.

SHUT_WR: Close the write side of the connection.

SHUT_RDWR: equivalent to calling shutdown twice: first with SHUT_RD, then with SHUT_WR

Notice:

In multi-process, if one process is shutdown(sfd, SHUT_RDWR), other processes will not be able to communicate. If one process is closed(sfd), it will not affect other processes.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325342635&siteId=291194637