C语言详解select函数的使用

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日

相关文章

  • 什么是服务器

    什么是服务器 服务器是指在网络中接受其他设备请求并提供服务的计算机。它可以是一台物理机器,也可以是一组虚拟机。在一个网络中,有许多不同种类的服务器,像是Web服务器、邮件服务器、数据库服务器等等。 服务器的作用 服务器的作用很多,其中最常见的应用包括: 存储和管理数据:服务器通常会有更大的储存空间,可以用来存储和管理数据。 提供服务:服务器主要被用来提供服务…

    其他 2023年4月16日
    00
  • thinkjs+swagger Editor

    使用ThinkJS和Swagger Editor构建API文档站点 随着现代web应用的快速发展,越来越多的开发人员需要访问和理解API文档。正确编写API文档是整个应用程序的关键组成部分,因此,在构建API时应该考虑提供易于阅读和理解的文档。在这篇文章中,我们将介绍如何使用ThinkJS和Swagger Editor构建易于理解和阅读的API文档站点。 什…

    其他 2023年3月28日
    00
  • docker-compose由命令行设置的env文件

    Docker Compose中使用命令行设置的env文件的完整攻略 Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。在Docker Compose中,可以使用命令行设置的env文件来容器的环境变量。以下是使用命令行设置的env文件的完整攻略。 步骤1:创建Docker Compose文件 首先,需要创建一个Docker Co…

    other 2023年5月8日
    00
  • 浅谈PHP封装CURL

    浅谈PHP封装CURL的完整攻略 介绍 CURL是一个常用的用于向各种网络服务发送请求并获取响应的库,它支持许多协议,包括HTTP、FTP、SMTP等。在进行Web开发中,使用CURL可以方便地执行HTTP请求和处理响应,并且支持多线程、Cookie等功能,十分强大和灵活。 PHP提供了CURL扩展,可以方便地使用CURL库进行网络请求。但是,使用CURL需…

    other 2023年6月25日
    00
  • Win10照片应用打不开怎么解决?Win10重置/重装照片应用程序的方法

    Win10照片应用打不开怎么解决? 如果你的Win10操作系统中的照片应用无法正常打开,或打开后闪退,那么可能是该应用出了些问题,此时你可以尝试以下的这些解决方法。 方法一:尝试重置照片应用 打开“设置”应用,点击“应用”并找到“照片”应用,点击它。 在新页面中,点击“高级选项”按钮。 在下拉菜单中选择“重置”。 等待一段时间,再尝试打开照片应用,看看是否问…

    other 2023年6月25日
    00
  • 值得收藏的27个Linux文档编辑命令

    “值得收藏的27个Linux文档编辑命令”是一篇介绍Linux命令行下文档编辑的文章,以下是一个详细的攻略: 介绍 首先,我们需要知道这篇文章的介绍,了解一下它讲解的内容是什么,以及接下来的攻略会做什么。 文档编辑 接下来,我们需要了解Linux命令行下常用的文档编辑工具,比如:vim,nano等。了解它们是如何工作的,如何打开、编辑和保存文档,以及一些常用…

    other 2023年6月26日
    00
  • Python+AutoIt实现界面工具开发

    Python+AutoIt实现界面工具开发的完整攻略 Python和AutoIt是两种常用的脚本语言,可以用于实现界面工具的开发。本文将为您详细讲解Python+AutoIt实现界面工具开发的完整攻略,包括介绍、方法和两个示例说明。 介绍 Python是一种高级编程语言,具有简单易学、功能强大、可扩展性好等特点。AutoIt是一种Windows平台下的自动化…

    other 2023年5月6日
    00
  • iOS实现消息推送及原理分析

    iOS实现消息推送及原理分析 什么是消息推送? 消息推送是指在无需打开应用程序的情况下,向手机用户发送通知消息。消息推送可以通过苹果官方提供的APNs(Apple Push Notification service,苹果推送服务)完成。 APNs的工作原理 APNs与苹果设备之间的通信是基于一种专门为该服务设计的二进制协议,这个协议被称为APNs协议。APN…

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