网络原理UDP和TCP/IP
应用层和传输层
应用层和传输层相对于网络层和数据链路层与日常开发联系更加紧密,并且在应用层层中需要确定一些协议
例如:一个外卖网站
1.需要根据需求,确定交互信息
a请求:获取用户位置,b响应:显示其附近的一些外卖
2.组织好数据格式,来确定请求和响应
这里组织数据格式的方式有
1.xml采用成对的标签,来表示数据
xml
<request>
<userId> 1234</userId>
<name> zhangsan</name>
</request>
优点:可读性和可扩展性都比较好
缺点:传输效率不高,主要是因为带宽,并且带宽是一个服务器最贵的硬件资源
但是这个xml现在不是特别常用了
2.json
请求
java
{
userId:1234
name:zhangsan
}
3.google protobuf
这里再存放的时候会采用二进制格式进行存放
优点:因为使用二进制,压缩率较高,这样就节省带宽
缺点:二进制存储可读性很差,肉眼无法阅读
端口号
端口号可以用来标识一个主机中通信中不同应用程序

TCP/IP协议中,用源IP、源端口号、目标IP、目标端口号和协议号通过五元组来标识一个通信
0 ~ 1023:这些端口号是知名端口号,被占用了
1024 ~ 65535:操作系统自动分配的端口号,端口号在这个区间中
例如:
ftp服务器,使用21端口
ssh服务器,使用22端口
SMTP服务器,使用25端口
http服务器,使用80端口
http服务器,使用443端口
..................
问题:
1.一个进程中是否可以bind多个端口号?
可以
因为服务器绑定一个,客户端绑定一个,其实本质上是一个socket对象绑定一个端口号,但是一个进程中可以有多个socket对象,因此其可以有多个端口号
并且服务器也会提供不同端口面向不同用户
业务端口,管理端口,调试端口等等
2.一个端口号是否可以被多个进程bind
不可以
一个进程绑定了一个端口号,另一个进程也绑定这个端口号就会出现异常
UDP协议
无连接、不可以靠传输、面向数据报、全双工
无连接 :知道对方的IP和端口号就直接传输,不建立连接
不可靠传输 :没有一些机制,如果没有传输过去其也不会返回任何错误信息
面向数据报:并且不可以灵活的控制读写次数和数量


但是有个问题,因为这个空间存储有限,但是有些问题可能会超过其范围
解决方案
1.应用层拆包组包
就是原本一次返回10条广告
这里拆分成两个UDP操作,各自返回5条
2.换成TCP因为这个是不限制大小的

相关协议
NFS:网络文件系统
TFTP:简单文件传输协议
DHCP:动态主机配置协议
BOOTP:启动协议
DNS:域名解析协议
TCP协议
TCP全称 " Transmission Control Protocol"传输控制协议


URG:紧急指针是否有效,就是**"插队"让后面数据先读取**
ACK:确认好是否有效
PSH:催促标志位,提示接收端将TCP缓冲区数据读走,尽快被读取走
RST:复位报文段,就是对方重新建立连接
SYN:同步报文段,请求建立连接
FIN:结束报文段,本端要关闭了,通知对方
16位校验和 :此处发送端填充,CRC校验,检验有TCP首部和数据部分
TCP特点 :有连接、可靠传输、面向字节流、全双工
此时虽然是可靠传输,但是其并不能保证100%成功,因此这里就需要一些机制进行控制,让其传输变得可靠
确认应答
有时候我们发送的数据,可能没有按照发送的顺序发送出去,这里有了确认应答就可以让这个顺序得到一定保障


这里女神发的信息可能会因为一些意外导致其发送顺序发生了改变,这样导致了信息错误,这里还好,要是像银行这种信息转账错误,问题是非常大的,因此需要确认应答这样的机制
可以通过对其发送的数据进行编号,即序列号,这样每一个ACK都有序列号,这样发送的时候可以根据其序列号进行发送数据

