码迷,mamicode.com
首页 > 编程语言 > 详细

可以“随时”终止的socket线程

时间:2015-05-08 19:46:10      阅读:134      评论:0      收藏:0      [点我收藏+]

标签:

很多人在刚学socket时,都是在线程中connect,然后while(flag) read();要停止这个线程时将falg置false,再wait,甚至直接termination(这种方式终止线程的安全隐患不在这里论述)。

一般情况下的确没什么问题,但拿到一个真正的项目中时,就不太好了,这样写就很可能出现这样的情况:网络情况不好时,connect和read可能需要很长时间才能返回,将flag置false时,可能还需要等待很久(最大等待时间和平台有关),也许长达几分钟,特别是在GUI线程中去停止这个线程时,很可能会造成界面卡死。

那么该如何解决这种问题呢?我的做法是,在connect之前,将socket置为非阻塞模式,这时再使用connect时,会立即返回,我们判断它的返回值,如果没有连接成功(内核还在继续为我们建立连接),即使用select去监测它,发现它连接成功时,再不愿成阻塞模式。

这样做还不够,因为select也是阻塞函数,但select是可以监测多个文件描述符的,我们可以创建一个pipe(还有eventfd之类),让select同时监测pipe和socket,在我们需要停止connect时,向pipe的写端写入数据,此时select就会被唤醒,当我们发现是由pipe唤醒select时,即直接释放相关资源后退出线程。

对于read部分,我们一样使用select同时监测pipe和socket,使用同样的方法退出。

这种方法相对可以更快更安全的终止线程,示例代码如下(注意,以下代码纯记事本写的,可能存在错误,仅供参考):

int MyThread::connect()
{
    int fd;
    /// 此处略过部分代码 ///
    
    unsigned long ul = 1;
    ioctl(fd, FIONBIO, &ul);		/// 设置为非阻塞模式
    if (connect(fd, (struct sockaddr *)&raddr, sizeof(struct sockaddr_in)) == -1)
    {
    	int error = 0;
    	int max_fd = ( fd > _pipe_fd[0] ? fd : _pipe_fd[0] ) + 1;
    	
    	fd_set set;
    	FD_ZERO(&set);
    	FD_SET(fd, &set);
    
    	fd_set rset;
    	FD_ZERO(&rset);
    	FD_SET(_pipe_fd[0], &rset);	/// 将pipe读端加入select,便于线程外随时退出select
    	
    	if ( ! (error = (select(max_fd, &rset, &set, NULL, NULL) <= 0) ) )		/// error == 0  !error == 1表示没发生错误,进一步检查错误
    	{
    		if (FD_ISSET(fd, &set))
    		{
    			int len = 0;
    			getsockopt(fd + 1, SOL_SOCKET, SO_ERROR, (char*)&error, &len);
    		}
    		else if (FD_ISSET(_pipe_fd[0], &rset))
    		{
    		    /// 接收到退出命令
    		    /// 如果退出后还需要复用到pipe,此处建议将pipe里的东西read出来以清空
    			error = -2;
    		}
    		else
    		{
    			/// 如果发生错误,或者由pipe唤醒,则终止连接
    			error = -1;
    		}
    	}
    	if (error)
    	{
    		close(fd);
    		return -1;
    	}
    }
    ul = 0;
    ioctl(sockfd, FIONBIO, &ul);	/// 还原为阻塞模式
    return fd;
}

void MyThread::run()
{
    int fd = connect();
    if (fd < 0)
    {
        return;
    }
    
    fd_set rset;
    int ret;
    int max_fd = ( fd > _pipe_fd[0] ? fd : _pipe_fd[0] ) + 1;
    while (true)
    {
        FD_ZERO(&rset);
        FD_SET(fd, &rset);
        FD_SET(_pipe_fd[0], &rset);
        
        ret = select(fd + 1, &rset, NULL, NULL, NULL);
        if (ret < 0)
        {
            break;
        }
        if (ret)
        {
            if (FD_ISSET(_pipe_fd[0], &rset))
            {
                break;
            }
            if (FD_ISSET(fd, &rset))
            {
                read();
            }
        }
    }
    close(fd);
}

  

以种方法只是我目前的处理方式,应该还有更好的方式(有很多优秀的库目前我还没有去深入学习)。

高手们可以飘过了,不过最好留下点指导吧。

可以“随时”终止的socket线程

标签:

原文地址:http://www.cnblogs.com/BYSF-XF/p/4488566.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!