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 服务器端表

 

客户端启动:

服务端表现:

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

时间: 2024-09-11 15:04:58

Linux IO模型漫谈(5)- IO复用模型之select的相关文章

Linux IO模型漫谈(2)

不管Linux的IO模型的阻塞同步分类是如何分类,几种IO模型的具体实现是确定的.这里借用<Unix 网络编程:卷一>的图片说明. 1 阻塞式IO模型 这个模型也是最容易理解的 程序调用和我们基本的程序编写是一致的: fd = connect() write(fd) read(fd) close(fd) 程序的read必须在write之后执行,当write阻塞住了,read就不能执行下去 2 非阻塞IO模型 从图中可以看出来,这是一个轮询的过程 每次用户询问内核是否有数据报准备好(文件描述符缓

Linux IO模型漫谈(1)

基础知识 Linux将所有外部设备都看做一个文件来进行操作.因此,linux对所有外部设备的操作都可以看做是文件的操作.文件的操作当然需要有个标示描述它,这就是文件描述符(file descriptor). linux的IO操作如何形象理解呢? 我们说网络socket的read()是一个IO操作命令,具体流程是这样的: 应用程序调用read命令,通知内核需要做读取数据操作 内核创建一个文件描述符 内核从物理层收到读数据的命令,从网络中获取数据包 数据包传递到TCP/IP层,解析数据包的头 内核将

Linux IO模型漫谈(6)- 信号驱动IO模型

Unix上有定义了许多信号.源自Berkeley的实现使用的是SIGIO信号来支持套接字和终端设备上的信号驱动IO. 信号驱动IO模型主要是在UDP套接字上使用,在TCP套接字上几乎是没有什么使用的. 在UDP上,SIGIO信号会在下面两个事件的时候产生: 1 数据报到达套接字 2 套接字上发上一部错误 因此我们很容易判断SIGIO出现的时候,如果不是发生错误,那么就是有数据报到达了. 而在TCP上,由于TCP是双工的,它的信号产生过于平凡,并且信号的出现几乎没有告诉我们发生了什么事情.因此对于

Linux 下的五种 IO 模型详细介绍_Linux

概念说明 用户空间与内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟存储空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限.为了保证用户进程不能直接操作内核(kernel),保证内核的安全,操作系统将虚拟空间划分为两部分,一部分为内核空间,一部分为用户空间.针对linux操作系统而言,将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为内核空

arm驱动linux异步通知与异步IO【转】

  转自:http://blog.csdn.net/chinazhangzhong123/article/details/51638793 <[ arm驱动] linux异步通知与 异步IO>涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个:可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动三个 描述:设备文件IO访问:阻塞与非阻塞io访问,poll函数提供较好的解决设备访问的机制,但是如果有了异步通知整套机制就更加完整了 一.阻塞 I/O,非阻塞IO,

btrfs cfq, noop, deadline三种IO调度策略下的IO性能表现

btrfs 格式化和挂载参数: # mkfs.btrfs -m raid10 -d raid10 -n 4096 -f /dev/sdb /dev/sdc /dev/sdd /dev/sde # mount -o noatime,nodiratime,ssd_spread,discard,space_cache /dev/sdb /data01 从结果来看,建议使用deadline. 测试结果: [root@digoal data01]# echo noop > /sys/block/sdb/q

mcu-我对一个传感器进行中断处理,当传感器动作时,会拉低IO口,当IO口为低时会有一些动作

问题描述 我对一个传感器进行中断处理,当传感器动作时,会拉低IO口,当IO口为低时会有一些动作 这是个检测到位与否的传感器,到位以后这个传感器就一直把IO拉低了,那么就一直在中断里面吗?好像不是啊,如果中断触发选择下经验只是在到位的那一瞬间才进入中断啊,然后就去一直低电平,但是并不在中断里面啊 解决方案 在使用cpu引脚中断处理外部事务的时候,根据你外部事物或传感器的特性配置CPU引脚的中断条件,依据你的说法正常时为高电平,动作时为低电平,并且传感器是一直有效电平也一直为低电平,这样的话,建议使

泛函编程(37)-泛函Stream IO:通用的IO处理过程-Free Process

  在上两篇讨论中我们介绍了IO Process:Process[I,O],它的工作原理.函数组合等.很容易想象,一个完整的IO程序是由 数据源+处理过程+数据终点: Source->Process->Sink所组成的.我们发现:Process[I,O]本身是无法兼顾Source和Sink的功能.而独立附加的Source和Sink又无法有效地与Process[I,O]进行函数组合(functional composition).   实际上Process[I,O]是一种固定单一输入类型(sin

《CUDA C编程权威指南》——第3章 CUDA执行模型 3.1 CUDA执行模型概述

第3章 CUDA执行模型 本章内容: 通过配置文件驱动的方法优化内核 理解线程束执行的本质 增大GPU的并行性 掌握网格和线程块的启发式配置 学习多种CUDA的性能指标和事件 了解动态并行与嵌套执行 通过上一章的练习,你已经学会了如何在网格和线程块中组织线程以获得最佳的性能.尽管可以通过反复试验找到最佳的执行配置,但你可能仍然会感到疑惑,为什么选择这样的执行配置会更好.你可能想知道是否有一些选择网格和块配置的准则.本章将会回答这些问题,并从硬件方面深入介绍内核启动配置和性能分析的信息. 3.1

泛函编程(35)-泛函Stream IO:IO处理过程-IO Process

    IO处理可以说是计算机技术的核心.不是吗?使用计算机的目的就是希望它对输入数据进行运算后向我们输出计算结果.所谓Stream IO简单来说就是对一串按序相同类型的输入数据进行处理后输出计算结果.输入数据源可能是一串键盘字符.鼠标位置坐标.文件字符行.数据库纪录等.如何实现泛函模式的Stream IO处理则是泛函编程不可或缺的技术. 首先,我们先看一段较熟悉的IO程序: 1 import java.io._ 2 def linesGt4k(fileName: String): IO[Boo