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\n");

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\n");

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\n");

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

你可能感兴趣的:(学习笔记)