(3)使用较新的SO_RCVTIMEO和SO_SNDTIMEO套接字选项。
static void connect_alarm(int); int connect_timeo(int sockfd, const struct sockaddr *saptr, socklen_t salen, int nsec) { Sigfunc *sigfunc; int n; sigfunc = signal(SIGALRM, connect_alarm); //说明已经超时了,则直接调用connect_alarm函数,返回 if (alarm(nsec) != 0){ printf("connect_time:alarm was already set"); exit(-1); } if ((n = connect(sockfd, saptr, salen)) < 0){ close(sockfd); //如果是中断,则将错误设置为超时 if (errno == EINTR) errno = ETIMEDOUT; } alarm(0);//关闭超时 signal(SIGALRM,sigfunc);//原始的超时函数(即当nsec的超时时间设置大于connect本身的超时时间,而connect本身产生超时的时候,调用此函数) return (n); } static void connect_alarm(int signo) { return; }
static void sig_alrm(int); void dg_cli(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE + 1]; signal(SIGALRM, sig_alrm); while (fgets(sendline,MAXLINE,fp) != NULL){ sendto(sockfd, sendline, strlen(sendline),0, pservaddr, servlen); alarm(5); if ((n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL)) < 0){ if (errno == EINTR) fprintf(stderr, "socket timeout\n"); else{ printf("recvfrom error\n"); } } else { alarm(0); recvline[n] = 0; fputs(recvline, stdout); } } } static void sig_alrm(int) { return; }
int readable_timeo(int fd, int sec) { fd_set rset; struct timeval tv; FD_ZERO(&rset); FD_SET(fd, &rset); tv.tv_sec = sec; tv.tv_usec = 0; return (select(fd + 1, &rset, NULL, NULL, &tv)); //select函数也可简单的用作定时器函数 } void dg_cli(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE + 1]; while (fgets(sendline, MAXLINE, fp) != NULL){ sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); if (readable_timeo(sockfd, 5) != 0){ fprintf(stderr, "socket timeout\n"); } else{ n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); recvline[n] = 0; fputs(recvline, stdout); } } }
void dg_cli(FILE *fp, int sockfd, const struct sockaddr *pservaddr, socklen_t servlen) { int n; char sendline[MAXLINE], recvline[MAXLINE + 1]; struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); while (fgets(sendline, MAXLINE, fp) != NULL){ sendto(sockfd, sendline, strlen(sendline), 0, pservaddr, servlen); n = recvfrom(sockfd, recvline, MAXLINE, 0, NULL, NULL); if (n < 0){ if (errno == EWOULDBLOCK){ fprintf(stderr, "socket timeout\n")l continue; } else{ printf("recvfrom error\n"); exit(-1); } } } recvline[n] = 0; fputs(recvline, stdout); }
recv和send允许通过第四个参数从进程到内核传递标志;readv和writev允许指定往其中输入数据或从其中输出数据的缓冲区向量;recvmsg和sendmsg结合了其他I/O函数的所有特性。(函数具体说明请参考APUE和UNP卷1)