Linux网络编程五、套接字超时
1.accept超时
accept等待并接受连接请求的过程是阻塞的,可以通过IO转接来设置等待一定的时长,如果超时没有连接,就让这个函数返回,让当前进程/线程处理别的任务。
例:
// 最大的文件描述符是: lfd fd_set rdset; FD_ZERO(&rdset); FD_SET(lfd, &rdset); struct timeval tm = {10, 0}; //设置超时时长10s int ret = select(lfd+1, &rdset, NULL, NULL, &tm); if(ret == 0) { // 超时, 不等了 return; } //有连接请求 else if(ret > 0) { accept(lfd, addr, len); // 必然不阻塞 }
2.read、write超时
同accept。
/*read*/ // 最大的文件描述符是: 通信的 connfd fd_set rdset; FD_ZERO(&rdset); FD_SET(connfd, &rdset); struct timeval tm = {10, 0}; int ret = select(connfd+1, &rdset, NULL, NULL, &tm); if(ret == 0) { // 超时, 不等了 } //有数据可读 else if(ret > 0) { read(connfd, buf, size); // 必然不阻塞 } /*write*/ //写缓冲区被写满,在写会造成阻塞 //对方不接收数据,本地写缓冲区一直阻塞。 fd_set wrset; FD_ZERO(&wrset); FD_SET(connfd, &wrset); struct timeval tm = {10, 0}; int ret = select(connfd+1, NULL, &wrset, NULL, &tm); if(ret == 0) { // 超时, 不等了, 写缓冲区还是满的 } else if(ret > 0) { // 写缓冲区可写 write(connfd, buf, size); }
3.connect超时
connect内部有一个超时检测,connect是一个阻塞函数,会阻塞等待服务器返回连接的结果。
如果要完成自己的connect超时检测,首先要将connect变成非阻塞。然后根据connect函数返回判断。
Posix定义了与select/epoll和非阻塞connect相关的规定:
connect连接建立成功,Socket描述符变为可写。
connect连接建立失败,Socket描述符既可读又可写。(由于有未决的错误,从而可读又可写)
连接失败,错误判定方式:
使用select检测,socket可读可写,只能在可读集合通过getsockopt获取错误代码。
使用epoll检测,socket可读可写,只能在EPOLLERR中通过getsockopt获取错误码。
// 1. 设置connect的非阻塞, 修改fd的属性 int fl = fcntl(fd, F_GETFL); fl |= O_NONBLOCK; fcntl(fd, F_SETFL, fl); // 2. 连接服务器 int ret = conect(connfd, serveraddr, len); // 返回-1: 连接失败, errno==EINPROGRESS,说明在连接过程中 if(ret == -1 && errno == EINPROGRESS) { fd_set wrset; FD_ZERO(&wrset); FD_SET(connfd, &wrset); struct timeval tm = {10, 0}; int ret = select(connfd+1, NULL, &wrset, NULL, &tm); if(ret == 0) { // 超时, 连接还没有完成 } else if(ret > 0) // ==1 { // 判断连接是成功还是失败 int op; int len = sizeof(op); getsockopt(connfd, SOL_SOCKET, SO_ERROR, &op, &len); if(op == 0) { // 正常 } else if(op == -1) { // 错误 } } } //4.设置回阻塞模式 fcntl(fd, F_SETFL, fl&(~O_NONBLOCK);
windows下设置阻塞模式
unsigned long ul = 1;
ioctlsocket(this->m_connfd, FIONBIO, &ul);
非阻塞
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
// 判断错误
sockfd: 文件描述符
level: SOL_SOCKET
optname: SO_ERROR
optval: int 类型, 存储错误状态
optlen: optval大小对一个的以地址