核心机制七----延时应答
默认情况下,接收方都是在收到数据报的第一时间,就返回ack,但是可以通过延时返回ack的方式来提高效率,理论上不是100%提高效率,但还是有一定帮助的。
因为如果接收数据的主机⽴刻返回ACK应答,这时候返回的窗⼝可能⽐较⼩.
- 假设接收端缓冲区为1M.⼀次收到了500K的数据;如果⽴刻应答,返回的窗⼝就是500K; 但实际上可能处理端处理的速度很快,10ms之内就把500K数据从缓冲区消费掉了 ,在这种情况下,接收端处理还远没有达到⾃⼰的极限,即使窗⼝再放⼤⼀些,也能处理过来;
- 如果接收端稍微等⼀会再应答,⽐如等待200ms再应答,那么这个时候返回的窗⼝⼤⼩就是1M; ⼀定要记得,窗⼝越⼤,⽹络吞吐量就越⼤,传输效率就越⾼.我们的⽬标是在保证⽹络不拥塞的情况下 尽量提⾼传输效率;
那么所有的包都可以延迟应答么?肯定也不是;
• 数量限制:每隔N个包就应答⼀次; • 时间限制:超过最⼤延迟时间就应答⼀次; 具体的数量和超时时间,依操作系统不同也有差异;⼀般N取2,超时时间取200ms(这个数值不固定,根据实际情况而定);
核心机制八---捎带应答
在延迟应答的基础上,我们发现,很多情况下,客⼾端服务器在应⽤层也是"⼀发⼀收"的.意味着客⼾端 给服务器说了"How areyou",服务器也会给客⼾端回⼀个"Fine,thank you"; 那么这个时候ACK就可以搭顺⻛⻋,和服务器回应的"Fine,thank you"⼀起回给客⼾端。
引入了延时应答,ack可以往后延时一段时间,恰好这个时候要返回响应数据,此时就可以把ack也代入到响应数据中,一起返回,ack设为1,窗口大小设为接收缓冲区剩余值,确认序号设为合适的值;把两个包合成一个,就能起到提高效率的作用。
核心机制九---面向字节流
创建⼀个TCP的socket,同时在内核中创建⼀个发送缓冲区和⼀个接收缓冲区;
- 调⽤write时,数据会先写⼊发送缓冲区中; 如果发送的字节数太⻓,会被拆分成多个TCP的数据包发出; 如果发送的字节数太短,就会先在缓冲区⾥等待,等到缓冲区⻓度差不多了,或者其他合适的时机发 送出去;
- 接收数据的时候,数据也是从⽹卡驱动程序到达内核的接收缓冲区;然后应⽤程序可以调⽤read从接收缓冲区拿数据;
- 另⼀⽅⾯,TCP的⼀个连接,既有发送缓冲区,也有接收缓冲区,那么对于这⼀个连接,既可以读数据,也可以写数据.
- 这个概念叫做全双⼯由于缓冲区的存在,TCP程序的读和写不需要⼀⼀匹配,例如:写100个字节数据时,可以调⽤⼀次write写100个字节,也可以调⽤100次write,每次写⼀个字节;读100个字节数据时,也完全不需要考虑写的时候是怎么写的,既可以⼀次read100个字节,也可以⼀ 次read⼀个字节,重复100次;
在这个过程中会出现粘包问题
1.⾸先要明确,粘包问题中的"包",是指的应⽤层的数据包.
- 在TCP的协议头中,没有如同UDP⼀样的"报⽂⻓度"这样的字段,但是有⼀个序号这样的字段.
- 站在传输层的⻆度,TCP是⼀个⼀个报⽂过来的.按照序号排好序放在缓冲区中.站在应⽤层的⻆度,看到的只是⼀串连续的字节数据. 那么应⽤程序看到了这么⼀连串的字节数据,就不知道从哪个部分开始到哪个部分,是⼀个完整的应 ⽤层数据包.
2. 那么如何避免粘包问题呢?归根结底就是⼀句话,明确两个包之间的边界.
- 对于定⻓的包,保证每次都按固定⼤⼩读取即可;例如上⾯的Request结构,是固定⼤⼩的,那么就从 缓冲区从头开始按sizeof(Request)依次读取即可;
- 对于变⻓的包,可以在包头的位置,约定⼀个包总⻓度的字段,从⽽就知道了包的结束位置; 对于变⻓的包,还可以在包和包之间使⽤明确的分隔符(应⽤层协议,是程序猿⾃⼰来定的,只要保证 分隔符不和正⽂冲突即可);
3.思考:对于UDP协议来说,是否也存在"粘包问题"呢?
- 对于UDP,如果还没有上层交付数据,UDP的报⽂⻓度仍然在.同时,UDP是⼀个⼀个把数据交付给应 ⽤层.就有很明确的数据边界.
- 站在应⽤层的站在应⽤层的⻆度,使⽤UDP的时候,要么收到完整的UDP报⽂,要么不收.不会出 现"半个"的情况.
核心机制十----异常情况的处理
TCP在通信过程中存在特殊情况
1.进程终止:进程终止和主动退出没有本质区别,进程终止会释放⽂件描述符,仍然可以发送FIN,进程虽然没了,但是TCP的连接信息还存在,此时四次挥手还是可以正常进行的。。
2.主机关机:正常流程的关机,本质上还是会先杀死所有的用户进程,与进程终止情况相同。
最终B任然可以把连接释放掉
3.主机掉电/网线断开:接收端认为连接还在,⼀旦接收端有写⼊操作,接收端发现连接已经不在了,就 会进⾏reset.即使没有写⼊操作,TCP⾃⼰也内置了⼀个保活定时器,会定期询问对⽅是否还在.如果 对⽅不在,也会把连接释放。
主机掉电:
网线断开就是上面两种情况的结合
TCP小结
为什么TCP这么复杂?因为要保证可靠性,同时⼜尽可能的提⾼性能.
- 可靠性: • 校验和 • 序列号(按序到达)• 确认应答 • 超时重发 • 连接管理 • 流量控制 • 拥塞控制
- 提⾼性能:• 滑动窗 • 快速重传 • 延迟应答 • 捎带应答
- 其他: • 定时器(超时重传定时器,保活定时器,TIME_WAIT定时器等)
TCP与UDP的对比
TCP用于可靠传输的情况,应用于文件传输,重要状态更新等场景;UDP用于对⾼速传输和实时性要求较⾼的通信领域,例如,早期的QQ,视频传输等.另外UDP可以⽤ 于⼴播;
如何基于UDP实现可靠运输?往TCP上套
3.网络层
3.1IP协议
基本概念
• 主机:配有IP地址,但是不进⾏路由控制的设备; • 路由器:即配有IP地址,⼜能进⾏路由控制; • 节点:主机和路由器的统称;
协议头格式:
IP协议/网络层做的工作,主要是两个:
1.地址管理,使用IP地址这样的概念,标识网络上的某个设备的位置;
2.路由器选择,在两个通信的节点之间,规划处一个合理的路径。
各部分相关介绍:
- 4位版本号(version):指定IP协议的版本,对于IPv4来说,就是4.
- 4位首部长度:IP协议的报头,也是变长的选项,IP头部的⻓度是多少个32bit,也就是length*4的字节数.4bit表⽰最 ⼤的数字是15,因此IP头部最⼤⻓度是60字节.
- 8位服务类型:决定了IP协议的工作方式,3位优先权字段(已经弃⽤),4位TOS字段,和1位保留字段(必须置为 0).4位TOS分别表⽰:最⼩延时,最⼤吞吐量,最⾼可靠性,最⼩成本.这四者相互冲突,只能选择⼀个. 对于ssh/telnet这样的应⽤程序,最⼩延时⽐较重要;对于ftp这样的程序,最⼤吞吐量⽐较重要.
- 16位总长度:IP数据报整体占多少个字节,就是一个IP数据报(报头+载荷)的长度.
- 16位标识:拆包,把拆出来的多个包,设为相同的标识;组包,把相同标识的数据包组合到一起.
- 3位标志:其中一个标志位表示是否触发了拆包操作,还有一个表示当前包是否是最后一个。
- 13位片偏移:描述了先后顺序,偏移小的放前面,大的放后面.
- 8位生存时间:数据报到达⽬的地的最⼤报⽂跳数.⼀般是64.每次经过⼀个路 由,TTL-=1,⼀直减到0还没到达,那么就丢弃了.这个字段主要是⽤来防⽌出现路由循环.
- 8位协议:标识传输层使用哪种协议,分用的时候,IP协议解析IP数据报的时候,拿到载荷,交给上层处理,此处8位协议号,就能起到区分效果.(传输层到应用层通过端口号区分,网络层到传输层=>8位协议,数据链路层到网络层=>也有一个类似的协议编号)
- 16位首部校验和:使⽤CRC进⾏校验,来鉴别头部是否损坏.
- 32位源地址和32位⽬标地址:表⽰发送端和接收端,IP协议最关键的部分,IP地址本质上是通过32位的整数来表示的,由于32位 整数不方便阅读,通常会把IP写作点分十进制表示方式,用三个点分成四个部分。如图:
.
3.2地址管理
IP不够了该怎么办?
1.动态分配IP:上网再分配,不上网就不分配,有所缓解,不能根本上解决问题.
2.NAT机制,网络地址转换(当前网络世界最主要的方式),把所有的IP分成两个大类
公网IP/外网IP:公网IP是唯一的
私网IP/内网IP:私网IP在不同的局域网中,可以重复。
- 10.*,前8位是⽹络号,共16,777,216个地址
- 172.16.到172.31.,前12位是⽹络号,共1,048,576个地址
- 192.168.*,前16位是⽹络号,共65,536个地址 包含在这个范围中的,都成为私有IP,其余的则称为全局IP(或公⽹IP);
NAT机制,就可以用一个外网IP对应到一系列的内网的设备,一个设备有一个独立的局域网IP(允许重复),但是多个设备共用同一个外网IP(不允许重复).
NAT背景下网络通信如何进行
1.同一个局域网下,设备A访问设备B,由于IP本身不允许重复,自然不收任何影响,NAT不起到作用.
2.公网设备A访问公网设备B,由于公网IP本身也不重复,也不受到影响,NAT不起作用.
3.不同局域网,设备A访问设备B是不允许的,NAT机制禁止这样的访问方式.
4.局域网设备A访问公网设备B.就是网络地址映射。
相关流程图:
5.公网设备A访问局域网设备A,不允许.
3.IPv6:IPv6并不是IPv4的简单升级版.这是互不相⼲的两个协议,彼此并不兼容;IPv6⽤16字节128位 来表⽰⼀个IP地址;但是⽬前IPv6还没有普及.
IP地址的其他规则
⽹段划分
IP地址分为两个部分:⽹络号和主机号
⽹络号:保证相互连接的两个⽹段具有不同的标识;主机号:同⼀⽹段内,主机之间具有相同的⽹络号,但是必须有不同的主机号;
在同一个局域网中,网络号必须相同,主机号必须不同;两个相邻的局域网中,网络号必须不同,主机号无限制。
- 不同的⼦⽹其实就是把⽹络号相同的主机放到⼀起.
- 如果在⼦⽹中新增⼀台主机,则这台主机的⽹络号和这个⼦⽹的⽹络号⼀致,但是主机号必须不能和 ⼦⽹中的其他主机重复. 通过合理设置主机号和⽹络号,就可以保证在相互连接的⽹络中,每台主机的IP地址都不相同.
那么问题来了,⼿动管理⼦⽹内的IP,是⼀个相当⿇烦的事情.
• 有⼀种技术叫做DHCP,能够⾃动的给⼦⽹内新增主机节点分配IP地址,避免了⼿动管理IP的不便. ⼀般的路由器都带有DHCP功能.因此路由器也可以看做⼀个DHCP服务器。
过去曾经提出⼀种划分⽹络号和主机号的⽅案,把所有IP地址分为五类,如下图所⽰:
这个方案,固定分为五类,使用前缀来区分是哪一类,每一个类,网络主机号都是固定的,但随着Internet的⻜速发展,这种划分⽅案的局限性很快显现出来,⼤多数组织都申请B类⽹络地址,导致B 类地址很快就分配完了,⽽A类却浪费了⼤量地址;
针对这种情况提出了新的划分⽅案,称为CIDR(Classless Interdomain Routing):
引⼊⼀个额外的⼦⽹掩码(subnet mask)来区分⽹络号和主机号;⼦⽹掩码也是⼀个32位的正整数.通常⽤⼀串"0"来结尾; 将IP地址和⼦⽹掩码进⾏"按位与"操作,得到的结果就是⽹络号; ⽹络号和主机号的划分与这个IP地址是A类、B类还是C类⽆关;
可⻅,IP地址与⼦⽹掩码做与运算可以得到⽹络号,主机号从全0到全1就是⼦⽹的地址范围;
特殊的IP地址
- 将IP地址中的主机地址全部设为0,就成为了⽹络号,代表这个局域⽹;
- 将IP地址中的主机地址全部设为1,就成为了⼴播地址,⽤于给同⼀个链路中相互连接的所有主机发 送数据包;
- 127.*的IP地址⽤于本机环回(loop back)测试,通常是127.0.0.1。
小结:
1.IPv4地址,占几个字节,不够用了(动态分配,NAT,IPv6);
2.IP地址构成:网络号+主机号 子网掩码;
3.特殊的IP:主机号为0,主机号全为1,127开头。
3.3路由选择
通过IP协议进行数据转发的过程,在复杂的⽹络结构中,找出⼀条通往终点的路线;路由的过程,是⼀跳⼀跳"问路"的过程. 所谓"⼀跳"就是数据链路层中的⼀个区间.具体在以太⽹中指从源MAC地址到⽬的MAC地址之间的帧 传输区间.
主要步骤:
IP数据包的传输过程也和问路一样,当数据包到达某个路由器的时候,就会匹配这个这个路由器的“路由表”(路由表记录了这个路由器周围的设备的IP,以及每个设备要通过那个路口转发过去);如果目的IP刚好匹配到了路由表中的记录,直接按照当前的对应的口转发过去就行了;如果没有匹配到,路由表就会有下一个特殊的表项,“下一跳”指向的设备,即上一级路由器所在的位置(这些路由器越往上,涵盖的范围也越大)。