C语言 select()多路复用

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

struct socket_t
{
    struct sockaddr_in cli_addr;
    int cli_fb;
};
int num;   // 在线人数
int count; // 掉线线人数
struct socket_t people[10];

int main(int argc, char const *argv[])
{
    // 1、创建socket
    int fb = socket(AF_INET, SOCK_STREAM, 0);
    if (fb < 0)
    {
        perror("socket");
        return 0;
    }
    // 绑定地址
    struct sockaddr_in addr;                  // 定义结构体
    addr.sin_family = AF_INET;                // 设置为IPV4
    addr.sin_port = htons(8966);              // 设置端口号为8080  htons():将本地网络字序改为网络传输统一的大端存储的字序
                                              // ntohs() 将网络字序转化为本地主机字序
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // 设置网络IP    inet_addr():将字符串转化为整型地址
    // 2、绑定socket
    if (bind(fb, (struct sockaddr *)&addr, sizeof(addr)) == 0)
    {
        perror("bind");
    }
    else
    {
        perror("bind fail");
    }

    // 3、设置监听,,模式
    if (listen(fb, 10))
    {
        perror("listen");
    }
    int i = 0;
    num = 0;
    count = fb;
    printf("输入文件描述符进行通信\n");
    while (1)
    {
        // 多路复用
        fd_set rfbs;
        // 清空集合
        FD_ZERO(&rfbs);
        // 添加服务器描述符
        FD_SET(fb, &rfbs);
        //添加标志输入描述符
        FD_SET(0, &rfbs);
        for (int x = 0; x < 10; x++)
        {
            if (people[x].cli_fb != 0)
            {
                printf("在线人员描述符:%d\n", people[x].cli_fb);
                //将连接的客户机加入监听
                FD_SET(people[x].cli_fb, &rfbs);
            }
        }
        // 设置超时连接时间
        // struct timeval timeout;
        // timeout.tv_sec = 10;
        // timeout.tv_usec=0;

        // 开始监听服务器
        char msg[521];
        int ret = select(count + 1, &rfbs, NULL, NULL, NULL);
        if (ret > 0)
        {
            printf("有活跃fb\n");
            if (FD_ISSET(fb, &rfbs))
            {
                printf("有人连接\n");
                printf("已连接%d人", num + 1);
                for (i = 0; i < 10; i++)
                {
                    if (people[i].cli_fb == 0)
                    {
                        break;
                    }
                }
                socklen_t len = sizeof(people->cli_addr);
                people[i].cli_fb = accept(fb, (struct sockaddr *)&people[i].cli_addr, &len);
                count = people[i].cli_fb;
                num++;
            }

            for (int j = 0; j < 10; j++)
            {
                if (people[j].cli_fb == 0)
                {
                    continue;
                }
                if (FD_ISSET(people[j].cli_fb, &rfbs))
                {
                    int temp = read(people[j].cli_fb, msg, sizeof(msg));
                    if (temp <= 0)
                    {
                        //将掉线的客户机移出监听
                        FD_CLR(people[j].cli_fb, &rfbs);

                        num--;
                        printf("%d掉线\n", people[j].cli_fb);
                        memset(&people[j], 0, sizeof(struct socket_t));
                    }
                    else
                    {
                        printf("收到%d的新消息%s\n", people[j].cli_fb, msg);
                        break;
                    }
                }
                if (msg[0] == '1')
                {
                    break;
                }
                if (FD_ISSET(0, &rfbs))
                {
                    int op;
                    scanf("%d", &op);
                    memset(msg, 0, sizeof(msg));
                    printf("请输入信息\n");
                    scanf("%s", msg);
                    write(op, msg, strlen(msg));
                }
            }
        }
    }

    for (int a = 0; a < 10; a++)
    {
        if (people[a].cli_fb == 0)
        {
            continue;
        }
        close(people[a].cli_fb);
    }
    close(fb);
    return 0;
}

        这这段代码主要实现TCP服务端时时监听客户机的连接,用实现一对多通行;

C语言 select()多路复用_第1张图片

 

SELECT(2)                                                        Linux Programmer's Manual                                                        SELECT(2)

NAME
       select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O multiplexing

SYNOPSIS
       /* According to POSIX.1-2001, POSIX.1-2008 */

函数头文件


       #include

       /* According to earlier standards */
       #include
       #include
       #include

       #include

函数原型       

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

功能;多路复用,同时监听多个阻塞的函数,并在其被唤醒时做对应的处理。

参数:

        nfds:监听的描述符中最大值加1;

        readfds:监听集合;

       readfds: 指定要让内核测试读条件的 fd 集合,如果不需要或不关心此类事件的话,可设置为NULL;
        writefds: 指定要让内核测试写条件的 fd 集合,如果不需要或不关心此类事件的话,可设置为NULL;
        exceptfds: 指定要让内核测试异常条件的 fd 集合,如果不需要或不关心此类事件的话,可设置为NULL;
timeout: 设置select的超时时间;

               timeout通常有以下三种设置方式:

               NULL:阻塞式等待,select将一直被阻塞,直到某个文件描述符上发送了事件;

               0:非阻塞式等待,调用select后检测文件描述符的状态,然后立即返回;

               特定的时间值:阻塞式等待一定时间,若期间没有事件发生,select将超时返回

        

        

函数全家桶

       void FD_CLR(int fd, fd_set *set);                

        功能:移除监听文件描述符

        参数:   fb:需要移除的描述符fb   

                      fd_set *set:监听集合;


       int  FD_ISSET(int fd, fd_set *set);              

        功能:处理活跃的文件描述符

        参数: fb:监听到活跃的文件描述符fb;

                    fd_set *set:监听集合;


       void FD_SET(int fd, fd_set *set);               

        功能:加入监听描述符

        参数:fb:需要加入监听的文件描述

                  fd_set *set:监听集合;


       void FD_ZERO(fd_set *set);                     

        功能:清空监听描述符

        参数: fd_set *set:监听集合;

 

你可能感兴趣的:(C语言,c语言,开发语言)