Loading

《Unix 网络编程》14:高级 I/O 函数

高级 I/O 函数

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

系列文章导航:《Unix 网络编程》笔记

  • 在 I/O 操作上设置超时的三种方法
  • recv 和 send 允许通过第四个参数从进程到内核传递标志
  • readv 和 writev 允许指定往其中输入数据或从其中输出数据的缓冲区向量
  • recvmsg 和 sendmsg 结合了其他 I/O 函数的所有特性,并具备接收和发送辅助数据的新能力

套接字超时

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

SIGALRM

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

connect

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
static void connect_alarm(int);

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

    // Signal 是我们自己包装的函数,用来设置处理函数,返回原来的 old 处理函数
    sigfunc = Signal(SIGALRM, connect_alarm);

    // alarm 的返回值:
  	// 0 如果成功设置
  	// 如果已经设置过,返回剩余的时间
    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() */
}

为什么可以打断 connect

可以参考前文的:可中断的系统调用

这一点有点像 Java 中的 InterruptedExecption 那一部分

这个函数的缺陷

connect 的超时通常为 75s ,我们只能指定一个更小的值,如果指定了一个更大的值,那么 connect 仍将在 75s 后发生超时

尽管本例子相当简单,但是在多线程中正确使用信号却非常困难,因此只建议在未线程化或单线程化的程序中使用本技术

recvfrom

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
static void sig_alrm(int);

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\n");
            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() */
}

这个代码还是比较清晰的,在 recvfrom 前设置 alarm

select

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
int readable_timeo(int fd, int sec) {
    fd_set rset;
    struct timeval tv;

    FD_ZERO(&rset);
    FD_SET(fd, &rset);

  	// 把 select 的时间设为用户传入的时间
    tv.tv_sec = sec;
    tv.tv_usec = 0;
		// 出错返回 -1
  	// 超时返回 0
  	// 否则返回已就绪的描述符的数目
    return (select(fd + 1, &rset, NULL, NULL, &tv));
    /* 4> 0 if descriptor is readable */
}

这个的源代码也比较清晰,具体在注释中就体现了

下面是一个使用的案例:

void dg_cli(FILE* fp, int sockfd, const SA* 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; /* null terminate */
            Fputs(recvline, stdout);
        }
    }
}

套接字选项

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

套接字选项一旦设置到某个描述符,其超时设置将应用于该描述符的所有读操作

  • SO_RCV.TIME.O 用于设置收消息超时
  • 类似地,SO_SND.TIME.O 选项则仅仅应用于写操作
  • 两者都不能用于为 connect 设置超时
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) {
            // 如果超时,发生 E.WOULD.BLOCK 错误
            if (errno == EWOULDBLOCK) {
                fprintf(stderr, "socket timeout\n");
                continue;
            } else
                err_sys("recvfrom error");
        }

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

消息处理的函数变体

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

recv 和 send

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

类似标准的 recv 和 send 函数,不过需要一个额外的 flags 参数:

#include <sys/socket.h>

ssize_t recv(int sockfd, void *buff, size_t nbytes, int flags);
ssize_t send(int sockfd, const void *buff, size_t nbytes, int flags);

flag 的具体说明:

  • MSG_DONT.ROUTE:告知内核目标主机在某个直接相连的本地网络上,无需执行路由表查找

    也可以用套接字选项 SO_DONTROUTE 对套接字开启

  • MSG_DONT.WAIT:在无需打开相应套接字的非阻塞标志的前提下,把单个 IO 操作临时设置为非阻塞

  • MSG_OOB:对于 send,本标志指明即将发送带外数据,对于 recv,指明即将读入的是带外数据不是普通数据

  • MSG_PEEK:适用于 recv 和 recvfrom,它允许我们查看已可读取的数据,而且系统不在操作后丢弃这些数据

  • MSG_WAITALL:它告知内核不要在尚未读入请求数目的字节之前让一个读操作返回

    即使指定了这个标识,如果发生如下情况之一,也仍可能返回比请求字节数少的数据:

    1. 捕获一个信号
    2. 连接被终止
    3. 套接字发生一个错误

flags 的问题

  • 它是按值传递的,而不是一个值-结果参数
  • 因此只能用于从进程向内核传递标志,而内核无法向内核传回标志
  • 这在 TCP/IP 协议不成问题,因为几乎不需要从内阁传回标志
  • 然而随着 OSI 协议的变化,提出了随输入操作向进程返送 MSG_EOR 标志的需求
  • 后面的 recvmsgsendmsg 可以解决这个需求

