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

相关文章

来自专栏游戏开发那些事

【LINUX/UNIX网络编程】之使用SOCKET进行UDP编程

(2)客户可向服务器发送多种指令:DOWNLOAD、UPLOAD、YES、NO、START、END、SHUTDOWN、CONTENT、OKDOWLOAD格式:D...

942
来自专栏运维

DNS主从服务器搭建

http://blog.51cto.com/yichenyang/1911098 http://blog.51cto.com/wubinary/1379595

771
来自专栏北京马哥教育

tcp_tw_reuse、tcp_tw_recycle 使用场景及注意事项

linux TIME_WAIT 相关参数: net.ipv4.tcp_tw_reuse = 0 表示开启重用。允许将TIME-WAIT sockets重...

42411
来自专栏三杯水

DNS主从服务器搭建

http://blog.51cto.com/yichenyang/1911098 http://blog.51cto.com/wubinary/1379595

632
来自专栏散尽浮华

Centos下内网NDS主从环境部署记录

一、DNS是什么? DNS(Domain Name System),即域名系统。它使用层次结构的命名系统,将域名和IP地址相互映射,形成一个分布式数据库系统。 ...

2365
来自专栏算法修养

Nginx 日志 worker_connections are not enough while connecting to upstream

记一次,排查错误所遇到的问题,和学习到的内容。 上周五,刚上线的项目出现了503 ,查看日志发现如下内容: System.Exception: Request ...

41910
来自专栏北京马哥教育

超详细!使用 LVS 实现负载均衡原理及安装配置详解

负载均衡集群是 load balance 集群的简写,翻译成中文就是负载均衡集群。常用的负载均衡开源软件有nginx、lvs、haproxy,商业的硬件负载均衡...

30210
来自专栏武军超python专栏

2018年8月14日TCP网络编程及具体代码

静态文件?顾名思义,静态文件就是那些不会改变的文件,例如视频音乐,图片等类似的文件 NT平台:指的是windows NT操作系统,是windows在199...

774
来自专栏Petrichor的专栏

Tmux 使用

[1] 十分钟学会 tmux [2] Tmux使用手册 [3] 第 2 章 配置 tmux

1012
来自专栏前端儿

【Chat】实验 -- 实现 C/C++下TCP, 服务器/客户端 "多人聊天室"

782

扫码关注云+社区