前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >I/O复用——shutdown函数

I/O复用——shutdown函数

原创
作者头像
jackieluo
发布2018-12-23 15:24:17
1.1K0
发布2018-12-23 15:24:17
举报
文章被收录于专栏:Jackie技术随笔Jackie技术随笔

尽管修改后的str_cli函数已经可以同时处理输入和网络套接口的事件,但是它仍旧是不正确的。在它修改前的版本,即阻塞I/O模型下,一个回射请求的总时间是RTT(往返时间)加上服务器的处理时间。根据这个总时间,我们可以估计出回射固定行数的请求,需要花费多长的时间。

使用ping简单估算RTT

使用ping是一个测量RTT的简单方法。简单的用主机ping一下回射服务器所在的腾讯云云主机,取30次的平均值得到平均RTT是21.476ms。

代码语言:txt
复制
--- 150.*.*.* ping statistics ---
30 packets transmitted, 30 packets received, 0.0% packet loss
round-trip min/avg/max/stddev = 14.283/21.476/99.440/15.753 ms

ping报文的大小为84字节。其中ICMP报文56个字节,再加上20个字节的IP头和8个字节的ICMP头。因此IP报文的总长度为84字节。

那么我们可以估算一下,一行文本,长度假设为44字节,那么加上20个字节的IP头和20个字节的TCP头,每行对应的分组刚好是84字节,与ping分组的大小相同,那么运行回射客户端服务器,发送这行文本的RTT大约需要21.476ms。

使用原始的回射客户端服务器程序,发送10条44字节的文本测试一下,可以看到实际的时延和我们预估的一致。

代码语言:txt
复制
jackieluo@JACKIELUO-MB1 ~/Desktop/unpv13e/tcpcliserv ./tcpcli01 150.*.*.* < tcpcli_input.txt
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
cost:222

停-等方式填充管道

将客户与服务器间的网络当做全双工管道来考虑,假设:

  1. 以停-等方式发送请求,即客户向服务器发送请求,服务器应答,然后发送下一个,以此类推。
  2. 假设RTT为8个时间单位,即时刻0发送请求,时刻4服务器收到请求并应答,时刻7客户收到应答。
  3. 假设服务器没有处理时间,收到请求立即应答。
  4. 请求和应答的数据大小相同。

绘制满足上述假设的一个请求过程:

以停-等方式填充管道
以停-等方式填充管道

由于管道是全双工的,这样一个请求过程中,我们只用了1/8的管道容量,为了充分利用管道,我们可能会考虑批量地在客户端进行输入。

批量方式填充管道

在批量方式下,假设:

  1. 发出第一个请求后马上发出下一个。
  2. 客户可以以网络能接受的最快速度发送请求。
  3. 客户可以以最快的速度处理应答。

绘制一系列请求过程:

以批量方式填充管道
以批量方式填充管道

上图能够解释,为什么在当前版本的str_cli函数下,当我们对输入输出进行重定向时,输出文件总是会小于输入文件。

代码语言:txt
复制
#include "unp.h"

void str_cli(FILE *fp, int sockfd) {
  char sendline[MAXLINE], recvline[MAXLINE];

  while (Fgets(sendline, MAXLINE, fp) != NULL) {
    Writen(sockfd, sendline, strlen(sendline));

    if (Readline(sockfd, recvline, MAXLINE) == 0) {
      err_quit("str_cli: server terminated prematurely");
    }
    Fputs(recvline, stdout);
  }
}

假设输入文件有9行,时刻8发送完这行以后,Fgets返回NULL,跳出循环,到达函数尾,main程序中止,但是此时仍有请求和应答在路上,未被客户处理。

管道中仍有未完成请求和应答
管道中仍有未完成请求和应答

因此我们需要一种方式来关闭TCP连接的一半,给服务器发送一个FIN,告诉它已经完成数据发送,但是仍开放套接口描述字用于读数据。这就需要shutdown函数来完成。

shutdown 函数

代码语言:txt
复制
# include <sys/socket.h>
int	shutdown(int sockfd, int howto);//返回——0 成功,-1——出错

函数具体的行为取决于第二个参数howto

参数

备注

SHUT_RD

关闭连接的读一半,不再接收套接口中的数据,且接收缓冲区数据作废。进程不能再对套接口执行任何读操作。调用后,由TCP套接口接收到的数据仅做确认,而不实际接收。

SHUT_WD

关闭连接的写一半,又称半关闭。发送缓冲区的数据都发送出去,然后TCP连接终止。无论描述字访问计数是否为0,进程都不能再对套接口执行任何写操作。

SHUT_RDWR

关闭连接的读和写。等效于先使用SHUT_RD调用,然后使用SHUT_WD调用。

终止网络连接的正常方法是调用close,但close有两个限制可由函数shutdown来避免。

  1. close将描述字的访问计数减1,仅在计数为0时才关闭套接口。shutdown可发起TCP的正常连接终止序列,无需访问计数为0。
  2. close会关闭数据传输的读/写两个方向。shutdown可以只关闭连接的某一半。
调用shutdown关闭TCP连接的写一半
调用shutdown关闭TCP连接的写一半

再修订版str_cli函数

在上一节加入select模型的str_cli函数的基础上再次进行修改,标准输入遇到文件结束符时,调用shutdown函数,关闭TCP连接的读一半,修改标志位为1,当从套接口读到文件终止符,而此标志位为1时,说明这是正常的终止。

再修订版str_cli函数
再修订版str_cli函数

使用批量方式后,再次运行输入之前的10行文本的文件,比较耗时:

代码语言:txt
复制
jackieluo@JACKIELUO-MB1 ~/Desktop/unpv13e/tcpcliserv ./tcpcli02 150.*.*.* < tcpcli_input.txt
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh
cost:34

可以看到,批量输入的方式比停-等输入的方式快了很多。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用ping简单估算RTT
  • 停-等方式填充管道
  • 批量方式填充管道
  • shutdown 函数
  • 再修订版str_cli函数
相关产品与服务
云服务器
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档