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


    首先需要了解的是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端口。

    服务器端代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    #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 ", 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 ", buffer);
            }
            close(clifd);
        }
         
        close(listenfd1);
        close(listenfd2);
     
        return 0;
    }

    客户端1 代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #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代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #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进行读监听

  • 相关阅读:
    【缓存】缓存穿透、缓存并发、热点缓存解决方案
    【缓存】redis缓存设计
    【AOP】Spring AOP 和 AspectJ
    disruptor
    Spring Boot application starters
    【日志】log4j2 yml
    PHP中间件--ICE
    docker 简单入门(一)
    redis、memcache和mongodb各自的优缺点是什么
    MYSQL三大范式
  • 原文地址:https://www.cnblogs.com/jiangzhaowei/p/8972180.html
Copyright © 2020-2023  润新知