获取最后一个序号,让其确认序号为 最后一个序号 + 1即可
这里1001有两种含义
1.1001之前的数据已经收到了
2.对方开始要1001后的数据了

这个确认应答,只需要将ACK这个标志位设置成1即可

但是数据中有很多的字节编号,那我们如何区分呢,因此其这个编号是从某个数字开始递增的
超时重传

主机A发送数据后,没有收到主机B的ACK确认应答,可能是数据丢失了 ,当超过一定时间没有应答的时候,其就会进行将其数据进行重传
但是其可能是主机B返回确认应答ACK时候发生丢包

这样就会出现主机B收到了主机A的相同多分数据
此时TCP协议就能够将这些重复的数据将其丢弃
TCP中的Socket中有一个"内存缓冲区",这里面会根据其重复的序列号进行去重
重传的次数肯定是不可以无限的,并且频率要根据不同情况不同分析
因此TCP会动态计算最大超时时间
随着重复次数的增加,其等待时间就会增加
第一次重传:超时时间 t1
第二次重传:超时时间 t2
t2 > t1
当重传次数过多 ,可能是其他地方出现问题,为了减少资源浪费,因此这里就会强制关闭连接
确认应答和超时传输是TCP可靠传输的最核心机制
连接管理
建立连接和断开连接
建立连接 (三次握手)
通信中的"握手",发送一个 "不包含业务数据"的数据包,也就是没有载荷
synchoronize 同步,这里的同步是"通知 "的意思
此时ack确认应答和syn同步报文 的标志位都要设置为1,也就是启动

上面服务器ack和syn可以一起发送给客户端,这样可以合并进行发送,这样可以提高效率
"三次握手"的意义
1.可以起到"投石问路"的效果,先初步验证这个通信链路是否畅通
就像第一班地铁是跑空车,这样可以看看起路是否畅通,如果不畅通就要采取一些措施
2.验证通信双方的发送能力和接收能力是否正常
3.还会进行一些必要的"协商工作"
问题
TCP建立连接时候,32位序号,并不是从1开始,并且每次连接起始序列号都不一样,并且可能差别较大,那如果这次连接有一个包丢了,还没有重连就断开连接了,等这个包到达的时候,此时新的连接已经建立,那会区分这个包吗?会的,会根据序列号进行区分,不同连接的序列号差别较大

断开连接 (四次挥手)
FIN (finish)
这里可以是客户端主动断开连接,也可以是服务器断开连接

ACK返回是由内核控制,收到FIN之后就会立即触发
而FIN返回时机,是根据起应用程序当 socket.close()方法才会触发这个FIN
因此这里返回时间间隔是由其中间代码逻辑决定
因此这里不一定能合并,如果时间间隔较少,ACK 和 FIN可能合并
而如果时间间隔较多,就不会合并
TCP状态
LISTENING :服务器特有的状态,就是服务器关联好了端口号,等待客户端连接

启动TCP服务器,但没有客户端连接

ESTABLISHED :客户端和服务器都有,当他们建立起连接,状态就会是这个

CLOSE_WAIT :断开连接,需要进行挥手,接收FIN的一方就是这个状态,直到这一方发出FIN,其实就是等待close方法调用,这个方法调用起才会发送FIN给对方,因此如果有些东西长时间处于这种状态,就可能使有一些Socket对象等内存空间没有释放

TIME_WAIT :主动断开连接的一方的状态,主要使处理"最后一个ACK丢包"的情况,此时对方没有收到ACK就会从新发送FIN ,因此需要等一段时间看看对方有没有重传FIN,会等一定时间,等待2MSL ,这里的MSL是一个常量,从一端到另一端最大理论时间 ,默认为1min
超出这个时间服务器就会主动断开


滑动窗口
由于每发送一次信息,由于确认应答就会返回一个ACK,这样收到ACK就说明这一段的数据已经收到了,此时就会进行下一段数据的传输,又会重复的操作,这样效率并不是很高

因此我们能不能批量的进行数据传输呢
是可以的

窗口大小 :无需等待应答可以发送数据的最大值,上面窗口最大4000个字节,因此发送前4个时候并不需要等待ACK
当第一个字段收到ACK之后,这个滑动窗口可以向后移动,这样就可以将第五个段的数据发送
操作系统需要一个缓冲区维护这个滑动窗口 ,需要记录那些没有应答,那些已经应答就需要从缓冲区中删除

这里窗口大小越大,批量传输的数据越多,传输效率更高,但是这里批量传输的滑动窗口大小不可以无限大
如果出现丢包情况如何处理呢?
1.ACK丢失

2.数据包丢失

这里可以称为 "快速重传"
流量控制
上面我们使用滑动窗口可以提高效率,虽然滑动窗口越大,传输效率越高,但是窗口大小也不可以特别大 ,并且如果传输过快可能会导致接收方接收不过来,这样容易丢包
因此这里流量控制就是对窗口大小的控制
接收方通过ACK将接收缓冲区剩余空间大小发送给发送方

如果接收端已经满了,此时就会暂停发送方发送,此时接收端的缓冲区空间为0
此时暂停了发送端发送,后面滑动窗口有空间了,此时后面如何通知给对方可以了呢?
当窗口满了,过了一段时间,发送端就会发送一个窗口探测包,接收端会发送窗口更新通知(为了保持正常通信)
窗口探测包 :只是为了触发ACK,为了得到滑动窗口大小的值

拥塞控制
虽然说有滑动窗口和流量控制,能高效可靠传输数据 ,但是如果在刚开始阶段就发送大量的数据,仍然可能有问题,此时可能网络比较堵塞
流量控制依据的是接收方的处理能力
拥塞控制依据通信路径的处理能力
刚开始以一个较慢的速度(较小的窗口)进行数据发送,来看其接收能力
如果正常进行(没有丢包),就会加快速度
如果出现异常(出现丢包),就会减慢速度
因此这里的速度(窗口大小)是动态变化的
这个窗口大小是由流量控制和阻塞控制的最小值决定


延迟应答
提高传输效率,就是在尽可能的情况下将滑动窗口的窗口变大
接收到数据并不是立即返回ACK,可以让其稍微拖延一点,在这个拖延过程中可能消耗了一部分数据 ,此时剩余空间就变大,但是其是相对的,其剩余容量可能变大、不变或减小
但是并不是所有包都进行延迟应答
数量限制 :每隔N个包就应答一次(N一般为2)
时间限制 :超出最大延迟就会应答一次(超时时间一般取200ms)

此时这里就是延迟应答,1001这个ACK就没有返回,只返回了2001这个ACK
此时就省略到了1001这个ACK
捎带应答
配合延迟应答使用
在返回ACK的时候顺便把一些响应也返回

面向字节流
TCP的创建一个socket,此时内核中也会有发送缓冲区 和接收缓冲区
使用writer进行数据写入,其会先写入到缓冲区中,等到一定长度其才会将数据发送出去,如果较长就会被拆分
使用read读也是同样的
因为缓冲区的存在,TCP中读和写是可以不一一匹配
粘包问题
因为面向字节流所以就会出现粘包问题

出现粘包问题主要是因为其缓冲区的问题,导致包与包之间的边界不明显
方案一 :使用特殊的分割符作为包的开头 / 结尾
方案二:数据开头加上一个固定属性像数据包的长度 [3] aaa [4]bbbb
异常情况
1.进程中止 :虽然会释放操作系统中PCB的文件描述符表,但是仍然可以发送FIN和正常关闭没有区别

2.主机关机 :和进程中止类似
这里会先结束所有应用程序相当于进程中止,关机是需要时间的,因此四次挥手可能执行完了,但是如果挥手挥的慢,也可能会出现没有执行完的情况
3.机器掉电 / 网线断开 :电源直接没电了,这样的情况是来不及挥手的
发送方发现接收方不在了最后就会发送一个"复位报文 "rst
接收方发现发送方不在了,就会发送"心跳包 "判断对方是否还在


上面就是TCP的一些实现可靠传输和传输速率的一些机制,如果UDP也想要实现这种可靠传输,此时就可以参考TCP
TCP适合可靠传输的情况
UDP适合一些对高速传输要求高,并且丢包率较低的情况
网络层
IP协议

主机:有IP地址,但不能进行路由控制
路由器:有IP地址,也可以对路由进行控制
节点:主机和路由器的统称

4位版本(version) :IP的版本,主流就只有IPv4和IPv6,像这里是IPv4,其版本长度就是4
4位首部长度(hander length) :头部长度是多少个32bit,也就是有4个字节,4bit表示的是0 ~ 15,最大是15*4 = 60也就是60个字节,和TCP类似
8位服务类型(Type Of Service) :3位优先权字段(已经弃用),1位保留字段(必须设置成0),4位TOS才是重要的分别表示:最小延迟、最大吞吐量、最高可靠性、最小成本 ,四个相互冲突,只可以选一个
16位总⻓度(total length) :最大长度64KB,如果需要携带较大的载荷,会自动触发拆包和组包
16位标识 :唯一的标识主机发送的报文,如果IP报文在数据链路层被分片了,那么每一片里面id相同
3位禁止段 :第一位分片 保留,第二位描述这次传输是否分片 ,为1就是禁止分片,禁止分片如果长度超过了MTU,就会丢弃报文,第三位表示"更多分片" ,如果有分片,其最后一个分片位置设置成1,其他设置成0,类似结束标记
13位分⽚偏移(framegament offset) :是分片相当于原本IP报文开始处的偏移量,除了最后一个报文,其他报文都是8的整数倍,为了保证报文连续
8位生存时间(Time To Live,TTL) : 这里并不是时间,而是次数,也就是最大报文跳数,32 / 64 / 128,通常是64,每经过一个路由,其TTL都会进行-1,直到减到0为止,主要是为了防止发生过呢无限传输(死循环 )
8位协议 :表示上层协议类型,这里也就是传输层使用什么协议对其载荷进行解析
16位头部校验和 :使用CRC进行校验,只校验头部是否损坏,载荷部分UDP和TCP里面也有校验和
32位源地址和32位目标地址:表示发送端和接收端


地址管理
IP地址的数量限制
IPv4地址是32位整数组成,分为4个部分,中间通过3个 . 进行分割(也叫点分十进制 ),每个不部分取值0 ~ 255,例如:192.160.90.10这个IPv4地址
IPv4地址范围是 0 ~ 42亿9千万 ,原本的思想是每一台设备都有自己唯一的IP地址,但是后面发现不够用了
如何解决这种问题呢?
1.动态分配IP地址
一个设备需要上网再进行分配,不需要上网就不需要分配,此时就可以节省一些IP地址,但是这并不能从本质解决问题,效果比较有限
2.NAT网络地址切换
将IP分为两大类
1.外网IP / 公网IP
除了内网IP,剩下的都是外网IP
2.内网 / 私网IP
10.* 前8位是网络号
172.16 - 172.31.,前12位是网络号
192.168. (现在常见的)前16位表示网络号
3.使用IPv6
IPv4是4个字节,而IPv6是16个字节可以表示 2 ^32 * 2 ^32 * 2 ^32 *2 ^32 这么多IP地址肯定够用,并且网速也比较快,需要更换硬件设备
但是其IPv6和IPv4并不兼容,目前还没有普及,但是在我国还是大力支持IPv6的
NAT技术
在不同局域网内,其内网IP是可以重复的,因为大部分机器都是在局域网内,这样就可以使IP地址的利用率变高
这样就引入了其他问题,如果垮局域网访问呢?
1.同一个局域网内两个设备进行访问
此时和NAT没有关系,因为同一个局域网内其IP地址是不同的
2.两个带有外网IP的设备进行访问
也是和NAT没有关系,因为其外网IP是唯一的
3.不同局域网内的设备进行访问
不允许(虽然在网路层不行),但是可以通过应用层实现
4.外网IP的设备,访问内网中IP的设备
不允许,因为不同局域网内可能有重复的IP
5.内网IP设备访问外网IP的设备
会触发"网络地址转换",NAT设备对源IP进行修改成自己的IP
NAT中维护了一个表,包含映射关系
替换前IP 替换前端口 替换后IP 替换后端口



NAT优缺点
优点 :不需要更新设备,只需要更新软件即可来解决IP地址不够用的问题,更多设备可以公用一个外网IP
缺点:无法从NAT外部向内部建立连接,依赖转换表,这个表生成和销毁需要额外开销,NAT出现异常,会使所有TCP连接全部断开
网段划分
IP有两部分,网络号和主机号
网络号 :两个相邻的局域网,其网络号是他们的标识,网络号必须不同
主机号 :同一个局域网内,有相同网络号,但是主机号必须不同

通过合理设置主机号和网络号就可以让相互作用的网络中,使每台主机的IP地址都不相同
划分网络号和主机号的方式
方式一:根据业务需求自己可以手动配置其网络号和主机号对应数量
命令行输入:ipconfig

当然如果想有更多的主机号,就可以自己设置这里的子网掩码
方式二:(古老)
分为ABCDFE五类IP地址

A类 0.0.0.0到127.255.255.255
B类128.0.0.0到191.255.255.255
C类192.0.0.0到223.255.255.255
D类224.0.0.0到239.255.255.255
E类240.0.0.0到247.255.255.255
随着发展,大多数都使用B类,这样就导致B类很快分配完了,但是其A没有人用,这样就导致了浪费大量地址
特殊的IP
将IP地址中主机地址全部设置成0,就成了网络号,代表这个局域网
将IP地址中的主机全部设为1,成为了广播地址,用于给同一链路中的所有主机发送数据包
127.*表示IP地址用于本机环回(loop back)测试,通常是127.0.0.1,有时候可以用localhost代替,但是有些情况127.0.0.1可以,但是localhost不可以
路径选择
IP数据包传输过程和问路一样
IP路由选择其并不知到其全局,因为整体的网络比较复杂
虽然每一个路由器感知能力较弱,无法感知整个网络环境,但是其知道一部分(他及其他周围设备的情况)
因此这里IP协议可以走一步看一步,是"渐进式 "的过程,每走到一个路由器其都会给出一个 "较优解 "
1.如果路由器知道目标在哪,就会给出具体路线
2.如果路由器不知道目标在哪,就会给出一个方向
如果都没有,其就会走一个 "默认路径 ",下一跳的表项通常是 "上一级"的路由器

数据链路层
以太网
从微观角度关心两个节点之间具体怎样传输
数据链路层 包含数据链路层和物理层的内容
以太网 是目前应用最广泛的局域网技术
wifi 数据链路层的另一种协议
5G另外一套协议体系,和TCP / IP独立,但是上层接入了TCP / IP
以太网帧

源地址和目标地址:网卡的硬件地址(MAC地址),长度是48位
帧协议类型字段,分别 IP、ARP 、RARP
帧末尾是CRC校验和


MAC地址描述路途中每一个区间的起点和终点
MTU
以太网帧数据长度规定最小46字节,最大1500字节,ARP数据包不够46字节,后面要补充
如果数据包从以太网中长度大于了最大长度,其就会拆包组包
不同的数据链路层标准MTU是不同的

重要应用层协议DNS
Domain Name System是一套从域名映射到IP的系统
域名:通过一段字符串表示一个IP地址

最早期DNS是一个.hosts文件

DNS是如何承担较高的并发量的呢
1.本机DNS解析完成后,做缓存 ,域名对应IP会放入硬盘,短时间内访问这个域名,上面因为进行了对应IP缓存,其就不会进行DNS解析,缓解了DNS压力
2.DNS是分布式系统 ,会有很多的DNS镜像服务器,通过镜像服务器就可以访问根域名服务器