UNP函数笔记十三: 非阻塞式I/O

58 篇文章 0 订阅
49 篇文章 0 订阅

第十六章  非阻塞式I/O:


示例:

#include <time.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/socket.h>

#include "err_exit.h"

#define MAXLINE 4096

char * 
gf_time(void)
{
    struct timeval   tv;
    time_t           t;
    static char      str[30];
    char           * ptr;

    if (gettimeofday(&tv, NULL) != 0) {  /* should always return 0 */
        err_exit("gettimeofday error");
    }

    t = tv.tv_sec; /* POSIX says tv.tv_sec is time_t; some BSDs don't agree */
    ptr = ctime(&t);
    strcpy(str, &ptr[11]);
    /* Fri Sep 13 00:00:00 1986\n\0 */
    /* 0123456789012345678901234 5  */
    snprintf(str+8, sizeof(str)-8, ".%06ld", tv.tv_usec);

    return(str);
}

void 
str_cli(FILE * fp, int sockfd)
{
    int       maxfdp1; 
    int       val;
    int       stdineof;
    ssize_t   n;
    ssize_t   nwritten;
    fd_set    rset;
    fd_set    wset;
    char      to[MAXLINE];
    char      fr[MAXLINE];
    char    * toiptr;
    char    * tooptr;
    char    * friptr;
    char    * froptr;

    if ((val = fcntl(sockfd, F_GETFL, 0)) == -1) {
        err_exit("fcntl error");
    }
    if (fcntl(sockfd, F_SETFL, val | O_NONBLOCK) == -1) {
        err_exit("fcntl error");
    }

    if ((val = fcntl(STDIN_FILENO, F_GETFL, 0)) == -1) {
        err_exit("fcntl error");
    }
    if (fcntl(STDIN_FILENO, F_SETFL, val | O_NONBLOCK) == -1) {
        err_exit("fcntl error");
    }

    if ((val = fcntl(STDOUT_FILENO, F_GETFL, 0)) == -1) {
        err_exit("fcntl error");
    }
    if (fcntl(STDOUT_FILENO, F_SETFL, val | O_NONBLOCK) == -1) {
        err_exit("fcntl error");
    }

    /* initialize buffer pointers */
    toiptr = tooptr = to;  
    friptr = froptr = fr;
    stdineof = 0;

    maxfdp1 = max(STDIN_FILENO, STDOUT_FILENO);
    maxfdp1 = max(maxfdp1, sockfd) + 1;
    for ( ; ; ) {
        FD_ZERO(&rset);
        FD_ZERO(&wset);
        if (stdineof == 0 && toiptr < &to[MAXLINE]) {
            FD_SET(STDIN_FILENO, &rset);  /* read from stdin */
        }
        if (friptr < &fr[MAXLINE]) {
            FD_SET(sockfd, &rset);  /* read from socket */
        }
        if (tooptr != toiptr) {
            FD_SET(sockfd, &wset);  /* data to write to socket */
        }
        if (froptr != friptr) {
            FD_SET(STDOUT_FILENO, &wset);  /* data to write to stdout */
        }

        if (select(maxfdp1, &rset, &wset, NULL, NULL) == -1) {
            err_exit("select error");
        }

        if (FD_ISSET(STDIN_FILENO, &rset)) {
            if ((n = read(STDIN_FILENO, toiptr, &to[MAXLINE] - toiptr)) < 0) {
                if (errno != EWOULDBLOCK) {
                    printf("read error on stdin\n");
                    exit(1);
                }
            } 
            else if (n == 0) {
                fprintf(stderr, "%s: EOF on stdin\n", gf_time());
                stdineof = 1;  /* all done with stdin */
                if (tooptr == toiptr) {
                    if (shutdown(sockfd, SHUT_WR) == -1) {  /* send FIN */
                        err_exit("shutdown error");
                    }
                }
            }
            else {
                fprintf(stderr, "%s: read %d bytes from stdin\n", gf_time(), n);
                toiptr += n;  /* just read */
                FD_SET(sockfd, &wset);  /* try and write to socket below */
            }
        }

        if (FD_ISSET(sockfd, &rset)) {
            if ((n = read(sockfd, friptr, &fr[MAXLINE] - friptr)) < 0) {
                if (errno != EWOULDBLOCK) {
                    printf("read error on socket\n");
                    exit(1);
                }
            }
            else if (n == 0) {
                fprintf(stderr, "%s: EOF on socket\n", gf_time());
                if (stdineof) {
                    return;  /* normal termination */
                }
                else {
                    printf("str_cli: server terminated prematurely");
                    exit(1);
                }
            }
            else {
                fprintf(stderr, "%s: read %d bytes from socket\n", 
                        gf_time(), n);
                friptr += n;  /* just read */
                FD_SET(STDOUT_FILENO, &wset);  /* try and write below */
            }
        }

        if (FD_ISSET(STDOUT_FILENO, &wset) && ((n = friptr - froptr) > 0)) {
            if ((nwritten = write(STDOUT_FILENO, froptr, n)) < 0) {
                if (errno != EWOULDBLOCK) {
                    printf("write error to stdout\n");
                    exit(1);
                }
            }
            else {
                fprintf(stderr, "%s: wrote %d bytes to stdout\n",
                        gf_time(), nwritten);
                froptr += nwritten;  /* just written */
                if (froptr == friptr) {
                    froptr = friptr = fr;  /* back to beginning of buffer */
                }
            }
        }

        if (FD_ISSET(sockfd, &wset) && ((n = toiptr - tooptr) > 0)) {
            if ((nwritten = write(sockfd, tooptr, n)) < 0) {
                if (errno != EWOULDBLOCK) {
                    printf("write error to socket\n");
                    exit(1);
                }
            }
            else {
                fprintf(stderr, "%s: wrote %d bytes to socket\n",
                        gf_time(), nwritten);
                tooptr += nwritten;  /* just written */
                if (tooptr == toiptr) {
                    toiptr = tooptr = to;  /* back to beginning of buffer */
                    if (stdineof) {
                        if (shutdown(sockfd, SHUT_WR) == -1) {  /* send FIN */
                            err_exit("shutdown error");
                        }
                    }
                }
            }
        }
    }
}
#include <signal.h>
#include <sys/socket.h>

