目录
TCP/IP五层协议栈
描述一个网络数据的时候,用了这几个词:包、报、段、帧
传输层的数据,通常叫做“段”
网络层的数据,通常叫做“包、报”
数据链路层,通常叫做“帧”
应用层
程序员最经常打交道的一层,其他四层,都是操作系统、驱动、硬件实现好了的。
在应用层这里,最重要的事情,就是“设计并实现一个应用层协议”。
当下比较流行的 协议模板(数据的组织形式)
1.xml (可读性好,运行效率不高)
2.json(可读性好,运行效率不高)
3.protobuffer(可读性不好,运行效率很高)
…
xml
xml由标签构成
因此,xml现在很少作为应用层协议的设计模板了,现在使用xml主要是作为一些配置文件。
json
当下最流行的一种,设计应用层协议的数据格式
json虽然传输效率比xml要高,但是仍然要多传递一些冗余信息,就是key的名字,这一点在表示数组的时候,尤为明显。
protobuffer
是一种二进制格式的数据
在protobuffer的数据中,不在包含上面key的名字了,而是通过顺序以及一些特殊符号,来区分每个字段的含义,同时在通过一个IDL文件来描述这个数据格式(每个部分是什么意思),IDL只是起到一个辅助开发的效果,并不会真正的进行传输。
以下是一个简化的表示版本
1\3\3 蛋炒饭\210\21\210\3炒面\28\21\28
通过二进制的数据重新对这里的内容进行编排,甚至可能会进行一些数据压缩。
这样做传输效率会更高,但是也会让这个数据肉眼难以观察,调试起来就不方便了。
除此之外,业界也有一些现成的,已经被设计好的,已经被广泛使用了的应用层协议,其中最知名的应用层协议就是HTTP。
传输层
端口号 0-65535之间的整数
知名端口号:把0-1024这些端口号,划分出了一些具体的作用
80:HTTP服务器
443:https服务器
22:ssh
23:ftp
UDP
学习一个协议,很多时候是在研究报文格式
TCP
TCP中的很多机制,都是围绕可靠性展开的。
确认应答
保证可靠传输的核心机制。
可靠性:发送发发出数据之后,能够知道对方有没有收到
关键就是接收方收到消息之后,给发送方返回一个应答报文(ACK,acknowledge),表示自己已经收到了。
超时重传
相当于是对确认应答的补充
确认应答是网络一切正常的时候,通过ACK通知发送方“我收到了”,如果出现了丢包的情况,超时重传机制就要起到效果了。
超时重传,重传的数据,一定就会成功吗?不一定。
如果只是因为网络抖动了一下,这个时候重传还是很容易成功的,如果是网络遭受了严重的伤害,可能就没有那么容易恢复,重传也成功不了。
在这个情况下,重传不会一直进行。
重传如果失败,可能还会在尝试,但是也不会无休止的重传。连续几次重传都不行,就认为这个网络可能遭受了严重 的情况,在怎么传也不行,就只能放弃了。(自动的断开TCP的连接)。重传的时间间隔,也不是固定的。
基于上述的两个机制,TCP的可靠性,就得到了有效的保证。
连接管理【重重重点】
也是TCP保证可靠性的一个机制。
如何建立连接
三次握手
客户端和服务器之间,通过三次交互,完成了建立连接的过程。
1.三次握手有什么用?和可靠性有什么关系?
(1)三次握手,相当于是投石问路,检查这个网络的情况是否满足可靠传输的基本情况。
其实也是在检测通信双方,发送能力和接收能力是否都正常。
如果网络本身就效果非常差,强行进行TCP传输,也会涉及到大量丢包。
(2)让双方能够协商一些必要的信息。
2.为什么握手是三次?两次、四次可以吗?
四次行,但没必要,分开传输降低效率。
两次,不行。意味着缺少最后一次。此时客户端这边关于发送接收能力正常的情况是完整的,但是服务器这边是残缺的,服务器不知道自己的发送能力和客户端的接收能力是否正常。
如何断开连接
四次挥手
三次握手,就是让客户端和服务器之间建立好了连接
其实建立好连接之后,操作系统内核中,就需要使用一定的数据结构来保存相关的信息。保存的信息就是前面讨论的“五元组’。(而且是客户端和服务器都要保存五元组)
源IP、目的IP、源端口、目的端口、协议类型(TCP)
既然保存了信息就需要占用系统资源(内存)
如果有一天,连接断开了,此时之前保存了连接的信息也就没意义了,对应的空间也就可以释放了。
滑动窗口
滑动窗口存在的意义就是在保证可靠性的前提下,尽量提高传输效率。
滑动窗口,本质就是在“批量的发送数据”。一次发一波数据,然后一起等一波ACK。
如果一次批量发送数据为N,统一等一波,此时这里N就称为“窗口大小”。
“滑动”的意思是,并不是把N组数据的ACK都等到了,才继续往下发送,而是收到一个ACK,就继续往下发送一组。
当前这个窗口大小越大,可以认为传输的速度越快。
当窗口大了,同一份时间内等待的ACK就多了,总的等待ACK的时间就少了。
但是当前有个核心问题,如果丢包了,怎么办
丢包分成两种
1.ACK丢了
ACK类似于闯关,后面的解锁成功,意味着前面的也解锁成功。
2.数据丢了
流量控制
滑动窗口的延伸,目的是保证可靠性。
在滑动窗口中,窗口越大,传输效率越高。但是也不能一味的考虑发送方,也要考虑接收方。
发送方发的超快,接收方根本处理不过来,就会把新收到的包给丢了,发送方还得重传。
流量控制的关键,就是得能够衡量接收方的处理速度,此处就直接用接收方接收缓冲区的剩余空间大小,来衡量当前的处理能力。
接收方如何告知发送方,剩余空间的大小?
通过ACK报文告知
拥塞控制
也是滑动窗口的延伸,也是限制滑动窗口发送的速率。
拥塞控制衡量的是,发送方到接收方,这整个链路之间,拥堵情况(处理能力)
延时答应
相当于流量控制的延伸。流量控制是使发送方,发的不要太快,延时应答,就想在这个基础上,能够尽量的在让窗口更大一些。
捎带应答
是延时应答的延伸。
客户端和服务器之间的通信,有以下几种模型:
1.一问一答:客户端发一个请求,服务器返回一个对应的响应
2.多问一答 : 上传文件
3.一问多答:下载文件
4:多问多答
面向字节流
粘包问题(不仅仅是TCP存在粘包,其他的面向字节流的机制,也存在)
粘包粘的是应用层数据报,在TCP接收缓冲区中,若干个应用层数据包混在一起了,分不出来谁是谁。
TCP异常处理
1.进程终止
在进程毫无防备的情况下,突然结束进程,这个时候该进程的TCP连接是怎样的?
TCP连接是通过socket来建立的,socket本质上是进程打开的一个文件,文件其实就存在于进程的PCB里面有个文件描述符表,每次打开一个文件(包括socket),都在文件描述符表里增加一项,每次关闭一个文件,都在文件描述符表里,进行删除一项。
如果直接结束进程,PCB也就没有了,里面的文件描述符表也就没有了,此处的文件相当于“自动关闭”了。
这个过程其实和手动调用socket.close()一样,都会触发四次挥手。
2.机器关机
按照操作系统约定的正常流程关机。
正常关机流程,会让操作系统杀死所有进程,然后在关机。
3.机器掉电/ 网线断开
操作系统不会任何的反应时间,更不会有任何的出库措施。
TCP vs UDP
1.什么时候使用TCP?
对可靠性有一定要求。
什么时候使用UDP?
对可靠性要求不高,对于效率要求更高。
基于UDP如何实现可靠传输?
本质上就是在应用层基于UDP复刻TCP的机制
网络层
IP协议
完成两方面的工作:地址管理、路由选择。
协议头格式如下:
(1)4位版本:IP协议的版本号,当前只有两个取值,4和6
(2)4位首部长度:IP报头和TCP报头类似,都是可变的,因为带有选项
(3)8位服务类型:说是8位,其实只有4位是有效的。
4位TOS分别是:最小延时、最大吞吐量、最高可靠性、最小成本。这4个是相互冲突的,同一时刻只能实现一个。
这里的TOS相当于切换形态。
IP协议能够规划处一条合适的路线。这里的“合适”就体现在最小延时、最大吞吐量、最高可靠性、最小成本。
(4)16位总长度
UDP中也有类似的情况,16位=64k,因此单个IP数据报最大长度不能超过64k,如果要构造一个更长的数据报(比如搭载的载荷部分已经超过了64k,这时怎么办)
IP协议自身实现了分包和组包这样的操作
(5)16位标识 、3位标志、13位片偏移这三个字段是用来进行分包组包的。
(6)8位生存空间
表示一个IP数据报,在网络上还能生存多久,这里的单位不是s或者ms,而是转发次数。
IP数据报被转发的的时候,会有一个初始的TTL,IP数据报每次经过了一个路由器,TTL就会-1,如果TTL减到0了,此时收到这个包的路由器就会把这个包丢弃。
主要就是有些包里面的IP地址,可能是永远也到不了的,像这样的包,不可能在网络上无休止的转发(占用的硬件资源太多了),正常的IP数据报都会在既定的时间内到达。
(7)8位协议
传输层用的哪种协议
TCP或者UDP都有不同的取值。
(8)32位源IP地址(发件人的地址)、32位目的IP地址(收件人的地址)
对于IPv4来说,一个IP地址本质是32位的整数,通常会用“点分十进制”这样的方式来表示这个IP地址。三个点,把32位整数分成4个部分,每个部分一个字节,每个部分的取值就是0-255.
地址管理
IP地址是一个点分十进制构成的数据。
把IP地址分成了两个部分,网络号+主机号
网络号:描述当前的网段信息(局域网的标识)
主机号:区分了局域网内部的主机
例如:192.168.0.5
192.168.0是网络号, 5是主机号
要求,同一个局域网里,主机之间的网络号是相同的,主机号不能相同。
两个相邻的局域网(同一个路由器连接的),网络号也是不同的。
那么,到底前多少个比特位是网络号,是怎样规定的?
引入“子网掩码”这样的概念,来表示多少个比特位是网络号。
子网掩码,也是一个32位,点分十进制表示的整数,子网掩码的左侧都是1,右侧都是0,(不会1和0混着排列),左边的这些1就表示哪些是网络号,剩下0就表示哪些位是主机号。
例如:IPv4地址:192.168.0.5
子网掩码:255.255.255.0
255 =>1111 1111 1111 1111 1111 1111 0000 0000
IP地址中的前24个bit位就是网络号,后8个bit位就是主机号
一些特殊的IP地址
- 如果IP的主机号为全0,该IP就表示网络号
- 如果IP的主机号全为1,该IP就表示“广播地址”,往这个广播上发的消息,整个局域网都能收到
- IP地址是127开头的,该IP表示“环回IP”,表示主机自己。
127.0.0.1:环回IP的典型代表
- IP地址是10开头,192.168,开头,172.16-172.31开头,该IP地址是一个局域网内部的IP(内网),除此之外,剩下的IP称为外网(直接在广域网上使用的IP)
要求外网IP一定是唯一的,每个外网IP都会对应到唯一的一个设备,内网IP只是在当前的局域网中是唯一的,不同的局域网中,可以有相同的内网IP设备。
IP地址是可以重复的。
本来预期IP地址应该就表示一个网络上的唯一位置,为什么同一个IP就能表示不同的设备?
当前IPv4协议,使用的IP地址是32位整数,32位能表示的数据范围是42亿多,如果给每个设备都分配一个唯一的IP地址,意味着世界上的设备不能超过42亿,但是世界上的设备已经超过这个数字了,让每个设备有唯一的IP地址,已经不现实了。
解决方案:
(1)动态分配。让每个设备连上网的时候,才有IP,不联网的时候就没IP(这个IP就可以给别人用)。
但是这个方案不能从根本上解决问题(设备没有减少,IP地址也没有增加)
(2)NAT机制。让多个设备共用一个IP(外网IP).
把网络分成内网(局域网)和外网(广域网),要求外网IP必须表示唯一的设备,同时内网中的若干个设备,可以共用同一个外网IP。这个时候每个外网IP都可能表示着几千个甚至上万个设备,这时候IP地址就缓解很多。
对于NAT机制,把IP分成了内网和外网,
对于一个外网IP,可以在互联网的任意位置访问,对于一个内网IP,只能在当前局域网内部访问,局域网1的设备,不能使用内网IP访问局域网2的设备。
(3)IPv6
IPv6在报头中使用了一个更长的字段来表示IP地址。16个字节,128位。
真正从根本上解决了IIP地址不够用的问题。
既然IPv6可以从根本上解决,那为什么现在还是使用IPv4和NAT?
最大的问题就是IPv4和IPv6是不兼容的。
路由选择
路由选择,也就是路径规划。两个设备之间,要找出一条通道,能够完成传输过程。
要想找出通道,前提是得先认识路。
IP数据报中的目的地址,就表示了这个包要发到哪里去。这个目的地址如果当前路由器认识路,就直接告诉了,如果当前路由器不认识,就会告诉一个大概的方向,走到下一个路由器的时候来问问。依次往后走,也是离目标越来越近。这个时候总会遇到一个认识这个地址的路由器,于是就可以具体的转发过去了。有的时候,不光遇到了一个认识这个地址的路由器,并且它还认识多个路,就可以选一个更合适的了。
什么是路由器“认识”这个IP地址?
在路由器的内部维护了一个数据结构,叫做路由表。路由表里面记录了一些网段信息(网络号),(目的IP就在这些网段中匹配)以及每个网络号对应的网络接口(网络接口其实就对应到路由器里面的具体的端口)
数据链路层
数据链路层的主要协议,叫做“以太网”。
以太网数据帧的格式如下:
特殊协议的补充
- DNS:应用层协议
域名解析
IP地址不好记忆,即使写成点分十进制,扔人对于人类来说不好记忆,就可以使用一串英文单词,来表示这个IP地址。这串英文单词,就称为“域名”。
www.sogou.com 就是域名
域名和ip地址之间,是一个一一对应的关系。
DNS系统,最开始的时候,只是一个普通的文件,称为hosts文件。
140.82.114.3 github.com 这一行,就描述了IP和域名之间的关系。
后序在其他程序(浏览器)中使用这也域名,就会被自动转换成IP地址。
但是现在全世界的域名那么多,总不能天天在这个hosts改来改去,于是,就成立了一个机构,负责维护这里的域名和IP的应对关系。比如,要申请一个域名,就去这个机构报备即可。这个机构维护一组服务器,把hosts文件存在这个服务器中,如果自己的电脑想要进行域名解析,就访问这个服务器就可以了。但是全世界的国家,地区有各自的网络运营商,这些运营商就会就近架设域名解析镜像服务器,我们平时上网的时候,一般就是就近访问镜像服务器。
当主机查询了一次DNS之后,主机就会把这个查询结果缓存一定的时间(浏览器来进行缓存),下次再访问到同一个域名的时候,就可以省略查询DNS的过程了。(DNS域名和IP的对应关系,是很少变化的)。