C语言详解select函数的使用

yizhihongxing

C语言详解select函数的使用

什么是select函数?

select函数是Linux系统中的多路复用函数,它通过检查一组文件描述符(socket、文件、管道等)的状态来实现同时监视多个文件描述符的读写状态,并在其中的一个文件描述符可读写时进行相应的处理。可以说,select函数是实现I/O多路复用的重要工具之一。

select函数的语法

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
  • nfds:指最大的文件描述符加1,即文件描述符集合中所有文件描述符的范围,nfds参数常常被设置为需要检查的文件描述符集合中的最大文件描述符+1。
  • readfds:可读文件描述符集合;其中每个元素代表一个文件描述符。如果文件描述符可读了,则相应元素会被置为1,否则为0。
  • writefds:可写文件描述符集合;其中每个元素代表一个文件描述符。如果文件描述符可写了,则相应元素会被置为1,否则为0。
  • exceptfds:异常文件描述符集合;其中每个元素代表一个文件描述符。如果文件描述符出现异常情况了,例如收到外带数据,该元素会被置为1,否则为0。
  • timeout:select超时时间,分两种情况:当它的值为NULL时,表示无限等待;如果在超时时间内仍没有事件到达,则返回0。当它的值不为NULL时,最长等待时间为timeout。如果时间耗尽还没有I/O事件发生,则该函数返回0。

select函数使用示例

示例一:使用select函数实现简单的回显服务器

下面的示例演示如何使用select函数实现简单的回显服务器。该服务器首先监听来客户端的连接,当有客户端连接到服务器后,便会以子进程的方式处理客户端请求,并进行回显处理。具体实现过程如下所示:

#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/select.h>

#define MAXLINE 1024
#define SERV_PORT 7000

void str_echo(int sockfd)
{
    ssize_t n;
    char buf[MAXLINE];
    while (1)
    {
        memset(buf, 0, MAXLINE);
        if ((n = read(sockfd, buf, MAXLINE)) == 0)
            return;
        write(sockfd, buf, n);
    }
}

int main(int argc, char **argv)
{
    int listenfd, connfd;
    pid_t childpid;
    socklen_t clilen;
    struct sockaddr_in cliaddr, servaddr;

    if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket error");
        exit(1);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) == -1)
    {
        perror("bind error");
        exit(1);
    }

    if (listen(listenfd, SOMAXCONN) == -1)
    {
        perror("listen error");
        exit(1);
    }

    fd_set rset, allset;
    FD_ZERO(&allset);
    FD_SET(listenfd, &allset);

    int maxfd = listenfd;
    int nready, i, sockfd;

    while (1)
    {
        rset = allset;
        if ((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) == -1)
        {
            perror("select error");
            exit(1);
        }

        if (FD_ISSET(listenfd, &rset))
        {
            clilen = sizeof(cliaddr);
            if ((connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &clilen)) == -1)
            {
                perror("accept error");
                exit(1);
            }

            if ((childpid = fork()) == 0)
            {
                close(listenfd);
                str_echo(connfd);
                exit(0);
            }

            close(connfd);
        }
    }

    exit(0);
}

示例二:使用select函数实现简单的客户端

下面的示例演示如何使用select函数实现简单的客户端。该客户端首先创建一个socket套接字,然后向指定的服务器发起一次连接,最后可以向服务器发送数据,并等待来自服务器的回应。具体实现过程如下所示:

#include <string.h>
#include <sys/socket.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/select.h>

#define MAXLINE 1024
#define SERV_PORT 7000

int main(int argc, char **argv)
{
    int sockfd, n;
    char recvline[1024], sendline[1024];
    struct sockaddr_in servaddr;

    if (argc != 2)
    {
        printf("usage: ./client <IPaddress>\n");
        exit(1);
    }

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
    {
        perror("socket error");
        exit(1);
    }

    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(SERV_PORT);
    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) < 0)
    {
        printf("inet_pton error for %s\n", argv[1]);
        exit(1);
    }

    if (connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        perror("connect error");
        exit(1);
    }

    fd_set rset;
    FD_ZERO(&rset);

    while (1)
    {
        bzero(sendline, 1024);
        bzero(recvline, 1024);

        FD_SET(fileno(stdin), &rset);
        FD_SET(sockfd, &rset);

        int maxfd = (fileno(stdin) > sockfd ? fileno(stdin) : sockfd) + 1;
        if (select(maxfd, &rset, NULL, NULL, NULL) == -1)
        {
            perror("select");
            exit(1);
        }

        if (FD_ISSET(fileno(stdin), &rset))
        {
            fgets(sendline, 1024, stdin);
            if (send(sockfd, sendline, strlen(sendline), 0) < 0)
            {
                perror("send error");
                exit(1);
            }
        }

        if (FD_ISSET(sockfd, &rset))
        {
            if ((n = recv(sockfd, recvline, MAXLINE, 0)) == -1)
            {
                if (errno == ECONNRESET)
                {
                    printf("server close my socket\n");
                    close(sockfd);
                    exit(EXIT_FAILURE);
                }
                perror("recv error");
                exit(1);
            }
            if (n == 0)
            {
                printf("server close my socket\n");
                close(sockfd);
                exit(EXIT_FAILURE);
            }

            printf("recv the echoed msg:%s\n", recvline);
        }
    }

    exit(0);
}

