Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >I/O复用——shutdown函数

I/O复用——shutdown函数

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

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

使用ping简单估算RTT

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

代码语言:txt
AI代码解释
复制
--- 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
AI代码解释
复制
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
AI代码解释
复制
#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
AI代码解释
复制
# 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
AI代码解释
复制
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 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
TCP回射客户-服务器程序
创建一个TCP套接口,用通配地址(INADDR_ANY)和unp.h中定义的众所周知端口(SERV_PORT),端口号为9877。
jackieluo
2018/10/20
4.3K0
TCP回射客户-服务器程序
UDP套接口编程
常用的UDP实现的程序:DNS域名系统,NFS网络文件系统,SNMP简单网络管理协议 ssize_t recvfrom(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr * from,socklen_t *addrlen); ssize_t sendto(int sockfd,void *buff,size_t nbytes,int flags,struct sockaddr * to,socklen_t addrlen); sock
用户1154259
2018/01/17
1K0
UNIX(进程间通信):17 深入理解unix域
unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法。
用户3479834
2021/03/04
1.9K0
UNIX(进程间通信):17 深入理解unix域
1-UNIX网络编程-Socket套接字编程简介
触发学习UNIX网络编程的动力在于前段时间需要开发一个接入服务,需要考虑比较高的并发处理能力,且尽量少占用的机器资源,选用了JAVA的Netty框架,学习过程产生不少疑问,限于基础知识太薄弱无法理解原理,所以开始关注UNIX编程。
zoujunjie202
2022/07/27
1.4K0
1-UNIX网络编程-Socket套接字编程简介
I/O复用——单进程服务器(select版)
为了可以处理多个客户的请求,我们之前一直使用多进程TCP并发服务器,socket()监听一个套接口,accept()多个用户,父进程监听listenfd,子线程们在connfd上进行应答处理。
jackieluo
2019/01/06
2.1K0
I/O复用——单进程服务器(select版)
网络编程入门_回显服务器
sockaddr和sockaddr_in包含的数据都是一样的,但他们在使用上有区别: 程序员不应操作sockaddr,sockaddr是给操作系统用的 程序员应使用sockaddr_in来表示地址,sockaddr_in区分了地址和端口,使用更方便。 一般的用法为: 程序员把类型、ip地址、端口填充sockaddr_in结构体,然后强制转换成sockaddr,作为参数传递给系统调用函数
yifei_
2022/11/14
7670
网络编程入门_回显服务器
linux服务器开发三(网络编程) --二
半关闭 当TCP链接中A发送FIN请求关闭,B端回应ACK后(A端进入FIN_WAIT_2状态),B没有立即发送FIN给A时,A方处在半链接状态,此时A可以接收B发送的数据,但是A已不能再向B发送数据。 从程序的角度,可以使用API来控制实现半连接状态。 #include <sys/socket.h> int shutdown(int sockfd, int how); sockfd: 需要关闭的socket的描述符 how: 允许为shutdown操作选择以下几种方式: SHUT
李海彬
2018/03/27
2.6K0
linux服务器开发三(网络编程) --二
简单的客户机服务器投射模拟
下面模拟了,简单的客户机服务器投射模拟的过程。客户机像服务器发送数据,服务器接受到数据后,发送回给客户机。再由客户机打印出来。 需要的函数: 网络方面 服务器 socket(AF_INET,SOCK_STREAM,0);  AF_INET表示IPV4,SOCK_STREAM表示基于字节流的,0表示协议由前面两个参数组合而成。返回描述符 bind(sockdf,(struct sockaddr*)servaddr,sizeof(servaddr));   用于把描述符与本地协议地址联系起来。 listen
用户1154259
2018/01/17
1K0
如何对回显服务器进行改进_1
上一篇中写了一个基本的回显服务器,最基本的功能是有了,但是并不够健壮,那么如何对它进行改进呢?我们需要考虑以下几种情况。
yifei_
2022/11/14
7300
浅谈I/O多路复用
​作为程序员,在日常工作中,都或多或少的接触过网络I/O这个概念,接触过网络编程,听说过socket等等,但是对于更深层次的理解,多少还是有点欠缺,通过本文,可以了解网络中最重要的模块I/O,以及对几种网络模型的介绍,在我们日常工作开发过程中,可以针对特定需求,选择特定的网络模型,达到事半功倍的效果。
高性能架构探索
2021/03/26
1K0
计算机网络面试题整理
要是对于mongodb还有点兴趣的话,可以查看文章 一文便知 GO 中mongodb 的安装与使用
阿兵云原生
2023/02/16
5220
poll()函数总结
http://www.cnblogs.com/Anker/archive/2013/08/15/3261006.html
bear_fish
2018/09/20
2.4K0
poll()函数总结
linux网络编程之socket(十):shutdown 与 close 函数 的区别
s1mba
2017/12/28
3.3K0
I/O多路复用select/poll/epoll
早期操作系统通常将进程中可创建的线程数限制在一个较低的阈值,大约几百个。因此, 操作系统会提供一些高效的方法来实现多路IO,例如Unix的select和poll。现代操作系统中,线程数已经得到了极大的提升,如NPTL线程软件包可支持数十万的线程。
WindSun
2019/09/09
1.4K0
I/O多路复用select/poll/epoll
select()函数详解
http://www.cnblogs.com/Anker/archive/2013/08/14/3258674.html
bear_fish
2018/09/20
1.9K0
select()函数详解
UNPv1第五章:TCP客户服务器程序实例
这个简单的例子是完成下述功能的一个回射服务器: 1. 客户从标准输入读一行文本,写到服务器上 2. 服务器读入此行,并回射给客户 3. 客户读此回射行写到标准输出
提莫队长
2019/02/21
5140
Linux的SOCKET编程详解
进 程通信的概念最初来源于单机系统。由于每个进程都在自己的地址范围内运行,为保证两个相互通信的进
黄规速
2022/04/14
8.7K1
Linux的SOCKET编程详解
I/O复用——select函数
select函数让进程告诉内核,等待数个事件,某个事件发生或者达到指定时间时,唤醒进程。
jackieluo
2018/12/02
1.2K0
I/O复用——select函数
CSAPP 网络编程 笔记
v2ray 文档 https://www.v2ray.com/developer/intro/roadmap.html
wywwzjj
2023/05/09
6750
SOCKET网络编程 (通俗易懂入门篇)
struct sockaddr :很多网络编程函数的出现早于IPV4协议,为了向前兼容,现在sockaddr都退化成(void *)结构了。 传递一个地址给函数,然后由函数内部再强制类型转换为所需的地址类型。
看、未来
2020/08/25
1.3K0
SOCKET网络编程 (通俗易懂入门篇)
相关推荐
TCP回射客户-服务器程序
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档