第14章——高级IO函数

Posted yangxinrui

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第14章——高级IO函数相关的知识,希望对你有一定的参考价值。

1.套接字超时

  套接字IO函数设置超时的方法有三种:

(1)调用alarm。

(2)select

(3)使用SO_RECTIMEO和 SO_SNDTIMEO 选项

  上面三种方法适用于输入输出操作(read , write, sendto , recvfrom ..)

  (1)(2)适用于所有文件描述符,(3)适用于套接字

  connect 内置超时时间很长(典型为75秒),select可用于connect的前提是对应套接字处于 非阻塞模式。(3)不适于connect。

2.一些设置超时示例

2.1 使用alarm

  涉及信号就避免多线程,信号本身适中全局资源,所以要注意对其他信号使用的影响

int
connect_timeo(int sockfd, const SA *saptr, socklen_t salen, int nsec)
{
        Sigfunc *sigfunc;
        int             n;

        sigfunc = Signal(SIGALRM, connect_alarm);
        if (alarm(nsec) != 0)
                err_msg("connect_timeo: alarm was already set");

        if ( (n = connect(sockfd, saptr, salen)) < 0) {
                close(sockfd);
                if (errno == EINTR)
                        errno = ETIMEDOUT;
        }
        alarm(0);                                       /* turn off the alarm */
        Signal(SIGALRM, sigfunc);       /* restore previous signal handler */

        return(n);
}

static void
connect_alarm(int signo)
{
        return;         /* just interrupt the connect() */
}
void
dg_cli(FILE *fp, int sockfd, const SA *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
");
                        else
                                err_sys("recvfrom error");
                } else {
                        alarm(0);
                        recvline[n] = 0;        /* null terminate */
                        Fputs(recvline, stdout);
                }
        }
}

static void
sig_alrm(int signo)
{
        return;                 /* just interrupt the recvfrom() */
}

2.2 使用select

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));
                /* 4> 0 if descriptor is readable */
}
/* end readable_timeo */

2.3 使用SO_RCVTIMEO 

void
dg_cli(FILE *fp, int sockfd, const SA *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
");
                                continue;
                        } else
                                err_sys("recvfrom error");
                }

                recvline[n] = 0;        /* null terminate */
                Fputs(recvline, stdout);
        }
}

 

3. recv 和 send 函数

       ssize_t recv(int sockfd, void *buf, size_t len, int flags);
       ssize_t send(int sockfd, const void *buf, size_t len, int flags);

  特别的是flags参数:

技术图片

  MSG_DONTROUTE:

    和SO_DONTROUTE的套接字选项类似,SO_DONTROUTE是修改套接字到达长期有效,MSG_DONTROUTE是单次有效。

    根据IPv4的正常机制,IP分组需要根据目的地址和路由表找到适当本地接口,如果无法确定本地接口,就会报错。

    可以使用本选项,以绕过上述机制,强制从某个接口发出。路由守护进程就是这么做的。

  MSG_DONTWAIT

    临时打开非阻塞标志,进行IO,关闭非阻塞标志

  MSG_OOB

    让本次调用,用于接受或发送带外数据。

    TCP连接上有一个字节可以作为带外数据发送。带外数据是加急数据。

  MSG_PEEK

    允许查看可读数据,且recv或recvfrom返回后,该数据仍然在缓冲区,不被丢弃。

  MSG_WAITALL

    通知内核,在尚未读到全部指定数量的数据前不返回。这样就可以简化readn

#define readn(fd, ptr, n)    recv(fd, ptr, n, MSG_WAITALL)

    即使这样,也有可能读到少的数据:(a)信号中断,(b)连接终止(c)套接字错误

 

以上是关于第14章——高级IO函数的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记导航

学习笔记导航

慕课网_反射——Java高级开发必须懂的

算法导论

apue 第14章 高级I/O

Java面向对象程序设计第14章3-8和第15章6