标签:
很多人在刚学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);
}
以种方法只是我目前的处理方式,应该还有更好的方式(有很多优秀的库目前我还没有去深入学习)。
高手们可以飘过了,不过最好留下点指导吧。
标签:
原文地址:http://www.cnblogs.com/BYSF-XF/p/4488566.html