#include "err_exit.h"

#define MAXLINE 4096

void 
str_cli(FILE * fp, int sockfd)
{
    int    n;
    pid_t  pid;
    char   sendline[MAXLINE];
    char   recvline[MAXLINE];

    if ((pid = fork()) < 0) {
        err_exit("fork error");
    }
    else if (pid == 0) {  /* child: server -> stdout */
        while ((n = readline(sockfd, recvline, MAXLINE)) > 0) {
            if (fputs(recvline, stdout) == EOF) {
                err_exit("fputs error");
            }
        }
        if (n < 0) {
            err_exit("readline error");
        }
        
        if (getppid() != 1) {
            kill(getppid(), SIGTERM);  /* in case parent still running */
        }
        exit(0);
    }
    else {  /* parent: stdin -> server */
        while (fgets(sendline, MAXLINE, fp) != NULL) {
            n = strlen(sendline);
            if (writen(sockfd, sendline, n) != n) {
                err_exit("writen error");
            }
        }
        if (ferror(fp)) {
            err_exit("fgets error");
        }

        if (shutdown(sockfd, SHUT_WR) == -1) {  /* EOF on stdin, send FIN */
            err_exit("shutdown error");
        }

        pause();
        return;
    }
}
#include <fcntl.h>
#include <sys/socket.h>

#include "err_exit.h"

