第6章 I/O复用:select和poll函数

I/O复用:一种预先告知内核的能力,使得内核一旦发现进程指定的一个或多个I/O条件就绪,它就通知进程。

同步I/O:导致请求的进程阻塞,直到I/O操作完成。

异步I/O:不导致请求进程阻塞。

I/O复用模型(select、poll):

5种I/O模型比较:

/* According to POSIX.1-2001, POSIX.1-2008 */
	#include <sys/select.h>

    /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

		int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

		void FD_CLR(int fd, fd_set *set);
		int  FD_ISSET(int fd, fd_set *set);
		void FD_SET(int fd, fd_set *set);
		void FD_ZERO(fd_set *set);

		#include <sys/select.h>

		int pselect(int nfds, fd_set *readfds, fd_set *writefds,
					fd_set *exceptfds, const struct timespec *timeout,
					const sigset_t *sigmask);
	/*  Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

			pselect(): _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
	*/

select客户端:

#include "../Gnet.h"

void do_client(int connfd)
{
    char buf[MAX_LINE];
    int maxfd;
    fd_set rset;
    int n;
    int closewr;

    closewr = 0;
    while(1)
    {
        FD_ZERO(&rset);
        if(closewr == 0)
            FD_SET(STDIN_FILENO, &rset);
        FD_SET(connfd, &rset);
        maxfd = STDIN_FILENO > connfd ? STDIN_FILENO : connfd;
        if(select(maxfd+1, &rset, NULL, NULL, NULL) < 0)
            perr_exit("select error.");
        if(FD_ISSET(connfd, &rset))
        {
            if((n = Read(connfd, buf, MAX_LINE)) == 0)
            {
                if(closewr == 1)
                    return;
                else
                    perr_exit("server terminated.");
            }
            Write(STDOUT_FILENO, buf, n);
        }
        if(FD_ISSET(STDIN_FILENO, &rset))
        {
            if((n = Read(STDIN_FILENO, buf, MAX_LINE)) == 0)
            {
                shutdown(connfd, SHUT_WR);
                FD_CLR(STDIN_FILENO, &rset);
                closewr = 1;
            }
            else
                Write(connfd, buf, n);
        }
    }
}

int main(int argc, const char* argv[])
{
    int connfd;
    struct sockaddr_in server_addr;

    if(argc < 2)
        perr_exit("usage : client <IPaddress>");

    connfd = Socket(AF_INET, SOCK_STREAM, 0);
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, argv[1], &server_addr.sin_addr);
    Connect(connfd, (struct sockaddr*)&server_addr, sizeof(server_addr));

    do_client(connfd);

    return 0;
}

select服务器:

#include "../Gnet.h"

int main(int argc, const char* argv[])
{
    int lfd, connfd;
    int maxfd;
    int client[FD_SETSIZE], maxi, findi;
    int nready;
    int nread;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;
    fd_set allset, rset;
    char buf[MAX_LINE];

    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(SERVER_PORT);

    lfd = Socket(AF_INET, SOCK_STREAM, 0);
    Bind(lfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
    Listen(lfd, LISTENQ);
    maxfd = lfd;
    maxi = -1;
    for(int i=0; i<FD_SETSIZE; ++i)
        client[i] = -1;
    FD_ZERO(&allset);
    FD_SET(lfd, &allset);
    printf("waiting for connecting.\n");

    while(1)
    {
        rset = allset;
        nready = select(maxfd+1, &rset, NULL, NULL, NULL);
        if(nready < 0)
            perr_exit("select error.");
        if(FD_ISSET(lfd, &rset))
        {
            client_addr_len = sizeof(client_addr);
            connfd = Accept(lfd, (struct sockaddr*)&client_addr, &client_addr_len);
            for(findi = 0; findi < FD_SETSIZE; ++findi)
            {
                if(client[findi] < 0)
                {
                    client[findi] = connfd;
                    break;
                }
            }
            if(findi == FD_SETSIZE)
                Close(connfd);
            else
            {
                FD_SET(connfd, &allset);
                if(connfd > maxfd)
                    maxfd = connfd;
                if(findi > maxi)
                    maxi = findi;
                if(--nready <= 0)
                    continue;
            }
        }
        for(int i=0; i <= maxi; ++i)
        {        
            if(client[i] < 0)
                continue;
            if(FD_ISSET(client[i], &rset))
            {
                if((nread = Read(client[i], buf, MAX_LINE)) == 0)
                {
                    Close(client[i]);
                    FD_CLR(client[i], &allset);
                    client[i] = -1;
                }
                else
                    Write(client[i], buf, nread);

                if(--nready <= 0)
                    break;
            }
        }
    }

    return 0;
}
    #include <poll.h>

    int poll(struct pollfd *fds, nfds_t nfds, int timeout);

    #define _GNU_SOURCE         /* See feature_test_macros(7) */
    #include <signal.h>
    #include <poll.h>

    int ppoll(struct pollfd *fds, nfds_t nfds,
            const struct timespec *tmo_p, const sigset_t *sigmask);

poll服务器:

#include "../Gnet.h"

int main(int argc, const char* argv[])
{
    int lfd, connfd;
    int maxi, findi;
    int nready;
    int nread;
    struct sockaddr_in server_addr, client_addr;
    socklen_t client_addr_len;    
    char buf[MAX_LINE];
    struct pollfd client[OPEN_MAX];

    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(SERVER_PORT);

    lfd = Socket(AF_INET, SOCK_STREAM, 0);
    Bind(lfd, (const struct sockaddr*)&server_addr, sizeof(server_addr));
    Listen(lfd, LISTENQ);

    client[0].fd = lfd;
    client[0].events = POLLRDNORM;
    for(int i=1; i<OPEN_MAX; ++i)
        client[i].fd = -1;
    maxi = 0;

    printf("waiting for connecting.\n");

    while(1)
    {
        nready = poll(client, maxi+1, -1);
        if(nready < 0)
            perr_exit("poll error.");

        if(client[0].revents & POLLRDNORM)
        {
            client_addr_len = sizeof(client_addr);
            connfd = Accept(lfd, (struct sockaddr*)&client_addr, &client_addr_len);
            for(findi=1; findi<OPEN_MAX; ++findi)
            {
                if(client[findi].fd < 0)
                {
                    client[findi].fd = connfd;
                    break;
                }
            }
            if(findi == OPEN_MAX)
                Close(connfd);
            else
            {
                if(findi > maxi)
                    maxi = findi;
                client[findi].events = POLLRDNORM;

                if(--nready <= 0)
                    continue;
            }
        }

        for(int i =1; i <= maxi; ++i)
        {
            if(client[i].fd < 0)
                continue;
            if(client[i].revents & (POLLRDNORM | POLLERR))
            {
                if((nread = Read(client[i].fd, buf, MAX_LINE)) < 0)
                {
                    client[i].fd = -1;
                    printf("read error.");
                }
                else if(nread == 0)
                {
                    Close(client[i].fd);
                    client[i].fd = -1;
                }
                else
                    Write(client[i].fd, buf, nread);

                if(--nready <= 0)
                    break;
            }
        }
    }

    return 0;
}

github:https://github.com/gongluck/CodeBase/tree/master/notes/unpv13-notes

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据库新发现

使用Oracle Wrap工具加密你的代码

Last Updated: Monday, 2004-11-15 22:31 Eygle

742
来自专栏数据和云

偷梁换柱 | 无备份情况下的数据恢复实践

在实际环境中,许多数据库环境并没有做好完整的数据备份恢复计划及容灾方案,无法保证数据安全,并且出现一些灾难性的错误。那么我们就面临这样的问题:在什么样的最极端情...

3145
来自专栏杨建荣的学习笔记

redo日志文件学习(22天)

关于redo日志文件,今天因为要做redo日志的在线迁移,所以特意做了个简单的总结。 1. 如果要把redo的切换信息显示到alert日志中,需要设置个参数。...

3415
来自专栏技术博文

php QR Code二维码生成类

<?php /* * PHP QR Code encoder * * This file contains MERGED version of PHP ...

3565
来自专栏杨建荣的学习笔记

关于exp statistics的问题和简单测试(82天)

在数据导出的时候,可能会碰到EXP-00091: Exporting questionable statistics.的问题,有时候会让人有点摸不到头脑,不知道...

2138
来自专栏乐沙弥的世界

Failed to upgrade Oracle Cluster Registry configuration(root.sh)

    最近在给客户基于Suse 11 sp3安装Oracle 10g RAC,在安装完clusterware执行/u01/app/crs/root.sh时收...

661
来自专栏后端之路

SpringBoot实现Mybatis多数据源方案

背景 目前报表导出需要多数据库的数据,因此我们需要做Mybatis多数据源的配置 我们之前使用Spring的AbstractRoutingDataSource ...

7417
来自专栏xingoo, 一个梦想做发明家的程序员

IP多播

                      1   多播地址                     IP多播地址采用D类IP地址确定多播的组,地址范围是224...

1945
来自专栏杨建荣的学习笔记

外部表的导入导出问题 (41天)

今天尝试了一下用exp导出外部表,碰到了一些问题。 ----导出 今天导出的时候发现一个严重的问题,导出一个很小的外部表花了很长时间,最后还是报错,而且生成的d...

3495
来自专栏蓝天

零停重启程序工具Huptime研究

零停重启目标程序,比如一个网络服务程序,不用丢失和中断任何消息实现重新启动,正在处理的消息也不会中断和丢失,重启的方法是给目标程序的进程发SIGHUP信号。...

651

扫码关注云+社区