readv 和 writev

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

read 和 write 的缺点:

  • 使用read()将数据读到不连续的内存、使用write()将不连续的内存发送出去,要经过多次的调用read、write

  • 如果要从文件中读一片连续的数据至进程的不同区域,有两种方案:

    • 使用read()一次将它们读至一个较大的缓冲区中,然后将它们分成若干部分复制到不同的区域

    • 调用read()若干次分批将它们读至不同区域

    • 同样,如果想将程序中不同区域的数据块连续地写至文件,也必须进行类似的处理

怎么解决多次系统调用+拷贝带来的开销呢

UNIX提供了另外两个函数—readv()和writev(),它们只需一次系统调用就可以实现在文件和进程的多个缓冲区之间传送数据,免除了多次系统调用或复制数据的开销[1]

这两个函数类似 readwrite ,但是允许单个系统调用读取或写出到一个或多个缓冲区,即:分散读集中写

#include <sys/uio.h>

ssize_t readv(int filedes, // 读到哪里
              const struct iovec *iov, // 数组指针,从哪里读
              int iovcnt); // 数量
ssize_t writev(int filedes, // 从哪里读
               const struct iovec *iov, // 写到哪里
               int iovcnt);

// 其中 struct iovec:
struct iovec {
  void *iov_base; // 开始的地址
  size_t iov_len; // 长度
}
  • writev 是一个原子操作,这意味着对如 UDP 而言,一次 writev 只产生单个 UDP 数据报

recvmsg 和 sendmsg

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

这两个函数是最通用的 IO 函数,实际上我们可以把所有 read、readv、recv、recvfrom 调用替换成 recvmsgsendmsg 也类似。

#include <sys/socket.h>

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);

这两个函数把大部分参数封装到了 msghdr 结构中:

struct msghdr {
  // 如下两个参数用于套接字未连接的场景
  void *msg_name;		// 指向一个套接字地址结构,无需指明则置空
  socklen_t msg_namelen;  // 存放长度(对sendmsg来说是一个值参数,对recvmsg来说是值-结果参数)
  
  // 如下两个参数指定输入或输出缓冲区数组
  struct iovec *msg_iov;
  int msg_iovlen;
  
  // 如下两个参数指定可选的辅助数据的位置和大小
  void *msg_control; 
  socklen_t msg_controllen; // (对recvmsg来说是值-结果参数)
  
  // 只有 recvmsg 使用这个成员,sendmsg 只使用外面的那个 flags 参数
  // recvmsg 调用时,flags 参数被复制到 msg_flags 中,并由内核更新其值
  int msg_flags; 
}

flags 的不同位置的不同设置

这里再记录一下后面 7 个的作用:

  • MSG_BCAST:本标志随 BSD/OS 引入,相对较新。它的返回条件是本数据包作为链路层广播收取或者其目的 IP 地址是一个广播地址。与 IP_RECVD-STADDR 套接字选项相比,本标志是用于判定一个 UPD 数据包是否发往某个广播地址的更好方法
  • MSG_MCAST:本标志随 BSD/OS 引入,相对较新。它的返回条件是本数据报作为链路层多播收取。
  • MSG_TRUNC:本标志的返回条件是本数据报被截断,也就是说,内核预备返回的数据超过进程事先分配的空间(所有 iov_len 成员之和)。
  • MSG_CTRUNC:本标志的返回条件是本数据报的辅助数据被截断,也就是说,内核预备返回的辅助数据超过进程事先分配的空间(msg_controllen)。
  • MSG_EOR:本标志的返回条件是返回数据结束一个逻辑记录。TCP 不使用本标志,因为它是一个字节流协议。
  • MSG_OOB:本标志绝不为 TCP 带外数据返回。它用于其他协议族(如 OSI 协议族)。
  • MSG_NOTIFICATION:本标志由 SCTP 接收者返回,指示读入的消息是一个事先通知,而不是数据消息。

消息处理函数的对比

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

辅助数据

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

辅助数据(ancillary data)可通过调用 sendmsg 和 recvmsg 这两个函数,使用 msghdr 结构中的 msg_control 和 msg_controllen 这两个成员发送和接收。辅助数据的另一个称谓是控制信息(control information)。

关于辅助数据的基本使用,如结构、填充、传递、处理宏可以参考如下文章:

关于辅助数据的具体使用案例在之后的文章中会提到

排队的数据量

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

即在不真正读取数据的前提下直到一个套接字上已经有多少数据排队等着读取