int 
connect_nonb(int sockfd, const struct sockaddr * saptr, 
             socklen_t salen, int nsec)
{
    int             flags;
    int             n;
    int             error;
    socklen_t       len;
    fd_set          rset;
    fd_set          wset;
    struct timeval  tval;

    if ((flags = fcntl(sockfd, F_GETFL, 0)) == -1) {
        err_exit("fcntl error");
    }
    if (fcntl(sockfd, F_SETFL, flags | O_NONBLOCK) == -1) {
        err_exit("fcntl error");
    }

    error = 0;
    if ((n = connect(sockfd, saptr, salen)) == -1) {
        if (errno != EINPROGRESS) {
            return(-1);
        }
    }

    /* Do whatever we want while the connect is taking place. */

    if (n == 0) {
        goto done;  /* connect completed immediately */
    }

    FD_ZERO(&rset);
    FD_SET(sockfd, &rset);
    wset = rset;
    tval.tv_sec = nsec;
    tval.tv_usec = 0;

    if ((n = select(sockfd+1, &rset, &wset, NULL,
                    nsec ? &tval : NULL)) == -1) {
        err_exit("select error");
    }
    else if (n == 0) {
        close(sockfd);  /* timeout */
        errno = ETIMEDOUT;
        return(-1);
    }

    if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) {
        len = sizeof(error);
        if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == -1) {
            return(-1);  /* Solaris pending error */
        }
    }
    else {
        printf("select error: sockfd not set\n");
        exit(1);
    }

done:
    /* restore file status flags */
    if (fcntl(sockfd, F_SETFL, flags) == -1) {
        err_exit("fcntl error");
    }

    if (error) {
        close(sockfd);        /* just in case */
        errno = error;
        return(-1);
    }
    return(0);
}
#include <fcntl.h>
#include <sys/select.h>

#include "myio.h"
#include "err_exit.h"
#include "host_serv.h"
#include "tcp_connect.h"

#define MAXFILES  20
#define SERV      "80"  /* port number or service name */

struct file {
    char * f_name;   /* filename */
    char * f_host;   /* hostname or IPv4/IPv6 address */
    int    f_fd;     /* descriptor */
    int    f_flags;  /* F_xxx below */
} file[MAXFILES];

#define F_CONNECTING   1  /* connect() in progress */
#define F_READING      2  /* connect() complete; now reading */
#define F_DONE         4  /* all done */

#define GET_CMD        "GET %s HTTP/1.0\r\n\r\n"

#define min(a, b) ((a) < (b) ? (a) : (b))

int     nconn;
int     nfiles;
int     nlefttoconn;
int     nlefttoread;
int     maxfd;
fd_set  rset;
fd_set  wset;

void 
home_page(const char * host, const char * fname)
{
    int   fd;
    int   n;
    char  line[MAXLINE];

    fd = tcp_connect(host, SERV);  /* blocking connect() */

    n = snprintf(line, sizeof(line), GET_CMD, fname);
    if (writen(fd, line, n) != n) {
        err_exit("writen error");
    }

    for ( ; ; ) {
        if ((n = read(fd, line, MAXLINE)) < 0) {
            err_exit("read error");
        }
        else if (n == 0) {
            break;  /* server closed connection */
        }

        printf("read %d bytes of home page\n", n);
        /* do whatever with data */
    }
    printf("end-of-file on home page\n");
    if (close(fd) == -1) {
        err_exit("close error");
    }
}

void 
write_get_cmd(struct file * fptr)
{
    int   n;
    char  line[MAXLINE];

    n = snprintf(line, sizeof(line), GET_CMD, fptr->f_name);
    if (writen(fptr->f_fd, line, n) != n) {
        err_exit("writen error");
    }
    printf("wrote %d bytes for %s\n", n, fptr->f_name);

    fptr->f_flags = F_READING;  /* clears F_CONNECTING */

    FD_SET(fptr->f_fd, &rset);  /* will read server's reply */
    if (fptr->f_fd > maxfd) {
        maxfd = fptr->f_fd;
    }
}

