zoukankan      html  css  js  c++  java
  • Linux IO模型漫谈(5) IO复用模型之select

    首先需要了解的是select函数:

    select函数

    #include<sys/select.h>

    #include<sys/time.h>

    int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval * timeout);

    返回:就绪描述字的正数目,0——超时,-1——出错

    参数解释:

    maxfd: 最大的文件描述符(其值应该为最大的文件描述符字 + 1)

    readset: 内核读操作的描述符字集合

    writeset:内核写操作的描述符字集合

    exceptionset:内核异常操作的描述符字集合

    timeout:等待描述符就绪需要多少时间。NULL代表永远等下去,一个固定值代表等待固定时间,0代表根本不等待,检查描述字之后立即返回。

    注意:readset,writeset,exceptionset都是值-结果参数,意思就是他们传进入指针进去,函数根据指针可以修改对应的fd_set

    fd_set集合操作

    fd_set和名字一样,是一个描述符的集合。有下面几个操作:

    void FD_ZERO(fd_set *fdset); /* 将所有fd清零 */

    void FD_SET(int fd, fd_set *fdset); /* 增加一个fd */

    void FD_CLR(int fd, fd_set *fdset); /* 删除一个fd */

    int FD_ISSET(int fd, fd_set *fdset); /* 判断一个fd是否有设置 */

    我们现在要做一个select使用的server,server监听两个端口(7778和7779)的socket。再使用两个cli,一个client连接到7778端口,另一个client连接到7779端口。

    服务器端代码:

    #include <stdio.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <unistd.h>
    
    int main(int argc, char *argv[])
    {
        //这个服务器同时监听7777和7778两个端口
        
        //绑定监听7779端口的fd
        int listenfd1;
        struct sockaddr_in serv_addr1;
        listenfd1 = socket(AF_INET, SOCK_STREAM, 0);
        
        bzero((char *) &serv_addr1, sizeof(serv_addr1));
        serv_addr1.sin_family = AF_INET;
        serv_addr1.sin_port = htons(7777);
        serv_addr1.sin_addr.s_addr = INADDR_ANY;
        
        bind(listenfd1, (struct sockaddr *) &serv_addr1, sizeof(serv_addr1));
        listen(listenfd1, 5);
        
        //绑定监听7778端口的fd
        int listenfd2;
        struct sockaddr_in serv_addr2;
        listenfd2 = socket(AF_INET, SOCK_STREAM, 0);
        
        bzero((char *) &serv_addr2, sizeof(serv_addr2));
        serv_addr2.sin_family = AF_INET;
        serv_addr2.sin_port = htons(7778);
        serv_addr2.sin_addr.s_addr = INADDR_ANY;
        
        bind(listenfd2, (struct sockaddr *) &serv_addr2, sizeof(serv_addr2));
        listen(listenfd2, 5);
        
        
        int maxfd;
        //为什么这里设置两个fd_set?每次select的时候函数会把没有事件发生的描述字清零,所以需要两个集合
        fd_set allset, rset;
        maxfd = listenfd1;
        if(listenfd2 > maxfd) {
            maxfd = listenfd2;
        }
        
        FD_ZERO(&allset);
        FD_SET(listenfd1, &allset);
        FD_SET(listenfd2, &allset);
        
        int clifd, clilen;
        struct sockaddr_in cli_addr;
        char buffer[256];
        for(;;) {
            rset = allset;
            select(maxfd + 1, &rset, NULL, NULL, NULL);
            
            //如果是listenfd1 获取消息
            if(FD_ISSET(listenfd1, &rset)) {
                clilen = sizeof(cli_addr);
                clifd = accept(listenfd1, (struct sockaddr *) &cli_addr, &clilen);
                
                bzero(buffer, 256);
                read(clifd, buffer, 255);
                printf("Listenfd1 Message is:%s\r\n", buffer);
            }
            
            //如果是listenfd1 获取消息
            if(FD_ISSET(listenfd2, &rset)) {
                clilen = sizeof(cli_addr);
                clifd = accept(listenfd2, (struct sockaddr *) &cli_addr, &clilen);
                
                bzero(buffer, 256);
                read(clifd, buffer, 255);
                printf("Listenfd2 Message is:%s\r\n", buffer);
            }
            close(clifd);
        }
        
        close(listenfd1);
        close(listenfd2);
    
        return 0;
    }

    客户端1 代码:

    #include <stdio.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[])
    {
        int socketfd, n;
        socketfd = socket(AF_INET, SOCK_STREAM, 0);
        
        struct sockaddr_in serv_addr;
    		
        bzero((char *)&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(7778);
    		
        connect(socketfd,(struct sockaddr *)  &serv_addr, sizeof(serv_addr));
    		
        write(socketfd, "client message", 14);
        return 0;
    
    }

    客户端2代码:

    #include <stdio.h>
    #include <sys/socket.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <string.h>
    #include <unistd.h>
    
    int main(int argc, char* argv[])
    {
        int socketfd, n;
        socketfd = socket(AF_INET, SOCK_STREAM, 0);
        
        struct sockaddr_in serv_addr;
    		
        bzero((char *)&serv_addr, sizeof(serv_addr));
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_port = htons(7779);
    		
        connect(socketfd,(struct sockaddr *)  &serv_addr, sizeof(serv_addr));
    		
        write(socketfd, "client message", 14);
        return 0;
    
    }

    调用步骤:

    1 启动服务器端

    2 启动客户端1

    3 启动客户端2

    4 服务器端表

    客户端启动:

    clip_image001

    服务端表现:

    clip_image002

    这里就是使用select函数对多个socket进行读监听

    参考文章:

    unix下网络编程之I/O复用(二)

    http://www.cnblogs.com/coser/archive/2012/02/29/2373478.html

    (摘)I/O多路复用详解(一)

    http://konglingchun.is-programmer.com/posts/12146.html

    《Unix网络编程》

    Linux/Unix IO多路复用之select网络编程(含源码)

    http://www.cnblogs.com/ggjucheng/archive/2012/01/17/2324859.html

    实时了解作者更多技术文章,技术心得,请关注微信公众号“轩脉刃的刀光剑影”

    本文基于署名-非商业性使用 3.0许可协议发布,欢迎转载,演绎,但是必须保留本文的署名叶剑峰(包含链接http://www.cnblogs.com/yjf512/),且不得用于商业目的。如您有任何疑问或者授权方面的协商,请与我联系

  • 相关阅读:
    CodeForces 19D Points (线段树+set)
    FZU 2105 Digits Count
    HDU 5618 Jam's problem again(三维偏序,CDQ分治,树状数组,线段树)
    HDU 5634 Rikka with Phi (线段树)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高 转圈游戏(暴力快速幂)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
    Java实现 蓝桥杯 算法提高VIP Substrings(暴力)
  • 原文地址:https://www.cnblogs.com/yjf512/p/2539755.html
Copyright © 2011-2022 走看看