• UNP学习笔记(第十四章 高级I/O函数)


    本章讨论我们笼统地归为“高级I/O”的各个函数和技术

    套接字超时

    有3种方法在涉及套接字的I/O操作上设置超时

    1.调用alarm,它在指定超时时期满时产生SIGALRM信号

    2.在select中阻塞等待I/O(select有内置的时间限制),以此代替直接阻塞在read或write调用上

    3.使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。

    使用SIGALRM为connect设置超时

    下面给出我们的connect_timeo函数,它以调用者指定的超时上限调用connect

     1 /* include connect_timeo */
     2 #include    "unp.h"
     3 
     4 static void    connect_alarm(int);
     5 
     6 int
     7 connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec)
     8 {
     9     Sigfunc    *sigfunc;
    10     int        n;
    11 
    12     sigfunc = Signal(SIGALRM, connect_alarm);
    13     if (alarm(nsec) != 0)
    14         err_msg("connect_timeo: alarm was already set");
    15 
    16     if ( (n = connect(sockfd, saptr, salen)) < 0) {
    17         close(sockfd);
    18         if (errno == EINTR)
    19             errno = ETIMEDOUT;
    20     }
    21     alarm(0);                    /* turn off the alarm */
    22     Signal(SIGALRM, sigfunc);    /* restore previous signal handler */
    23 
    24     return(n);
    25 }
    26 
    27 static void
    28 connect_alarm(int signo)
    29 {
    30     return;        /* just interrupt the connect() */
    31 }
    32 /* end connect_timeo */
    View Code

    如果connect被中断就会返回EINTR错误。

    使用SIGALRM为recvfrom设置超时

     1 #include    "unp.h"
     2 
     3 static void    sig_alrm(int);
     4 
     5 void
     6 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
     7 {
     8     int    n;
     9     char    sendline[MAXLINE], recvline[MAXLINE + 1];
    10 
    11     Signal(SIGALRM, sig_alrm);
    12 
    13     while (Fgets(sendline, MAXLINE, fp) != NULL) {
    14 
    15         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
    16 
    17         alarm(5);
    18         if ( (n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0) {
    19             if (errno == EINTR)
    20                 fprintf(stderr, "socket timeout
    ");
    21             else
    22                 err_sys("recvfrom error");
    23         } else {
    24             alarm(0);
    25             recvline[n] = 0;    /* null terminate */
    26             Fputs(recvline, stdout);
    27         }
    28     }
    29 }
    30 
    31 static void
    32 sig_alrm(int signo)
    33 {
    34     return;            /* just interrupt the recvfrom() */
    35 }
    View Code

    使用select为recvfrom设置超时

     1 /* include readable_timeo */
     2 #include    "unp.h"
     3 
     4 int
     5 readable_timeo(int fd, int sec)
     6 {
     7     fd_set            rset;
     8     struct timeval    tv;
     9 
    10     FD_ZERO(&rset);
    11     FD_SET(fd, &rset);
    12 
    13     tv.tv_sec = sec;
    14     tv.tv_usec = 0;
    15 
    16     return(select(fd+1, &rset, NULL, NULL, &tv));
    17         /* 4> 0 if descriptor is readable */
    18 }
    19 /* end readable_timeo */
    View Code

    该函数等待一个描述符变为可读或者发生超时(超时返回0)

    我们可以使用该函数来改写上面的dg_cli函数

     1 #include    "unp.h"
     2 
     3 void
     4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
     5 {
     6     int    n;
     7     char    sendline[MAXLINE], recvline[MAXLINE + 1];
     8 
     9     while (Fgets(sendline, MAXLINE, fp) != NULL) {
    10 
    11         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
    12 
    13         if (Readable_timeo(sockfd, 5) == 0) {
    14             fprintf(stderr, "socket timeout
    ");
    15         } else {
    16             n = Recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
    17             recvline[n] = 0;    /* null terminate */
    18             Fputs(recvline, stdout);
    19         }
    20     }
    21 }
    View Code

    直到readable_timeo告知所关注的描述符已变为可读后我们才调用recvfrom(超时就打印错误)

    使用SO_RCVTIMEO套接字选项为recvfrom设置超时

    下面是使用SO_RCVTIMEO套接字选项的另一个版本的dg_cli函数

     1 #include    "unp.h"
     2 
     3 void
     4 dg_cli(FILE *fp, int sockfd, const SA *pservaddr, socklen_t servlen)
     5 {
     6     int                n;
     7     char            sendline[MAXLINE], recvline[MAXLINE + 1];
     8     struct timeval    tv;
     9 
    10     tv.tv_sec = 5;
    11     tv.tv_usec = 0;
    12     Setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
    13 
    14     while (Fgets(sendline, MAXLINE, fp) != NULL) {
    15 
    16         Sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen);
    17 
    18         n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL);
    19         if (n < 0) {
    20             if (errno == EWOULDBLOCK) {
    21                 fprintf(stderr, "socket timeout
    ");
    22                 continue;
    23             } else
    24                 err_sys("recvfrom error");
    25         }
    26 
    27         recvline[n] = 0;    /* null terminate */
    28         Fputs(recvline, stdout);
    29     }
    30 }
    View Code

    如果I/O操作超时,recvfrom将返回一个EWOULDBLOCK错误

    read和write函数的变体

    recv和send允许通过第四个参数从进程到内核传递标志;

    readv和writev允许指定往其中输入数据或从其中输出数据的缓冲区向量;

    recvmsg和sendmsg结合了其他I/O的所有特性,并具有接收和发送辅助数据的新能力

    可以查看之前apue的学习笔记  http://www.cnblogs.com/runnyu/p/4648678.html

  • 相关阅读:
    mpstat 查看多核CPU负载状态
    redis pipeset发布订阅
    sqlalchemyorm学生签到 成绩记录查询系统
    ORM数据库命令操作包装实例对象学习
    Python Mysql数据库操作
    redis hash操作 list列表操作
    pymysqlsqlalchemyorm
    ss命令用来显示处于活动状态的套接字信息。
    8月20日学习日志
    8月22日学习日志
  • 原文地址:https://www.cnblogs.com/runnyu/p/4666340.html
Copyright © 2020-2023  润新知