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);

  非阻塞

  ul = 0;
  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大小对一个的以地址

posted @ 2019-11-08 10:39  qetuo[  阅读(722)  评论(0编辑  收藏  举报