void 
start_connect(struct file * fptr)
{
    int               fd;
    int               flags;
    int               n;
    struct addrinfo * ai;

    ai = host_serv(fptr->f_host, SERV, 0, SOCK_STREAM);

    if ((fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol)) == -1) {
        err_exit("socket error");
    }
    fptr->f_fd = fd;
    printf("start_connect for %s, fd %d\n", fptr->f_name, fd);

    /* Set socket nonblocking */
    if ((flags = fcntl(fd, F_GETFL, 0)) == -1) {
        err_exit("fcntl error");
    }
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
        err_exit("fcntl error");
    }

    /* Initiate nonblocking connect to the server. */
    if ((n = connect(fd, ai->ai_addr, ai->ai_addrlen)) == -1) {
        if (errno != EINPROGRESS) {
            printf("nonblocking connect error\n");
            exit(1);
        }
        fptr->f_flags = F_CONNECTING;
        FD_SET(fd, &rset);  /* select for reading and writing */
        FD_SET(fd, &wset);
        if (fd > maxfd) {
            maxfd = fd;
        }
    }
    else {  /* connect is already done */ 
        write_get_cmd(fptr);  /* write() the GET command */
    }
}

int 
main(int argc, char ** argv)
{
    int     i;
    int     fd;
    int     n;
    int     maxnconn;
    int     flags;
    int     error;
    char    buf[MAXLINE];
    fd_set  rs;
    fd_set  ws;

    if (argc < 5) {
        printf("usage: web <#conns> <hostname> <homepage> <file1> ...\n");
        exit(1);
    }
    maxnconn = atoi(argv[1]);

    nfiles = min(argc - 4, MAXFILES);
    for (i = 0; i < nfiles; i++) {
        file[i].f_name = argv[i + 4];
        file[i].f_host = argv[2];
        file[i].f_flags = 0;
    }
    printf("nfiles = %d\n", nfiles);

    home_page(argv[2], argv[3]);

    FD_ZERO(&rset);
    FD_ZERO(&wset);
    maxfd = -1;
    nlefttoread = nlefttoconn = nfiles;
    nconn = 0;

    while (nlefttoread > 0) {
        while (nconn < maxnconn && nlefttoconn > 0) {
            /* find a file to read */
            for (i = 0 ; i < nfiles; i++) {
                if (file[i].f_flags == 0) {
                    break;
                }
            }
            if (i == nfiles) {
                printf("nlefttoconn = %d but nothing found\n", nlefttoconn);
            }
            start_connect(&file[i]);
            nconn++;
            nlefttoconn--;
        }

        rs = rset;
        ws = wset;
        if ((n = select(maxfd+1, &rs, &ws, NULL, NULL)) == -1) {
            err_exit("select error");
        }

        for (i = 0; i < nfiles; i++) {
            flags = file[i].f_flags;
            if (flags == 0 || flags & F_DONE) {
                continue;
            }
            fd = file[i].f_fd;
            if (flags & F_CONNECTING &&
                (FD_ISSET(fd, &rs) || FD_ISSET(fd, &ws))) {
                n = sizeof(error);
                if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n) < 0 ||
                    error != 0) {
                    printf("nonblocking connect failed for %s\n", 
                            file[i].f_name);
                }
                else {  /* connection established */
                    printf("connection established for %s\n", file[i].f_name);
                    FD_CLR(fd, &wset);  /* no more writeability test */
                    write_get_cmd(&file[i]);  /* write() the GET command */
                }
            }
            else if (flags & F_READING && FD_ISSET(fd, &rs)) {
                if ((n = read(fd, buf, sizeof(buf))) < 0) {
                    err_exit("read error");
                }
                else if (n == 0) {
                    printf("end-of-file on %s\n", file[i].f_name);
                    if (close(fd) == -1) {
                        err_exit("close error");
                    }
                    file[i].f_flags = F_DONE;  /* clears F_READING */
                    FD_CLR(fd, &rset);
                    nconn--;
                    nlefttoread--;
                }
                else {
                    printf("read %d bytes from %s\n", n, file[i].f_name);
                }
            }
        }
    }
    exit(0);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值