总结

本文详细介绍了select函数的用法及其相关示例,包括使用select函数实现简单的回显服务器和客户端。使用select函数能够实现I/O多路复用,提高程序的性能和效率,是Linux网络编程中不可或缺的一部分。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:C语言详解select函数的使用 - Python技术站

(0)
上一篇 2023年6月27日
下一篇 2023年6月27日

相关文章

  • 教你使用PLSQLDeveloper14连接Oracle11g的详细过程

    下面我就来详细讲解“教你使用PLSQLDeveloper14连接Oracle11g的详细过程”。 步骤一:下载和安装PLSQLDeveloper14 首先,要使用PLSQLDeveloper14连接Oracle11g,您需要下载和安装PLSQLDeveloper14。您可以通过官方网站或第三方软件下载站下载PLSQLDeveloper14安装包。下载完安装包…

    other 2023年6月27日
    00
  • Spring aop失效的几种解决方案

    好的。下面给您详细讲解“Spring AOP失效的几种解决方案”的完整攻略。 1. Spring AOP 无效的原因分析 Spring AOP(面向切面编程)是一种常见的面向对象编程的技术,可以提高程序的可扩展性、可维护性和可重用性。但是,当我们在实际开发中使用Spring AOP的时候,可能会遇到一些问题。其中最常见的问题是AOP失效。这种情况下,我们可以…

    other 2023年6月26日
    00
  • 什么是Python变量作用域

    什么是Python变量作用域 在Python中,变量作用域指的是变量在程序中可访问的范围。Python中有四种不同的变量作用域,它们是:局部作用域、嵌套作用域、全局作用域和内置作用域。 局部作用域 局部作用域是指在函数内部定义的变量。这些变量只能在函数内部访问,函数外部无法访问到这些变量。当函数执行完毕后,局部作用域中的变量将被销毁。 下面是一个示例,演示了…

    other 2023年7月29日
    00
  • Access2010默认数字字段怎么设置成小数?

    想要将Access2010默认数字字段设置成小数,可以按照以下步骤进行操作: 打开Access 2010数据库,选择要设置小数的表格,进入表格设计模式。 在表格设计模式中,找到要设置为小数的数字字段,双击该字段,进入字段属性窗口。 在字段属性窗口中,找到“数据类型”项,选择“十进制数”或“货币”数据类型(这两种数据类型会自动设置小数位数),并根据需要设置小数…

    other 2023年6月25日
    00
  • 浅谈redis五大数据结构和使用场景

    浅谈Redis五大数据结构和使用场景 简介 Redis是一种开源的基于内存的数据结构存储系统,可以用作数据库、缓存和消息中间件。Redis支持多种数据结构,这些数据结构可在复杂数据处理中提供更灵活的功能。 Redis支持五种主要的数据结构: 字符串(String) 列表(List) 集合(Set) 哈希(Hash) 有序集合(Sorted Set) 本文将对…

    other 2023年6月27日
    00
  • openstack中的rpc远程调用的方法

    OpenStack中RPC远程调用的方法 RPC(Remote Procedure Call)是一种进程间通信机制,允许在不同的计算机上的进程之间进行调用。在OpenStack中,RPC用于在不同节点间的服务进程通讯,充当了OpenStack分发服务的核心。下面是RPC远程调用的方法。 1. RPC远程调用简介 RPC远程调用是通过消息传输的方式进行数据的交…

    other 2023年6月27日
    00
  • 决策树归纳算法之c4.5

    决策树归纳算法之c4.5 在机器学习领域,决策树是一种常用的分类和预测模型。而C4.5是一种流行的决策树归纳算法,由Ross Quinlan于1993年提出,是对ID3算法的改进。 C4.5算法基本原理 C4.5算法是一种基于信息增益的决策树归纳算法。 首先,C4.5算法通过计算某个特征对于目标变量的信息增益来确定最佳分类特征。信息增益表示特征划分后,目标变…

    其他 2023年3月28日
    00
  • sql server——分组查询(方法和思想)

    以下是“SQL Server——分组查询(方法和思想)”的完整攻略,包括分组查询的概念、方法和示例说明。 分组查询的概念 分组查询是一种SQL查询语句,它将数据按照指定的列进行分组,并对每个分组进行聚合计算。分组查询通常用于统计和汇总数据,例如计算每个部门的销售总额、平均工资等。 分组查询的方法 以下是分组查询的方法: 使用GROUP BY子句:GROUP …

    other 2023年5月6日
    00
合作推广
合作推广
分享本页
返回顶部