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进行读监听

目录
相关文章
|
4月前
|
Linux
Linux 下的五种 IO 模型详细介绍
根据上述定义,我们的前4种模型——阻塞式I/O模型、非阻塞式I/O模型、I/O复用模型和信号驱动式I/O模型都是同步I/O模型,因为其中真正的I/O操作(recvfrom )将阻塞进程。异步请求:A调用B,B的处理是异步的,B在接到请求后先告诉A我已经接到请求了,然后异步去处理,处理完之后通过回调等方式再通知A。和上面的阻塞IO模型相比,非阻塞IO模型在内核数据没准备好,需要进程阻塞的时候,就返回一个错误,以使得进程不被阻塞。阻塞请求:A调用B,A一直等着B的返回,别的事情什么也不干。
36 0
Linux 下的五种 IO 模型详细介绍
|
4月前
|
Unix Linux
Linux五种IO模型
Linux五种IO模型
|
5月前
|
存储 Linux 程序员
Linux网络-五种IO模型
Linux网络-五种IO模型
|
网络协议 Linux 调度
Linux 五种Io模型
异步的概念和同步相对。当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者。比如:调用aio_read系统调用时,不必等IO操作完成就直接返回,调用结果通过信号来通知调用者。
Linux 五种Io模型
|
监控 容器
系统编程之高级文件IO(十三)——IO多路复用-select
系统编程之高级文件IO(十三)——IO多路复用-select
系统编程之高级文件IO(十三)——IO多路复用-select
|
网络协议 Linux API
系统编程之文件IO(一)——概述
系统编程之文件IO(一)——概述
系统编程之文件IO(一)——概述
1.17~1.30 标准C库IO函数和Linux系统IO函数
1.17~1.30 标准C库IO函数和Linux系统IO函数
1.17~1.30 标准C库IO函数和Linux系统IO函数
|
缓存 运维 Unix
浅析Linux中的五种IO模型(上)
IO是什么 I/O(Input/Output),中文名为输入/输出,指的是一切操作程序或设备与计算机之间发生的数据传输的过程。它分为IO设备和IO接口两个部分。
|
网络协议 Linux 调度
浅析Linux中的五种IO模型(下)
IO是什么 I/O(Input/Output),中文名为输入/输出,指的是一切操作程序或设备与计算机之间发生的数据传输的过程。它分为IO设备和IO接口两个部分。
|
数据库
操作系统第五章_01 IO设备的基本概念和分类 IO控制器 IO控制方式
操作系统第五章_01 IO设备的基本概念和分类 IO控制器 IO控制方式
365 0
操作系统第五章_01 IO设备的基本概念和分类 IO控制器 IO控制方式