Recently, a problem occurs in the project. When the program exits, the resources are not released normally. After debugging, it was found that the network thread had been congested, causing some necessary resources not to be released. I wrote a few simple test programs to debug them, in Linux, directly close the file descriptor of the socket does not cause some blocking socket functions called by the program, such as read and recvfrom, to exit blocking, and thus the resource cannot be released normally. The following is a simplified example.
The following is a simplified UDP Service Program. First, create a socket object, enable the service thread, and send the data packets sent from the client back to the client. When you press ENTER twice in shell, the program exits. Let's observe under what circumstances the socket service thread can exit normally after the program exits.
- #include <stdio.h>
- #include <sys/types.h>
- #include <sys/socket.h>
- #include <linux/in.h>
- #include <string.h>
- #include <pthread.h>
-
- #define SERVER_PORT 8888
- #define BUFFER_LEN 256
-
- int g_Exit = 0;
-
- void *service( void* arg )
- {
- char buff[BUFFER_LEN];
- struct sockaddr clientAddr;
- int socklen = sizeof(clientAddr);
- int recvbytes;
- int socketfd = *((int *)arg);
-
- printf("OK, Enter Service!\n");
-
- while(!g_Exit)
- {
- recvbytes = recvfrom(socketfd,buff,BUFFER_LEN,0,&clientAddr,&socklen);
-
- sendto(socketfd,buff,recvbytes,0,&clientAddr,socklen);
- }
-
- printf("OK, Service Thread Exit!\n");
-
- pthread_exit(NULL);;
- }
-
- int main( int argc,char * argv[] )
- {
- int fd;
- void *status;
- struct sockaddr_in serverAddr;
- pthread_t thr;
- pthread_attr_t attr;
-
- fd = socket(AF_INET,SOCK_DGRAM,0);
-
- memset(&serverAddr,0,sizeof(serverAddr));
-
- serverAddr.sin_family = AF_INET;
- serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);
- serverAddr.sin_port = htons(SERVER_PORT);
-
- bind(fd,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
-
- // create service thread
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- if( pthread_create(&thr,&attr,service,(void *)&fd ) )
- {
- printf("pthread_create fail!\n");
- return -1;
- }
- // Free attribute
- pthread_attr_destroy(&attr);
-
- // wait user control exit
- getchar();
- getchar();
-
- g_Exit = 1;
-
- printf("OK, Waiting For Thread Exit...!\n");
-
- close(fd);
-
- // wait for thread exit
- pthread_join(thr, &status);
-
- printf("OK, Exit Main Process !\n");
-
- return 0;
- }
After you press ENTER twice, the result is as follows:
650) this. width = 650; "src =" http://www.bkjia.com/uploads/allimg/131228/1AI64537-0.jpg "border =" 0 "alt =" "/>
We can see that the exit information of the main process and service thread is not displayed, and neither the main process nor the service thread Exits normally. Therefore, directly close the socket handle does not cause the recvfrom function to exit.
So what if we replace pthread_join with pthread_cancel? The results are the same. Although the main process exits, the service thread cannot exit normally. So how can we exit the recvfrom blocking?
You can use the shutdown function after searching for it online.
- // The shutdown function is prototype:
- # Include <sys/socket. h>
- Int shutdown (int s, int how );
- // Shutdown () allows for more detailed control over socket shutdown, which allows one-way closure of sockets or all prohibitions.
- // The parameter s is the socket descriptor to be closed.
- // The how parameter specifies the closing method. The specific values are as follows:
- // SHUT_RD: closes the read channel on the connection. After that, the process will no longer receive any data, and the data not read in the receiving buffer will also be discarded, however, data can still be sent on the socket.
- // SHUT_WR: closes the write channel on the connection. After that, the process will no longer be able to send any data, and data not sent in the sending buffer will also be discarded, however, data can still be received on the socket.
- // SHUT_RDWR: The read and write channels will all be disabled.
- // If the execution succeeds, 0 is returned. If an error occurs,-1 is returned. The error code is stored in errno.
You can test the code by adding shutdown (fd, SHUT_RDWR) before pthread_join. then compile and debug the code. The result is as follows:
650) this. width = 650; "src =" http://www.bkjia.com/uploads/allimg/131228/1AI62F0-1.jpg "border =" 0 "alt =" "/>
We can see that the Service thread has exited normally. Further test: What if I only use the shutdown write channel or the shutdown read channel?
After testing, we can find that if only the write channel shutdown (fd, SHUT_WR) is disabled, the service thread still cannot exit normally, and if only the read channel shutdown (fd, SHUT_RD) is disabled ), the service thread Exits normally. The analysis is as follows:Because recvfrom is in the fd read channel waiting list, you must disable the read channel to wake up the recvfrom blocking.
So why does shutdown cause recvfrom to exit and block, but close is not?
My understanding is as follows: shutdown destroys the read/write channel of the socket connection, leading to the wake-up of the socket function with read/write blocking, while the close function only does the operation to close the connection and release the socket resource, the read/write channel is not cleaned up, and thus the blocking of the read/write function cannot be successfully awakened. Hope experts can give a deeper explanation)
Further, is there any other way to solve this problem?
Below I will briefly list some feasible methods found on the internet, and I will study them further in the future:
1. Set socket sending/receiving timeout
2. Use non-blocking mode, asynchronous socket model
3. You are welcome to add other methods.
The article is written here, welcome to send a letter to further exchange lujun.hust@gmail.com
This article from the "three shadows" blog, please be sure to keep this source http://ticktick.blog.51cto.com/823160/845536