`

柳大的Linux游记·基础篇(5)select IO复用机制

 
阅读更多

柳大的Linux游记·基础篇(5)select IO复用机制

  • Author: Poechant
  • Blog:blog.CSDN.net/Poechant
  • Email: zhongchao.ustc#gmail.com (#->@)
  • Date: March 13th, 2012
  • Copyright © 柳大·Poechant

1 基本原理

Resize icon

注:select 原理图,摘自IBM iSeries 信息中心

1 数据结构与函数原型

1.1 select

  • 函数原型
       int select(
          int nfds,
          fd_set *readset,
          fd_set *writeset,
          fd_set* exceptset,
          struct timeval *timeout
       );
    
  • 头文件
    • select位于:
      #include <sys/select.h>
      
    • struct timeval位于:
      #include <sys/time.h>
      
  • 返回值

    返回对应位仍然为1的fd的总数。

  • 参数
    • nfds:第一个参数是:最大的文件描述符值+1;
    • readset:可读描述符集合;
    • writeset:可写描述符集合;
    • exceptset:异常描述符;
    • timeout:select 的监听时长,如果这短时间内所监听的 socket 没有事件发生。

1.2 fd_set

1.2.1 清空描述符集合

FD_ZERO(fd_set *)

1.2.2 向描述符集合添加指定描述符

FD_SET(int, fd_set *)

1.2.3 从描述符集合删除指定描述符

FD_CLR(int, fd_set *)

1.2.4 检测指定描述符是否在描述符集合中

FD_ISSET(int, fd_set *)

1.2.5 描述符最大数量

#define FD_SETSIZE 1024

1.3 描述符集合

可读描述符集合中可读的描述符,为1,其他为0;可写也类似。异常描述符集合中有异常等待处理的描述符的值为1,其他为0。

1.4 ioctl

  • 函数原型:

      int ioctl(int handle, int cmd,[int *argdx, int argcx]);
    
  • 头文件:

      #include <sys/ioctl.h>
    
  • 返回值:

    • 0 - 成功
    • 1 - 失败

2 示例

程序各部分的解释在注释中。

#include <sys/socket.h>
#include <string.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>

#define TRUE  1
#define FALSE 0

int main(int argc, char *argv[])
{
    int i, len, rc, on = TRUE;
    int listen_sd, new_sd = 0, max_sd;
    int desc_ready;
    char buffer[80];
    int close_conn, end_server = FALSE;
    struct sockaddr_in server_addr;
    struct timeval timeout;
    struct fd_set master_set, working_set;

    // Listen
    listen_sd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_sd < 0)
    {
        perror("socket() failed");
        exit(-1);
    }

    // Set socket options
    rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *) &on, sizeof(on));
    if (rc < 0)
    {
        perror("setsockopt() failed");
        close(listen_sd);
        exit(-1);
    }

    // Set IO control
    rc = ioctl(listen_sd, FIONBIO, (char *) &on);
    if (rc < 0)
    {
        perror("ioctl() failed");
        close(listen_sd);
        exit(-1);
    }

    // Bind
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(atoi(argv[1]));
    rc = bind(listen_sd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (rc < 0)
    {
        perror("bind() failed\n");
        close(listen_sd);
        exit(-1);
    }

    // Listen
    rc = listen(listen_sd, 32);
    if (rc < 0)
    {
        perror("listen() failed\n");
        close(listen_sd);
        exit(-1);
    }

    // Intialize sd set
    FD_ZERO(&master_set);
    max_sd = listen_sd;
    FD_SET(listen_sd, &master_set);

    timeout.tv_sec = 3 * 60;
    timeout.tv_usec = 0;

    // Start
    do
    {
        // Copy master_set into working_set
        memcpy(&working_set, &master_set, sizeof(master_set));

        printf("Waiting on select()...\n");
        rc = select(max_sd + 1, &working_set, NULL, NULL, &timeout);
        if (rc < 0)
        {
            perror("  select() failed\n");
            break;
        }
        if (rc == 0)
        {
            printf("  select() timed out. End program.\n");
            break;
        }

        desc_ready = rc; // number of sds ready in working_set

        // Check each sd in working_set
        for (i = 0; i <= max_sd && desc_ready > 0; ++i)
        {
            // Check to see if this sd is ready
            if (FD_ISSET(i, &working_set))
            {
                --desc_ready;

                // Check to see if this is the listening sd
                if (i == listen_sd)
                {
                    printf("  Listeing socket is readable\n");
                    do
                    {
                        // Accept
                        new_sd = accept(listen_sd, NULL, NULL);

                        // Nothing to be accepted
                        if (new_sd < 0)
                        {
                            // All have been accepted
                            if (errno != EWOULDBLOCK)
                            {
                                perror("  accept() failed\n");
                                end_server = TRUE;
                            }
                            break;
                        }

                        // Insert new_sd into master_set
                        printf("  New incoming connection - %d\n", new_sd);
                        FD_SET(new_sd, &master_set);
                        if (new_sd > max_sd)
                        {
                            max_sd = new_sd;
                        }
                    }
                    while (new_sd != -1);
                }
                // This is not the listening sd
                else
                {
                    close_conn = FALSE;
                    printf("  Descriptor %d is avaliable\n", i);
                    do
                    {
                        rc = recv(i, buffer, sizeof(buffer), 0);

                        // Receive data on sd "i", until failure occurs
                        if (rc < 0)
                        {
                            // Normal failure
                            if (errno != EWOULDBLOCK)
                            {
                                perror("  recv() failed\n");
                                close_conn = TRUE;
                            }
                            break;
                        }

                        // The connection has been closed by the client
                        if (rc == 0)
                        {
                            printf("  Connection closed\n");
                            close_conn = TRUE;
                            break;
                        }

                        /* Receiving data succeeded and echo it back
                           the to client */
                        len = rc;
                        printf("  %d bytes received\n", len);
                        rc = send(i, buffer, len, 0);
                        if (rc < 0)
                        {
                            perror("  send() failed");
                            close_conn = TRUE;
                            break;
                        }
                    }
                    while (TRUE);

                    // If unknown failure occured
                    if (close_conn)
                    {
                        // Close the sd and remove it from master_set
                        close(i);
                        FD_CLR(i, &master_set);

                        // If this is the max sd
                        if (i == max_sd)
                        {
                            // Find the max sd in master_set now
                            while (FD_ISSET(max_sd, &master_set) == FALSE)
                            {
                                --max_sd;
                            }
                        } // End of if (i == max_sd)
                    } // End of if (close_conn)
                }
            }
        }
    }
    while (end_server == FALSE);

    /* Close each sd in master_set */
    for (i = 0; i < max_sd; ++i)
    {
        if (FD_ISSET(i, &master_set))
        {
            close(i);
        }
    }

    return 0;
}

参考

  1. http://publib.boulder.ibm.com/infocenter/iseries/v5r3/index.jsp?topic=%2Frzab6%2Frzab6xnonblock.htm

-

转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant

-

分享到:
评论

相关推荐

    Linux1.0核心游记

    本书不仅适合初学者了解Linux的基本概念和发展历程,也适合有一定基础的技术人员进一步探索Linux内核的内部机制。 #### 二、Linux 1.0内核概览 Linux 1.0内核发布于1994年,标志着Linux系统开始走向成熟。这一版本...

    Linux 1.0核心游记

    Linux 1.0的成功也为后续版本奠定了坚实的基础,随着技术的进步,Linux核心不断迭代更新,目前最新的稳定版已经超过了5.x系列。 在"codefans.net"这个文件中,我们可以期待找到对Linux 1.0核心的深度剖析,包括其...

    Linux1.0核心游记.rar

    5. **网络支持**:Linux1.0内置了对TCP/IP协议的支持,使其具备了强大的网络功能,为互联网的普及提供了基础。 6. **开放源码**:Linux1.0遵循GNU通用公共许可协议(GPL),鼓励全球开发者参与其改进,这一开放的...

    两本最流行的Linux电子书(Linux内核游记)

    源代码分析是Linux内核学习的一大特色,因为Linux是开源的,这意味着任何人都可以查看、学习甚至修改其源代码。"Linux1.0核心游记"很可能会引导读者逐步剖析源码,通过实例解析关键函数和数据结构,帮助读者更好地...

    linux 1.0源代码+1.0核心游记+0.11内核完全注释

    本文将深入探讨Linux 1.0源代码、核心游记以及0.11内核的完全注释,揭示这个操作系统内核的发展历程和关键技术。 Linux 1.0源代码是Linux系统早期的重要里程碑。在1994年发布时,它包含了大约十万行C语言代码,展示...

    Linux1-1.0.zip

    5. **网络协议栈**:尽管Linux 1.0在网络支持上可能相对有限,但其基础的TCP/IP协议栈已经具备。了解这一部分源码有助于理解网络数据包的发送和接收,以及如何实现网络通信。 6. **用户空间和内核空间交互**:Linux...

    linux 1.0内核源代码

    1. **进程管理**:Linux 1.0 的内核实现了基本的进程调度机制,包括进程创建、上下文切换、信号处理等。通过阅读 `process.c` 文件,可以了解如何管理进程的生命周期和状态转换。 2. **内存管理**:在早期的 Linux ...

    【小学生作文】小学生游记作文范文5篇.doc

    【小学生游记作文范文5篇】是一份适合小学生学习写作的文档,包含了多个关于游记的实例,旨在帮助孩子们提高写作技巧和表达能力。游记作为一种文体,是对旅行经历的记录,可以涵盖对自然风光、人文历史、生活体验等...

    linux-0.11 源代码+内核代码完全注释

    Linux-0.11是Linux操作系统历史上的一个早期版本,对于理解Linux内核的工作原理具有重要的参考价值。这个源代码包包含的是Linux-0.11的原始代码,并且附带了详细的内核代码注释,是学习和研究Linux内核的宝贵资料。 ...

    猫游记辅助工具代码

    《猫游记辅助工具代码》是一款基于C#编程语言开发的辅助软件,主要针对网络游戏“猫游记”设计。此工具旨在提供自动化功能,包括自动打怪和自动行走,以及录制和播放移动脚本,以减轻玩家在游戏中重复劳动的负担。...

    暑假游记作文范例600字左右5篇.pdf

    暑假游记作文范例600字左右5篇.pdf

    马蜂窝游记爬虫代码案例.zip

    《马蜂窝游记爬虫代码案例》是一份关于使用爬虫技术抓取马蜂窝网站数据的代码案例。通过Python语言和相关库,如requests和Beautiful Soup,来抓取马蜂窝网站上的游记数据,包括游记的标题、作者、发布时间、内容等...

    游记散文阅读试题66篇.doc

    本文主要围绕“游记散文阅读试题66篇.doc”中的部分案例进行分析,旨在通过具体事例来探讨阅读理解能力的重要性及其提升方法。文中涉及的故事包括技术问题解决、个人挑战、以及自然景观的描绘,这些内容不仅能够激发...

Global site tag (gtag.js) - Google Analytics