有三种方法:

  1. 如果获悉已排队数据量的目的在于避免读操作阻塞在内核中,即没有数据可读时能做其他事情。这种情况值仅需要知道是否有数据而关心具体排队数量时,可以使用非阻塞IO

  2. 既想查看数据,又想数据留在接收队列以供进程中其他业务部分稍后读取,可以使用MSG_PEEK标志

    • 注意,设定 flags 不能肯定是否真有数据可读,可以结合非阻塞套接字使用,也可以组合使用MSG_DONTWAIT标志
    • 另外,对于流式套接字TCP和数据报套接字UDP,先后两次调用recv的结果有一定差异:
      • TCP 可能在两次调用之间又接收到了新的数据,所以可能不一致
      • UDP 则会完全相同
  3. 目前有些实现支持 ioctl 的 FIONREAD 命令,第三个参数是一个指向某整数的指针,用于接收内核返回当前套接字接收队列中可读的数据

原书这部分并没有给出代码,但是可以参考 网络编程(13)高级IO函数 (2)排队的数据量 作进一步了解

套接字和标准 IO

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

介绍

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

到目前为止的例子,我们一直使用 Unix I/O 的函数执行 IO 操作;这些函数围绕描述符工作,通常作为 Unix 内核中的系统调用实现。

执行 IO 的另一个方法是使用标准 I/O 函数库,这个函数库由 ANSI C 标准规范,意在便于移植到支持 ANSI C 的非 Unix 系统上。

标准 IO 处理我们直接使用 Unix I/O 函数时必须考虑的一些问题,比如自动缓冲输入流和输出流,不幸的是,这导致我们可能需要解决一些其他的问题。

问题

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

由于标准 IO 函数库中缓冲的存在,数据可能不会被立刻打印出来;

标准 IO 函数库的三种缓冲和输出的条件:

  1. 完全缓冲:缓冲区满、进程调用 flush、进程 exit 自己
  2. 行缓冲:碰到换行符、进程调用 fflush、进程 exit 自己
  3. 不缓冲:每次调用都发生 I/O

标准 IO 函数库的大多数 Unix 实现使用如下原则:

  • 标准错误输出总是不缓冲
  • 标准输入和标准输出完全缓冲,除非他们是终端设备(此时为行缓冲)
  • 所有其他 IO 流都是完全缓冲,除非他们是终端设备(此时为行缓冲)

在网络编程中的实践:

由于套接字不是终端设备,因此是完全缓冲

如果想实现 echo 函数的效果,可以:

  1. 调用 setvbuf 迫使输出流变为行缓冲
  2. 每次调用 fputs 后调用 fflush 强制输出

但是这两个办法都容易犯错,并且与 Nagle 算法的交互也是问题

所以:

  • 尽量避免在套接字上使用标准 IO 函数库
  • 在缓冲区而不是文本行上工作,如第三章所述
  • 当标准 IO 流的便利性大于对缓冲带来的 bug 时,使用标准 IO 流也可行,但很少见

高级轮询技术

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

他们都具备 select 和 poll 两个函数的特性

另外,这里提到的机制和代码应被认为是不可移植的

/dev/poll 接口

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

Solaris 上名为 /dev/poll 的特殊文件提供了一个可扩展的轮询大量描述符的方法

优势:

  • select 和 poll 的一个问题是,每次调用它们都得传递待查询的文件描述符
  • 而轮询设备能够在调用之间维持状态,因此轮询进程可以预先设置好待查询描述符的列表,然后进入一个循环等待事件发生,每次循环回来时不必再次设置该列表

使用步骤:

  1. 打开 /dev/poll

  2. 初始化 pollfd 结构数组

  3. 调用 write 往 /dev/poll 设备上写这个结构数组以把它传递给内核

  4. 执行 ioctlDP_POLL 命令阻塞自身以等待事件的发生

    传递给 ioctl 的 dvpoll 结构如下:

    struct dvpoll {
      struct pollfd* dp_fds; // 指向一个缓冲区,
      int dp_nfds; // 缓冲区的大小
      int dp_timeout; // 超时事件,或 -1 表示无
    }
    

代码:

void str_cli(FILE* fp, int sockfd) {
    int stdineof;
    char buf[MAXLINE];
    int n;
    int wfd;
    struct pollfd pollfd[2];
    struct dvpoll dopoll;
    int i;
    int result;

    // 打开 /dev/poll
    wfd = Open("/dev/poll", O_RDWR, 0);

  	// 初始化数组结构
    pollfd[0].fd = fileno(fp);
    pollfd[0].events = POLLIN;
    pollfd[0].revents = 0;

    pollfd[1].fd = sockfd;
    pollfd[1].events = POLLIN;
    pollfd[1].revents = 0;

  	// 调用 write 往 `/dev/poll` 设备上写这个结构数组以把它传递给内核
    Write(wfd, pollfd, sizeof(struct pollfd) * 2);

    stdineof = 0;
    for (;;) {
        // 执行 `ioctl` 的 `DP_POLL` 命令阻塞自身以等待事件的发生
        dopoll.dp_timeout = -1;
        dopoll.dp_nfds = 2;
        dopoll.dp_fds = pollfd;
        result = Ioctl(wfd, DP_POLL, &dopoll);

        /* loop through ready file descriptors */
        for (i = 0; i < result; i++) {
            if (dopoll.dp_fds[i].fd == sockfd) {
                /* socket is readable */
                if ((n = Read(sockfd, buf, MAXLINE)) == 0) {
                    if (stdineof == 1)
                        return; /* normal termination */
                    else
                        err_quit("str_cli: server terminated prematurely");
                }

                Write(fileno(stdout), buf, n);
            } else {
                /* input is readable */
                if ((n = Read(fileno(fp), buf, MAXLINE)) == 0) {
                    stdineof = 1;
                    Shutdown(sockfd, SHUT_WR); /* send FIN */
                    continue;
                }

                Writen(sockfd, buf, n);
            }
        }
    }
}

kqueue 接口

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

简介:

kueue 是在 UNIX 上比较高效的 IO 复用技术

常见的 IO 复用技术有 select, poll, epoll 以及 kqueue 等等

其中 epoll 为 Linux 独占,而 kqueue 则在许多 UNIX 系统上存在,包括 macOS

使用步骤:

  1. 调用 kqueue() ,返回一个 kqueue 描述符
  2. struct kevent changes[FD_NUM] 中保存要监视的事件列表
  3. 调用 EV_SET 将上一步的事件进行注册
  4. 进行 kevent() 调用,如果 changes 中有任何发生了变化,就保存在 struct kevents events[FD_NUM]
  5. 循环遍历并处理

可以参考这篇文章 kqueue用法简介 - luMinO - 博客园

T/TCP:事务目的 TCP

★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★
本文信息本文信息防爬虫替换信息
作者网站LYMTICShttps://lymtics.top
作者LYMTICS(樵仙)https://lymtics.top
联系方式me@tencent.mlme@tencent.ml
原文标题《Unix 网络编程》14:高级 I/O 函数《Unix 网络编程》14:高级 I/O 函数
原文地址https://www.cnblogs.com/lymtics/p/16350621.htmlhttps://www.cnblogs.com/lymtics/p/16350621.html
  • 如果您看到了此内容,则本文可能是恶意爬取原作者的文章,建议返回原站阅读,谢谢您的支持
  • 原文会不断地更新和完善排版和样式会更加适合阅读,并且有相关配图
  • 如果爬虫破坏了上述链接,可以访问 `lymtics.top` 获取更多信息
★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★ ★

T/TCP 是对 TCP 进行过略微修改的一个版本,所谓“事务”是客户的请求与服务器的应答,常见的事务如 DNS 请求与服务器的应答、HTTP 请求与服务器的应答等。

如果客户和服务器最近通过三次握手建立过连接,且都没有崩溃重启过,各自告诉缓存的一些信息都没有过时则他们能够避免彼此通信过的主机之间的三次握手。它能把 SYN、FIN 和数据组合到单个分节中,前提是数据的大小小于 MSS。

如下是一个案例:

可以看到不仅在网络中传输的分节有所减少(T/TCP 需要 3 个,TCP 需要 10 个,UDP 需要 2 个),而且客户从初始化到发送一个请求再到读取相应应答所花费的时间也减少了一个 RTT。

T/TCP 的优势:

T/TCP 的优势在于 TCP 的所有可靠性(序列号、超时、重传)以及慢启动和拥塞避免得以保留,而不像 UDP 那样把可靠性推给应用程序去实现。

T/TCP 的问题:

这只是一种试验性的协议。因为存在安全性问题,并没有成为标准,也没有被应用。[2]

在原书的第三版中已经删除,但是译者把这一部分又加上了


  1. https://blog.csdn.net/weixin_36750623/article/details/84579243 ↩︎

  2. https://baike.baidu.com/item/T%2FTCP/1647857 ↩︎

posted @ 2022-06-07 09:47  樵仙  阅读(37)  评论(0编辑  收藏  举报