zoukankan      html  css  js  c++  java
  • Linux 网络编程详解十一

    /**
     * read_timeout - 读超时检测函数,不含读操作
     * @fd:文件描述符
     * @wait_seconds:等待超时秒数,如果为0表示不检测超时
     * 成功返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
     * */
    int read_timeout(int fd, unsigned int wait_seconds)
    {
        int ret = 0;
        if (wait_seconds > 0)
        {
            //定义文件描述符集合
            fd_set readfds;
            //清空文件描述符
            FD_ZERO(&readfds);
            //将当前文件描述符添加集合中
            FD_SET(fd, &readfds);
            //定义时间变量
            struct timeval timeout;
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
            } while (ret == -1 && errno == EINTR);
            //ret==-1时,返回的ret正好就是-1
            if (ret == 0)
            {
                errno = ETIMEDOUT;
                ret = -1;
            } else if (ret == 1)
            {
                ret = 0;
            }
        }
        return ret;
    }
    /**
     * write_timeout - 写超时检测函数,不含写操作
     * @fd:文件描述符
     * @wait_seconds:等待超时秒数,如果为0表示不检测超时
     * 成功返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
     * */
    int write_timeout(int fd, unsigned int wait_seconds)
    {
        int ret = 0;
        if (wait_seconds > 0)
        {
            //定义文件描述符集合
            fd_set writefds;
            //清空集合
            FD_ZERO(&writefds);
            //添加文件描述符
            FD_SET(fd, &writefds);
            //定义时间变量
            struct timeval timeout;
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                ret = select(fd + 1, NULL, &writefds, NULL, &timeout);
            } while (ret == -1 && errno == EINTR);
            if (ret == 0)
            {
                errno = ETIMEDOUT;
                ret = -1;
            } else if (ret == 1)
            {
                ret = 0;
            }
        }
        return ret;
    }
    /**
     * accept_timeout - 带超时accept (方法中已执行accept)
     * @fd:文件描述符
     * @addr:地址结构体指针
     * @wait_seconds:等待超时秒数,如果为0表示不检测超时
     * 成功返回已连接的套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT
     * */
    int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
    {
        int ret = 0;
        if (wait_seconds > 0)
        {
            /*
             * 说明:accept和connect都会阻塞进程,accept的本质是从listen的队列中读一个连接,是一个读事件
             * 三次握手机制是由TCP/IP协议实现的,并不是由connect函数实现的,connect函数只是发起一个连接,
             * connect并非读写事件,所以只能设置connect非阻塞,而用select监测写事件(读事件必须由对方先发送报文,时间太长了)
             * 所以accept可以由select管理
             * 强调:服务端套接字是被动套接字,实际上只有读事件
             * */
            fd_set readfds;
            FD_ZERO(&readfds);
            FD_SET(fd, &readfds);
            struct timeval timeout;
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
            } while (ret == -1 && errno == EINTR);
            if (ret == -1)
            {
                return ret;
            } else if (ret == 0)
            {
                ret = -1;
                errno = ETIMEDOUT;
           return ret; }
    //成功无需处理,直接往下执行 } //一旦检测出select有事件发生,表示有三次握手成功的客户端连接到来了 //此时调用accept不会被阻塞 if (addr != NULL) { socklen_t len = sizeof(struct sockaddr_in); ret = accept(fd, (struct sockaddr *) addr, &len); } else { ret = accept(fd, NULL, NULL); } return ret; }
    /**
     * activate_nonblock - 设置套接字非阻塞
     * @fd:文件描述符
     * 成功返回0,失败返回-1
     * */
    int activate_nonblock(int fd)
    {
        int ret = 0;
        int flags = fcntl(fd, F_GETFL);
        if (flags == -1)
            return -1;
        flags = flags | O_NONBLOCK;
        ret = fcntl(fd, F_SETFL, flags);
        if (ret == -1)
            return -1;
        return ret;
    }
    
    /**
     * deactivate_nonblock - 设置套接字阻塞
     * @fd:文件描述符
     * 成功返回0,失败返回-1
     * */
    int deactivate_nonblock(int fd)
    {
        int ret = 0;
        int flags = fcntl(fd, F_GETFL);
        if (flags == -1)
            return -1;
        flags = flags & (~O_NONBLOCK);
        ret = fcntl(fd, F_SETFL, flags);
        if (ret == -1)
            return -1;
        return ret;
    }
    
    /**
     * connect_timeout - 带超时的connect(方法中已执行connect)
     * @fd:文件描述符
     * @addr:地址结构体指针
     * @wait_seconds:等待超时秒数,如果为0表示不检测超时
     * 成功返回0.失败返回-1,超时返回-1并且errno = ETIMEDOUT
     * */
    int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
    {
        int ret = 0;
        //connect()函数是连接服务器,本来connect会阻塞,但是设置未阻塞之后,
        //客户端仍然会三次握手机制,如果三次握手失败,那么客户端一定无法向文件描述符中写入数据
        //如果连接成功,那么客户端就可以向文件描述符写入数据了,
        //所以交给select监管的文件描述符如果可以写,说明连接成功,不可以写说明连接失败
    
        //设置当前文件描述符未阻塞--设置非阻塞之后,
        //connect在网络中非常耗时,所以需要设置成非阻塞,如果有读事件,说明可能连接成功
        //这样有利于做超时限制
        if (wait_seconds > 0)
        {
            if (activate_nonblock(fd) == -1)
                return -1;
        }
        ret = connect(fd, (struct sockaddr *) addr, sizeof(struct sockaddr));
        if (ret == -1 && errno == EINPROGRESS)
        {
            fd_set writefds;
            FD_ZERO(&writefds);
            FD_SET(fd, &writefds);
            struct timeval timeout;
            timeout.tv_sec = wait_seconds;
            timeout.tv_usec = 0;
            do
            {
                ret = select(fd + 1, NULL, &writefds, NULL, &timeout);
            } while (ret == -1 && errno == EINTR);
            //ret==-1 不需要处理,正好给ret赋值
            //select()报错,但是此时不能退出当前connect_timeout()函数
            //因为还需要取消文件描述符的非阻塞
            if (ret == 0)
            {
                errno = ETIMEDOUT;
                ret = -1;
            } else if (ret == 1)
            {
                //ret返回为1(表示套接字可写),可能有两种情况,一种是连接建立成功,一种是套接字产生错误,
                //此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。
                int err = 0;
                socklen_t len = sizeof(err);
                ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &len);
                if (ret == 0 && err != 0)
                {
                    errno = err;
                    ret = -1;
                }
                //说明套接字没有发生错误,成功
            }
        }
        if (wait_seconds > 0)
        {
            if (deactivate_nonblock(fd) == -1)
                return -1;
        }
        return ret;
    }
  • 相关阅读:
    update mysql row (You can't specify target table 'x' for update in FROM clause)
    MySQL中使用group by 是总是出现1055的错误
    centos7下查看mysql配置文件适用顺序
    mysql中EXPLAIN 的作用
    查看mysql库大小,表大小,索引大小
    mysql互换表中两列数据方法
    mysql在建表语句中添加索引
    使用vue-lazyload实现图片懒加载
    vue使用jsonp获取数据,开发热卖推荐组件
    vue实现首页导航面板组件
  • 原文地址:https://www.cnblogs.com/zhanggaofeng/p/6160490.html
Copyright © 2011-2022 走看看