如何唤醒socket被阻塞的函数
2012-04-27 09:58
155 查看
最近项目遇到一个问题,程序退出的时候资源没有正常释放。经过调试发现,原来是网络线程一直阻塞,导致一些必要的资源没有被释放,写了几个简单的测试程序调试了一下才明白,原来在Linux下直接close socket的文件描述符,并不会使程序中调用的一些阻塞式的socket函数(比如 read、recvfrom 等)退出阻塞,从而导致无法正常释放资源。简化示例如下。
下面是一个简化的UDP服务程序,首先创建socket对象,然后开启服务线程,将客户端发送过来的数据包回发给客户端。当用户在shell中敲入两次回车后,程序退出。我们来观察一下程序退出后,socket服务线程在怎样的情况下可以正常退出。
上述程序,当用户敲两次回车后,显示结果如下:
可以看到,没有打出主进程和服务线程的退出信息,无论是主进程还是服务线程都没有正常退出,由此可见,直接close socket句柄,并不能使 recvfrom 函数退出阻塞。
那么,如果把 pthread_join 换成 pthread_cancel 呢?结果是一样的,虽然主进程退出了,但依然无法让 service 线程正常退出。那么,该如何才能正常退出 recvfrom 的阻塞呢?
网上搜了一下,可以考虑使用 shutdown 函数。
可以测试一下,我们在上述代码的pthread_join前面加上一句:shutdown(fd,SHUT_RDWR); 然后再编译调试,结果如下:
可以看到,Service服务线程已经正常退出了。进一步测试,如果只是shutdown写通道或者只shutdown读通道呢?
经过测试可以发现,如果只关闭写通道 shutdown(fd,SHUT_WR); 服务线程依然无法正常退出,而如果只关闭读通道 shutdown(fd,SHUT_RD),则服务线程正常退出了。分析如下:因为recvfrom在fd的读通道等待列表中,因此必须关闭读通道时才能将recvfrom阻塞唤醒。
那么,为啥shutdown就可以使得recvfrom退出阻塞,而close却不能呢?
我的理解如下:shutdown破坏了socket连接的读写通道,导致读写阻塞的socket函数被唤醒,而close函数只是做了关闭连接释放socket资源的操作,却并没有进行读写通道的清理工作,从而无法成功唤醒读写函数的阻塞。(期待高手给出更深层次的解释)
进一步,那么,解决这一问题,还有其他的什么办法没有?
下面我简单地罗列一下网上搜到的可行的一些方法,以后有时间再深入研究:
1. 设置socket发送/接收超时
2. 使用非阻塞方式,异步socket模型
3. 其他方式,欢迎大家补充。
文章就写到这里了,欢迎大家来信进一步交流lujun.hust@gmail.com
本文出自 “Jhuster的专栏” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/845536
下面是一个简化的UDP服务程序,首先创建socket对象,然后开启服务线程,将客户端发送过来的数据包回发给客户端。当用户在shell中敲入两次回车后,程序退出。我们来观察一下程序退出后,socket服务线程在怎样的情况下可以正常退出。
#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; }
上述程序,当用户敲两次回车后,显示结果如下:
可以看到,没有打出主进程和服务线程的退出信息,无论是主进程还是服务线程都没有正常退出,由此可见,直接close socket句柄,并不能使 recvfrom 函数退出阻塞。
那么,如果把 pthread_join 换成 pthread_cancel 呢?结果是一样的,虽然主进程退出了,但依然无法让 service 线程正常退出。那么,该如何才能正常退出 recvfrom 的阻塞呢?
网上搜了一下,可以考虑使用 shutdown 函数。
//shutdown函数原型为: #include <sys/socket.h> int shutdown(int s, int how); //shutdown() 可以对套接字的关闭进行更细致的控制,它允许对套接字进行单向关闭或全部禁止。 //参数 s 为待关闭的套接字描述符。 //参数 how 指定了关闭方式,具体取值如下: //SHUT_RD : 将连接上的读通道关闭,此后进程将不能再接收到任何数据,接收缓冲区中还未被读取的数据也将被丢弃,但仍然可以在该套接字上发送数据。 //SHUT_WR : 将连接上的写通道关闭,此后进程将不能再发送任何数据,发送缓冲区中还未被发送的数据也将被丢弃,但仍然可以在该套接字上接收数据。 //SHUT_RDWR : 读、写通道都将被关闭。 //执行成功返回 0,出错则返回 -1,错误代码存入 errno 中。
可以测试一下,我们在上述代码的pthread_join前面加上一句:shutdown(fd,SHUT_RDWR); 然后再编译调试,结果如下:
可以看到,Service服务线程已经正常退出了。进一步测试,如果只是shutdown写通道或者只shutdown读通道呢?
经过测试可以发现,如果只关闭写通道 shutdown(fd,SHUT_WR); 服务线程依然无法正常退出,而如果只关闭读通道 shutdown(fd,SHUT_RD),则服务线程正常退出了。分析如下:因为recvfrom在fd的读通道等待列表中,因此必须关闭读通道时才能将recvfrom阻塞唤醒。
那么,为啥shutdown就可以使得recvfrom退出阻塞,而close却不能呢?
我的理解如下:shutdown破坏了socket连接的读写通道,导致读写阻塞的socket函数被唤醒,而close函数只是做了关闭连接释放socket资源的操作,却并没有进行读写通道的清理工作,从而无法成功唤醒读写函数的阻塞。(期待高手给出更深层次的解释)
进一步,那么,解决这一问题,还有其他的什么办法没有?
下面我简单地罗列一下网上搜到的可行的一些方法,以后有时间再深入研究:
1. 设置socket发送/接收超时
2. 使用非阻塞方式,异步socket模型
3. 其他方式,欢迎大家补充。
文章就写到这里了,欢迎大家来信进一步交流lujun.hust@gmail.com
本文出自 “Jhuster的专栏” 博客,请务必保留此出处http://ticktick.blog.51cto.com/823160/845536
相关文章推荐
- 如何唤醒socket被阻塞的函数
- 如何唤醒socket被阻塞的函数
- 如何唤醒socket被阻塞的函数
- QTcpSocket如何阻塞readyRead()信号
- Java千百问_02基本使用(012)_如何编写非阻塞SocketChannel程序
- php socket 处理不过来数据流,该如何避免(好像是阻塞了)
- BCB如何屏蔽TClientSocket阻塞方式产生的10061错误
- 网络编程中重要函数总结:如何判断socket关闭
- 基于非阻塞socket的多线程服务器的实现------一个服务器如何与多个客户端进行通信?
- soket编程相关(二)如何判断连接状态断开,Linux:C/Socket多路复用select(),阻塞
- Unix网络编程之socket阻塞与非阻塞模式下函数调用结果分析
- 面试中的soket编程相关(一)(如何判断连接状态断开,Linux:C/Socket多路复用select(),阻塞
- 如何设置linux socket为非阻塞
- socket 哪些函数会发生通信阻塞
- linux c实现超时、非阻塞socket的函数select
- Socket send函数和recv函数详解以及利用select()函数来进行指定时间的阻塞【转】
- 如何判断连接状态断开,Linux:C/Socket多路复用select(),阻塞
- 阻塞模式下socket连接connect超时后长达75秒,如何避免
- Unity,如何阻塞当前函数一段时间
- 【多线程】控制多线程并发的函数的阻塞功能如何实现?