Blog.csdn.net/lishenglong…

I had not paid much attention to this problem before. It was a problem I encountered in the interview recently. When I interviewed two companies, they both met this problem, which made me start to pay attention to this problem. Speaking of CLOSE_WAIT, if you don’t know, take a look at the TCP state transition diagram. \

\

Closing a socket can be classified into Active closure and Passive closure. The former refers to the shutdown initiated by the local host; The latter is when the local host detects a shutdown initiated by the remote host and responds to close the entire connection. Take out the state transition of the closed part and you get the following: \

\

When is the connection in CLOSE_WAIT state? In the passive closed connection case, the connection is in CLOSE_WAIT state when a FIN has been received but its own FIN has not been sent. In general, the CLOSE_WAIT state should be short in duration, as in the SYN_RCVD state. However, in some special cases, the connection is in CLOSE_WAIT state for a long time. A large number of close_wait cases occur, the main reason is that in some cases the other party has closed the socket link, but we are busy with reading or writing, so the connection is not closed. The code needs to determine the socket. Once 0 is read, disconnect, read returns negative, check errno, if not AGAIN, disconnect. As described in Reference 4, the CLOSE_WAIT state connection is generated by sending syn-FIN packets. However, I think the protocol stack will discard such illegal packets. If you are interested, you can test it and tell me the result. -) To illustrate this problem more clearly, let’s write a test program. Note that the test program is flawed. Whenever we construct a case in which the socket is closed and we are still reading, or we simply do not close the socket, we construct such a case. \

server.c:\

#include <stdio.h>
#include <string.h>#include <netinet/in.h> #define MAXLINE 80 #define SERV_PORT 8000 int main(void) { struct sockaddr_in servaddr, cliaddr; socklen_t cliaddr_len; int listenfd, connfd; char buf[MAXLINE]; char str[INET_ADDRSTRLEN]; int i, n; listenfd = socket(AF_INET, SOCK_STREAM, 0); int opt = 1; setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SERV_PORT); bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); listen(listenfd, 20); printf("Accepting connections ... \n"); while (1) { cliaddr_len = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len); //while (1) { n = read(connfd, buf, MAXLINE); if (n == 0) { printf("the other side has been closed.\n"); break; } printf("received from %s at PORT %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)), ntohs(cliaddr.sin_port)); for (i = 0; i < n; i++) buf[i] = toupper(buf[i]); write(connfd, buf, n); } // the socket is not closed, or a sleep is added before the close; //close(connfd); }}Copy the code

\

client.c:\

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>#include <sys/socket.h> #include <netinet/in.h> #define MAXLINE 80 #define SERV_PORT 8000 int main(int argc, char *argv[]) { struct sockaddr_in servaddr; char buf[MAXLINE]; int sockfd, n; char *str; if (argc ! = 2) { fputs("usage: ./client message\n", stderr); exit(1); } str = argv[1]; sockfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; Inet_pton (AF_INET, "127.0.0.1", & servaddr. Sin_addr); servaddr.sin_port = htons(SERV_PORT); connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)); write(sockfd, str, strlen(str)); n = read(sockfd, buf, MAXLINE); printf("Response from server:\n"); write(STDOUT_FILENO, buf, n); write(STDOUT_FILENO, "\n", 1); close(sockfd); return 0; }Copy the code

The results are as follows: \

debian-wangyao:~$ ./client a Response from server: A debian-wangyao:~$ ./client b Response from server: B debian-wangyao:~$ ./client c Response from server: C debian-wangyao:~$ netstat -antp | grep CLOSE_WAIT (Not all processes could be identified, non-owned process info will not be shown, ) TCP 1 0 127.0.0.1:8000 127.0.0.1:58309 CLOSE_WAIT 6979/server TCP 1 0 127.0.0.1:8000 127.0.0.1:58308 CLOSE_WAIT 6979/server TCP 1 0 127.0.0.1:8000 127.0.0.1:58307 CLOSE_WAIT 6979/serverCopy the code

\

The basic idea of the solution is to detect the socket that the other party has closed, and then close it. Once read returns 0, disconnect, read returns negative, check errno, if not AGAIN, disconnect. (Note: in Figure 7.6 of UNP 7.5, you can see that connection CLOSE_WAIT can be processed by using select to detect that a FIN has been sent.) 2 Select last_update from each socket, which updates the timestamp with the current time every time data is received or sent successfully. Periodically check all timestamps and close the socket if the timestamp difference exceeds a certain threshold. 3. Use a heart-beat thread to periodically send heartbeat packets in the specified format to the socket. If RST packets are received, the socket is closed. 4. Set the SO_KEEPALIVE option and modify kernel parameters if the socket KEEPALIVE mechanism is enabled: // Enable the socket KEEPALIVE int iKeepAlive = 1; setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, (void *)&iKeepAlive, sizeof(iKeepAlive)); tcp_keepalive_intvl (integer; default: 75; Since Linux 2.4) The number of seconds between TCP keepalive probes. Tcp_keepalive_probes (INTEGER; default: 9; Since Linux 2.2) The maximum number of TCP keep-alive probes to send before giving up and killing The connection if no response is obtained from the other end. tcp_keepalive_time (integer; default: 7200; Since Linux 2.2) The number of seconds a connection needs to be idle before TCP begins sending out keep-alive probes. Keep-alives are only sent when the SO_KEEPALIVE socket option is enabled. The default value is 7200 seconds (2 hours). An idle connec‐tion is terminated after approximately An additional 11 minutes (9 probes An interval of 75 seconds apart) when keep-alive is enabled. echo 120 > /proc/sys/net/ipv4/tcp_keepalive_time echo 2 > /proc/sys/net/ipv4/tcp_keepalive_intvl echo 1 > /proc/sys/net/ipv4/tcp_keepalive_probes In addition to modifying kernel parameters, You can use setsockopt to modify socket parameters. For details, see Man 7 Socket.

int KeepAliveProbes=1;
int KeepAliveIntvl=2;
int KeepAliveTime=120;
setsockopt(s, IPPROTO_TCP, TCP_KEEPCNT, (void *)&KeepAliveProbes, sizeof(KeepAliveProbes));
setsockopt(s, IPPROTO_TCP, TCP_KEEPIDLE, (void *)&KeepAliveTime, sizeof(KeepAliveTime));
setsockopt(s, IPPROTO_TCP, TCP_KEEPINTVL, (void *)&KeepAliveIntvl, sizeof(KeepAliveIntvl));
Copy the code

\

Reference:

Blog.chinaunix.net/u/20146/sho… http://blog.csdn.net/eroswang/archive/2008/03/10/2162986.aspx haka.sharera.com/blog/BlogTo… http://learn.akae.cn/media/ch37s02.html faq.csdn.net/read/208036… http://www.cndw.com/tech/server/2006040430203.asp davidripple.bokee.com/1741575.htm… http://doserver.net/post/keepalive-linux-1.php

\

\

\

\

\

\