layout: post
title: 八股总结(二)计算机网络与网络编程
description: 八股总结(二)计算机网络与网络编程
tag: 八股总结
OSI七层模型是法律上规定的国际标准,但TCP/IP体系占据了市场,是实际上的国际标准,为了方便理解,由将TCP/IP四层的结构中的网络接口层分为了物理层和数据链路层。
比特流
,数据链路层为帧
,网络层为包
,传输层为数据段
。网络协议栈数据报文发送与接收过程中的变化:
通俗来讲,数据链路层完成的是从目的主机所在路由器(目的MAC地址)到本地路由器(源MAC地址)的连接。
ARP地址解析协议只能在单个数据链路段中使用,用于将IP地址解析为对应的MAC地址。
通俗来讲,网络层完成目的主机和本地主机之间的连接。
网络层最常用的就是IP协议(Internet protocol),将传输层的报文作为数据部分再加上IP包头组装成IP报文,如果IP报文超过MTU(以太网中一般为1500字节)就会再次进行分片,得到一个即将发送到网络的IP报文。
IP 地址分成两种意义:
⼀个是⽹络号,负责标识该 IP 地址是属于哪个⼦⽹的;
⼀个是主机号,负责标识同⼀⼦⽹下的不同主机。
为了有效转发IP数据报和提高交付成功的机会,网际层使用了网际控制报文协议ICMP,使得主机和路由器可以使用ICMP来发送差错报告报文 和 询问报文,ICMP报文被封装在IP数据报中发送。
ICMP差错报文分为以下5种:
网络层已经解决了主机与主机之间的通信,而传输层要解决的是两个主机各自进程间进行通信的问题。
TCP是面向连接的、可靠的、基于字节流的传输层通信协议。
一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和粘包问题。
原因
1、应用程序写入数据的字节大小大于套接字发送缓冲区的大小.
2、进行MSS大小的TCP分段。( MSS=TCP报文段长度-TCP首部长度)
3、以太网的payload大于MTU进行IP分片。( MTU指:一种通信协议的某一层上面所能通过的最大数据包大小。)
解决方案:
1、增加消息长度字段。
2、在包尾部增加回车或者空格符等特殊字符进行分割
3、将消息分为消息头和消息尾
4、使用其它复杂的协议,如RTMP协议等。
由于TCP是面向连接的,能够保证数据的可靠性交付,因此经常被用于FTP文件传输,HTTP、HTTPS
由于UDP是面向无连接的,它可以随时发送数据,加上UDP本身的处理既简单又高效,因此经常用于包总量较少的通信,如DNS、SNMP等,视频,音频等多媒体通信,广播通信。
原因是 TCP 有可变⻓的「选项」字段,⽽ UDP 头部⻓度则是不会变化的,⽆需多⼀个字段去记录 UDP 的⾸部⻓度。
端口号:TCP是进程与进程间的通信,因此,头部两端是16位的源端口号和16位的目的端口号
序列号(SEQ):在建立连接时由计算机生成的随机数作为其初始值,通过SYN包传递给接收端主机,每发送一次数据,就累加一次该数据字节数的大小,用来解决网络包乱序问题
。
确认应答号(ACK):指下一次期望收到数据的序列号,发送端收到这个应答以后可以认为在这个序号以前的数据都已经被正常接收,用来解决不丢包的问题
。
控制位:ACK:该位为 1 时,「确认应答」的字段变为有效,TCP 规定除了最初建⽴连接时的 SYN 包之外该位必须设置为 1。
RST:该位为 1 时,表示 TCP 连接中出现异常必须强制断开连接。
SYN:该位为 1 时,表示希望建⽴连接,并在其「序列号」的字段进⾏序列号初始值的设定。
FIN:该位为 1 时,表示今后不会再有数据发送,希望断开连接。当通信结束希望断开连接时,通信双⽅的主机之间就可以相互交换 FIN 位为 1 的 TCP 段。
窗口大小:用于拥塞控制
校验和:差错控制
紧急指针:仅当前面的 URG 控制位为 1 时才有意义。它指出本数据段中为紧急数据的字节数,占 16 位。当所有紧急数据处理完后,TCP 就会告诉应用程序恢复到正常操作。即使当前窗口大小为 0,也是可以发送紧急数据的,因为紧急数据无须缓存。
选项(Option):长度不定,但长度必须是 32bits 的整数倍。
数据
客户端连续发送多次SYN建立连接的报文,在网络拥堵情况下,一次旧的SYN报文比新的SYN报文到达了服务端,那么此时服务端就会返回一个SYN + ACK报文给客户端,客户端收到后可以根据自身的上下文,判断这是一个历史连接(序列化过期或者超时),那么客户端就会发送RST报文给服务端,表示终止这一次连接。
如果是两次握手,就不足以判断当前连接是否为历史连接。
我们都知道 TCP 连接建⽴是需要三次握⼿,假设攻击者短时间伪造不同 IP 地址的 SYN 报⽂,服务端每接收到⼀个 SYN 报⽂,就进⼊ SYN_RCVD 状态,但服务端发送出去的 ACK + SYN 报⽂,⽆法得到未知 IP 主机的 ACK 应答,久⽽久之就会占满服务端的 SYN 接收队列(未连接队列),使得服务器不能为正常⽤户服务。
MSL 是 Maximum Segment Lifetime,报⽂最⼤⽣存时间
TCP 针对数据包丢失的情况,会⽤重传机制解决。常见的重传机制如下:
RTO(Retransmission Timeout 超时重传时间)根据RTT(Round-Trip Time ,往返时延/)确定。
快速重传
快速重传的⼯作⽅式是当收到三个相同的 ACK 报⽂时,会在定时器过期之前,重传丢失的报⽂段。
快速重传机制只解决了⼀个问题,就是超时时间的问题,但是它依然⾯临着另外⼀个问题。就是重传的时候,是重
传之前的⼀个,还是重传所有的问题。
⽐如对于上⾯的例⼦,是重传 Seq2 呢?还是重传 Seq2、Seq3、Seq4、Seq5 呢?因为发送端并不清楚这连续的
三个 Ack 2 是谁传回来的。
SACK ( Selective Acknowledgment 选择性确认)重传
这种⽅式需要在 TCP 头部「选项」字段⾥加⼀个 SACK 的东⻄,它可以将缓存的地图发送给发送⽅,这样发送⽅就可以知道哪些数据收到了,哪些数据没收到,知道了这些信息,就可以只重传丢失的数据。
如下图,发送⽅收到了三次同样的 ACK 确认报⽂,于是就会触发快速重发机制,通过 SACK 信息发现只有200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进⾏重复。如果要⽀持 SACK ,必须双⽅都要⽀持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux2.4 后默认打开)。
4. Duplicate SACK ⼜称 D-SACK ,其主要使⽤了 SACK 来告诉「发送⽅」有哪些数据被重复接收了。
例子1:
例子2:
可见使用D-SACK的好处是:
在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启/关闭这个功能(Linux 2.4 后默认打开)。
#1 是已发送并收到 ACK确认的数据:1~31 字节
#2 是已发送但未收到 ACK确认的数据:32~45 字节
#3 是未发送但总⼤⼩在接收⽅处理范围内(接收⽅还有空间):46~51字节
#4 是未发送但总⼤⼩超过接收⽅处理范围(接收⽅没有空间):52字节以后
TCP 滑动窗⼝⽅案使⽤三个指针来跟踪在四个传输类别中的每⼀个类别中的字节。其中两个指针是绝对指针(指特
定的序列号),⼀个是相对指针(需要做偏移)。
接收部分如上图所示,其中三个接收部分,使⽤两个指针进⾏划分:
RCV.WND :表示接收窗⼝的⼤⼩,它会通告给发送⽅。
RCV.NXT :是⼀个指针,它指向期望从发送⽅发送来的下⼀个数据字节的序列号,也就是 #3 的第⼀个字节。
指向 #4 的第⼀个字节是个相对指针,它需要 RCV.NXT 指针加上 RCV.WND ⼤⼩的偏移ᰁ,就可以指向 #4 的
第⼀个字节了。
发送⽅不能⽆脑的发数据给接收⽅,要考虑接收⽅处理能⼒。
如果⼀直⽆脑的发数据给对⽅,但对⽅处理不过来,那么就会导致触发重传机制,从⽽导致⽹络流重的⽆端的浪费。
为了解决这种现象发⽣,TCP 提供⼀种机制可以让「发送⽅」根据「接收⽅」的实际接收能⼒控制发送的数据量,
这就是所谓的流量控制。
1、假如发送方报文丢失,那么接收方回传的ack会一直不包含丢失字段,则发送方会在重传等待时间结束后对丢失报文段进行重传。接收方利用接受窗口大小这个参考,控制发送方的发送速率。
2、假如接收方有了新的可缓存空间,并将消息发送给发送方的途中,报文丢失,那么发送方一直以为接收方没有空间接收,而接收方也并不知道自己的报文丢失。这样就陷入死锁状态。为了打破死锁,发送方每发送一次报文都会启动一个持续计时器,当持续计时器超时的时候,发送零窗口探测报文
。询问是否依旧是没有缓存空间。零窗口探测报文对于接收方没有空间要求,且也有超时重传机制。
前⾯的流量控制是避免「发送⽅」的数据填满「接收⽅」的缓存,但是并不知道⽹络的中发⽣了什么。
而拥塞控制是当⽹络发送拥塞时,TCP 会⾃我牺牲,降低
发送的数据量。控制的⽬的就是避免「发送⽅」的数据填满整个⽹络。
拥塞控制主要是4个算法:
慢开始:从1开始倍数增长。
拥塞避免:如果当拥塞窗⼝ cwnd 「超过」慢启动⻔限 ssthresh就进入拥塞避免算法。一般ssthresh的大小是65535。进入拥塞避免算法后,每次线性增加1。如果网络发送拥塞,有丢包现象发生,就会触发重传机制,将ssthresh 设为 cwnd/2,cwnd窗口值设置为1,并执行慢开始算法。
快重传:发送方一旦收到3个连续的重复确认,立即重传相应报文段。TCP认为在还能收到3个连续重复确认的情况下,说明大部分没丢,只是丢了一小部分。于是将cwnd拥塞窗口设置为原来的一半,将ssthresh设置为cwnd,然后进入快速恢复算法。
快恢复:拥塞窗口 cwnd = ssthresh + 3(3的意思是确认有3个数据包收到了),重传丢失的数据包,如果再收到重复的ACK,那么cwnd增加1.
HTTP 是⼀个在计算机世界⾥专⻔在「两点」之间「传输」⽂字、图⽚、⾳频、视频等「超⽂本」数据的「约定和
规范」
1xx 类状态码属于提示信息,是协议处理中的⼀种中间状态,实际⽤到的⽐较少
2xx 类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态
3xx 类状态码表示客户端请求的资源发送了变动,需要客户端⽤新的 URL新发送请求获取资源,也就是重定向。
4xx 类状态码表示客户端发送的报⽂有误,服务器⽆法处理,也就是错误码的含义。
5xx 类状态码表示客户端请求报⽂正确,但是服务器处理时内部发⽣了错误,属于服务器端的错误码。
Get (获取)⽅法的含义是请求从服务器获取资源,这个资源可以是静态的⽂本、⻚⾯、图⽚视频等。
POST(投递) ⽅法则是相反操作,它向 URI 指定的资源提交数据,数据就放在报⽂的 body ⾥。
HTTP由于是明文传输,安全上存在窃听、篡改和冒充的风险。
HTTPS在HTTP与TCP层之间加入了SSL/TSL协议,通过对信息的混合加密,防止窃听;采用校验机制,使用摘要算法,为数据生成独一无二的指纹,用于校验数据的完整性,防止数据篡改;将服务器公钥放入到数字证书中,解决身份冒充的风险。
前两步也就是SSL/TSL的建立过程,也就是握手阶段。
因为UDP快啊!UDP的DNS协议只要一个请求、一个应答就好了。
而使用基于TCP的DNS协议要三次握手、发送数据以及应答、四次挥手,但是UDP协议传输内容不能超过512字节。
不过客户端向DNS服务器查询域名,一般返回的内容都不超过512字节,用UDP传输即可。
需要注意的是:监听的 socket 和真正⽤来传送数据的 socket,是「两个」 socket,⼀个叫作监听 socket,⼀个叫作已完 成连接 socket。
成功连接建⽴之后,双⽅开始通过 read 和 write 函数来读写数据,就像往⼀个⽂件流⾥⾯写东⻄⼀样。
Linux内核中会维护两个队列: