第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 条评论
登录 后参与评论

相关文章

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

关于CPU使用率高的awr分析(r8笔记第46天)

今天看到一个报警信息,大体是CPU使用异常。 ZABBIX-监控系统: ------------------------------------ 报警内容: ...

3225
来自专栏佳爷的后花媛

gitbookGitBook Toolchain Documentation

This document aims to be a comprehensive guide to GitBook. It contains the full ...

932
来自专栏数据库新发现

Oracle诊断案例-Spfile案例一则

情况说明: 系统:SUN Solaris8 数据库版本:9203 问题描述:工程人员报告,数据库在重新启动时无法正常启动.检查发现UNDO表空间丢失. 问题诊断...

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

IP多播

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

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

redo日志文件学习(22天)

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

3465
来自专栏后端之路

SpringBoot实现Mybatis多数据源方案

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

7927
来自专栏数据库新发现

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

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

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

10g升级至11g后数据库无法启动的问题解决

今天升级数据库碰到一个很郁闷的问题,把10g的数据库升级到11g以后,结果有一个改动,需要重启数据库,就敲了shutdown immediate,结果再star...

3726
来自专栏逍遥剑客的游戏开发

做了Nebula3的应用程序向导

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

11g Dataguard中的snapshot standby特性(r8笔记第49天)

11g中的ADG特性本身已经非常有特色,促使很多对于10g中不太灵便的备库升级到11g,对于DBA是一大福利,那么还有一个福利就是snapshot standb...

2805

扫码关注云+社区