1. 网络通信
中继器:信号放大器
集线器(hub):是中继器的一种形式,区别在于集线器能够提供多端口服务,多口中继器,每个数据包的发送都是以广播的形式进行的,容易阻塞网络。
网桥:局域网之间建立连接的桥梁,网桥是一种对帧进行转发的技术,根据MAC分区块,可隔离碰撞。网桥将网络的多个网段在数据链路层连接起来。
交换机(switch):工作在数据链路层,交换机与网桥的细微差别在于交换机常常用来连接独立的计算机,而网桥连接的目标是LAN,所以交换机的端口较网桥多。而且集线器是以广播形式发送数据包,交换机有一个智能化的功能,可以根据相应的地址发送数据包。
- 转发过滤: 当一个数据帧的目的地址在MAC地址中有映射时,它被转发到连接目的节点的端口而不是所有端口
- 学习功能:以太网交换机了解每一个端口相连设备的MAC地址,并将地址同相应的端口映射起来存放在交换机缓存中的MAC地址表中。
路由器:连接多个逻辑上分开的网络,能够判断网络地址和选择IP路径,内部存储路由表(配置路由“”),路由表可静态设置,亦可动态设置(根据RIP路由解析协议自动记录),每经过一次路由器,TTL值就会减1。
ping命令使用的是ICMP协议
ARP协议: 根据IP地址获取mac地址 (arp -a, 查看插卡的Mac地址)
RARP协议:根据mac地址获取IP地址
IP:标记逻辑上的地址
MAC:标记实际转发数据时的设备地址
netmask:和IP地址一起来确定网络号,
默认网关:发送的IP不在同一个网段内,那么会把这个数据转发给默认网关。Mac地址,在两个设备之间通信时变化(路由器),IP地址在整个通信过程中不会发生任何变化。
DNS服务器:域名解析服务器,根据域名解析IP地址
通信领域的单工、半双工、全双工
- 单工通信:传输数据只支持数据在一个方向上传输(收音机)
- 半双工:传输允许在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,实际上是一种切换方向的单工通信放心,如:对讲机,单行道
- 全双工:允许数据同时在两个方向上传输,同一时间,允许发送和接收数据。如:网卡,电话,手机,socket。软件开发领域实现TCP的全双工只能是通过多线程或者多进程来处理。
OSI模型
常用的以太网帧格式
- MAC帧主要有两种格式 一种是以太网V2标准,一种是IEEE802.3,常用是前者。参考:Mentalflow同志
- Ethernet II
- DMAC(Destination Mac) 目的MAC地址 DMAC字段的长度是6个字节,标识帧的接收者
- SMAC(Source Mac) 是源MAC地址,字段长度6个字节,标识发送者
- TYPE 用于标识数据字段中包含的高层协议,该字段长度为2个字节,类型字段为0x0800的帧代表IP协议帧,类型字段值为0x0806的帧标识ARP协议帧。
- Data 是网络层数据,最小长度必须是46字节以保证帧长至少为64字节,数据字段的最大长度是1500字节
- FCS 循环冗余校验字段 提供了一种错误检测机制,该字段长度为4个字节
- 802.3帧
- EEE802.3帧格式类似于Ethernet_II帧,只是Ethernet_II帧的Type域被802.3帧的Length域取代,并且占用了Data字段的8个字节作为LLC和SNAP字段。
- Length 字段定义了Data字段包含的字节数
- LLC 逻辑链路控制,由目的服务访问点DSAP、源服务访问点SSAP和Control字段组成
- SNAP (sub network Access Protocol)由机构代码(Org Code)和类型(TYPE)字段组成,Org code三个字节都为0。Type字段的含义与Ethernet_II帧中的Type字段相同。
- 两台电脑之间通信的前提是什么? 在同一网段
ping命令的过程:
以太网帧 TYPE: 0x806 代表ARP协议,先通过ARP协议在同网段中广播获取目的IP的MAC地址,
2 TCP/IP协议族详解之IP协议
2.1 IP协议的功能:
- 路由寻址
- 传递服务,有两个特点:不可靠,可靠性由上层协议提供,如TCP协议,无连接(IP并不维护任何关于后续数据报的状态信息。每个数据报的处理是相互独立的,这也就是说IP数据报可以不按发送顺序接收)
- 数据包分段(Segment)和重组
2.2 IP协议头部格式
可根据Wireshark抓包工具分析数据包含义 参考:TCP/IP协议族详解(二)
- 版本: IP协议的版本,目前版本号为4, 下一代IP协议的版本号为6
- 首部长度: IP报头的长度,占4位,固定部分的长度(20字节)和可变部分的长度之和,最大为60字节
- 服务类型:TOS, 目前暂没有人使用
- 报文总长度:报头的长度和数据部分之和
- 标识:是一个计数器,用来产生数据报的标识。唯一的标识主机发送的每一分数据报。通常每发送一个报文,它的值加一。当IP报文长度超过传输网络的MTU(最大传输单元)时必须分片,这个标识字段的值被复制到所有数据分片的标识字段中,使得这些分片在达到最终目的地时可以依照标识字段的内容重新组成原先的数据
- 标志位:共3位。R、DF、MF三位。目前只有后两位有效,DF位:为1表示不分片,为0表示分片。MF:为1表示“更多的片”,为0表示这是最后一片
- 片偏移:本分片在原先数据报文中相对首位的偏移位。(需要再乘以8)
- 报文生存时间:IP报文所允许的通过路由器的最大数量。没经过一个路由器,TTL减1,当为0时,路由器将该数据报丢弃,一般是64, 当发送ICMP回显应答时设置为最大值255
- 协议:指定IP报文所携带的数据使用的是什么协议。以便目的主机IP层知道要将数据上交到那个进程(不同的协议有专门不同的进程处理),和端口号类型,此处采用协议好。TCP的协议号为6,UDP的协议号为17, ICMP的协议号为1,IGMP的协议号为2.
- 首部校验和:计算IP头部的校验和,检查IP报头的完整性
应用程序使用TCP/IP协议传输数据时,数据要被送入协议栈经过逐层封装,最后作为比特流在媒体上传送,其过程示意图如下所示:
注:从上图可以看到以太网帧的数据长度是有大小限制的,这个最大值称为 MTU,所以当 IP 数据包长度大于 MTU 时会被拆成多个帧传输,称为 “IP分片”-------Mr.su Blog
3 TCP/IP协议族详解之ARP协议
ARP是为IP协议提供服务的,所以,把ARP划分到了网络层
3. 1 为什么有了IP地址还要使用Mac地址
- IP地址容易修改和变动,不能再网络上固定标识一个设备
- Mac地址一般在出场时被烧录到硬件中,不易修改,能在局域网中定位唯一一台设备
- 从拓扑结构和分层上分析,IP地址属于网络层,主要功能实在广域网范围内路由寻址,选择最佳路由,而Mac地址是网络接口层要形成适合于网络媒体上传输的数据帧。
请求包是广播,而应答包是单播
3.2 ARP混村表以及相关命令
ARP高速缓存表的作用:
为了减少网络上的通信量,主机 A 在发送其 ARP 请求分组时,就将自己的 IP 地址到硬件地址的映射写入 ARP 请求分组。当主机 B 收到 A 的 ARP 请求分组时,就将主机 A 的这一地址映射写入主机 B 自己的 ARP 高速缓存中。这对主机 B 以后向 A 发送数据报时就更方便了。
注意:arp缓存表分为静态和动态两种方式,默认情况下ARP缓存的超时时限是两分钟。
ARP命令:
- arp -d 清除本机arp 缓存表
- arp -a 查看本机当前arp表
- arp -s 绑定arp地址(机器重启后全部失效)
3 TCP/IP协议族详解之ICMP协议
存在的意义:由于IP协议无差错报告和差错纠正机制,缺少一种为主机和管理查询的机制。如, 当IP数据报在网络中超时了它的TTL, 那么路由器就会将这个数据报丢失,但是没有对这个丢弃操作返回错误报告。为了弥补这个缺点,所以产生了ICMP协议。注:ICMP没有纠正错误的机制
ICMP是网络层协议,报文首先封装成IP数据报,然后再传送给下一层,在IP数据报中的协议位,标识值为1(PRO:0x1f)
3.1 Ping命令使用详解
ping
命令是基于ICMP的查询报文,分为回送请求和回送应答,请求类型为8, 应答类型为0。
C:\Windows\System32>ping www.baidu.com
正在 Ping www.a.shifen.com [14.215.177.39] 具有 32 字节的数据:
来自 14.215.177.39 的回复: 字节=32 时间=7ms TTL=54
来自 14.215.177.39 的回复: 字节=32 时间=8ms TTL=54
来自 14.215.177.39 的回复: 字节=32 时间=8ms TTL=54
来自 14.215.177.39 的回复: 字节=32 时间=7ms TTL=54
14.215.177.39 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返行程的估计时间(以毫秒为单位):
最短 = 7ms,最长 = 8ms,平均 = 7ms
详细流程:
- 首先经过DNS服务器,将域名解析成IP地址,即www.a.shifen.com [14.215.177.39]
- 然后Ping命令发送一个带有32字节数数据的ICMP请求,收到回复后显示结果,也就是上面的
来自 14.215.177.39 的回复: 字节=32 时间=7ms TTL=54
, 其中字节表示测试数据长度(可以通过-l参数指定测试数据的大小,例如ping -l 1024 www.baidu.com
); 时间表示包的往返时间(一去一来所用的时间); TTL为54(请求包的TTL为64),也就是回复的包进过了10个路由器
3.2 tracert命令详解
Linux下的命令是traceroute, windows下的命令是tracert。
tracert是路由跟踪程序,用于确定 IP 数据报访问目标所经过的路径。
tracert 命令用 IP 生存时间 (TTL) 字段和 ICMP 错误消息来确定从一个主机到网络上其他主机的路由。 在工作环境中有多条链路出口时,可以通过该命令查询数据是经过的哪一条链路出口。
tracert一般用来检测故障的位置,我们可以使用用tracert IP命令确定数据包在网络上的停止位置,来判断在哪个环节上出了问题,虽然还是没有确定是什么问题,但它已经告诉了我们问题所在的地方,方便检测网络中存在的问题。
常用相关命令:
tracert -d www.baidu.com // 不讲地址解析成主机名,能够更快显示路由器路径
tracert -h 5 www.baidu.com // 指定跟踪的跃点数
tracert -w 10 www.baidu.com //指定等待每个应答的时间(以毫秒为单位)。默认值为 3000 毫秒(3 秒)
4. UDP 用户数据报协议
4.1 UDP用户数据报协议
无连接的简单的面向数据报的运输层协议。
-
特点: UDP数据报文中包括目的端口号和源端口号信息,由于通讯不需要连接,所以可以实现广播发送。UDP传输数据时有大小限制,每个被传输的数据报必须限定在64KB之内。不可靠传输协议,发送方所发送的数据报并不一定以相同的次序到达接收方。传输速度快。
-
适用场景:UDP一般用于多点通信和实时数据的业务,注重速度流畅
- 语音广播
- 视频会议系统
- TFTP SNMP RIP(路由信息协议,如报告股票市场,航空信息)
- DNS(域名解释)
4.2 创建UDP网络程序流程:
- 1.创建客户端套接字
- 2.发送/接受数据
- 3.关闭套接字
通信流程:
5 TCP/IP协议族详解之TCP协议
面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。
- 特性:
- 面向连接,通信双方必须先建立连接,双方都必须为该连接分配一定的内核资源,以管理连接的状态和连接上的传输。
- 可靠传输:
- TCP采用发送应答机制
- 超时重传:发送端发出一个报文段之后就会启动定时器,在定时时间内没收到应答就重发这个报文段,为了保证不发生丢包,就给每一个包一个序号,同时序号也保证了传送到接收端实体的包按序接收。然后接收端对已成功收到的包回一个 ACK包。如果发送端在合理的RTT内未收到确认,对应的数据包将被假设为已丢失,将会进行重传
- 错误校验:TCP用一个校验和函数来检验数据是否有错误;在发送和接收时都要计算校验和。
- 流量控制和阻塞管理:流量控制用来避免主机发送得过快而使接收方来不及完全收下
5.1 创建TCP网络程序流程
-
服务端
-
# coding: utf-8 import socket # 创建tcp套接字 tcpserver = socket.socket(socket.AF_INET, socket.SOCK_STREAM) addr = ('localhost', 7777) # 绑定ip tcpserver.bind(addr) # 开启监听 tcpserver.listen(5) # 接收客户端请求 print(f'TCP 服务器已开启:{addr}') while True: newSocket, clientAddr = tcpserver.accept() while True: data = newSocket.recv(1024) if len(data) > 0: print('receive from [%s]:%d, data: %s' % (*clientAddr, data.decode('utf-8'))) else: break newSocket.send('thank you!'.encode('utf-8')) newSocket.close() tcpserver.close()
-
-
客户端
-
# coding: utf-8 import socket tcpclient = socket.socket(socket.AF_INET, socket.SOCK_STREAM) dest = ('localhost', 7777) tcpclient.connect(dest) while True: sendData = input('send: #some msg#') if len(sendData) > 0: tcpclient.send(sendData.encode('utf-8')) else: break recvData = tcpclient.recv(1024) print(recvData.decode('utf-8')) tcpclient.close()
-
5.2 TCP的数据包格式
-
源端口和目的端口:各占16bit=2字节
-
序列号(Seq):占32位=4字节 range=[0:2^32] 表示数据的第一个字节的序列号,TCP的数据交互式基于序列号(控制华东窗口),发送方通过序列号控制发送的数据,以及超时重传,接收方通过序列号控制乱序重排。
接收方根据三次握手后确认的首字节序列号+数据长度,计算得到最后一个字节的序列号,并将其加1作为ack应答。
-
确认号(ACK):占4个字节,表示期望下次收到的序列号。比如服务器收到客户端发来的报文段,其序列号字段值为501,并通过计算可知数据长度为200,所以服务器可以算出最后一个字节的序列号为700。这表明服务器正确收到了客户端发送的序列号到700为止的数据,因此,服务器期望下次收到的序列号为701,并将其作为确认号放入应答报文段中
确认号和序列号范围相同,当溢出时从0开始
-
数据偏移:占4bit, 表示TCP报文段的第一个数据距离报文段起始处有多远。数据偏移代表的是4字节的倍数,由于4位二进制最大的可以表示15, 所以数据偏移最大值为4*15=60字节,即TCP报文首部最大长度。最小为20字节,偏移值=5。
-
保留 占6位,占未使用,可能是预留其他控制标志位,或者对齐字节位
-
控制位,用于说明报文段的性质。每个控制字段占1位
-
紧急URG:开启时表示此数据包处于紧急状态应优先处理
-
确认标志位ACK:开启表明确认号有效,TCP规定连接建立后发送的所有报文段ACK位都必须置1
-
推送PSH:该控制位很少使用,因为TCP会自己决定什么时候应该使用PUSH操作。
-
复位RST:用于复位,表示连接出现错误,应当立即关闭。当TCP接收到复位报文段后会通知应用程序连接被复位,随后关闭连接
-
同步SYN:连接建立的过程中用于同步序列号,告知对方自己的起始序列号。可以根据对方的序列号初始化缓冲区起点(滑动窗口)
SYN=1,ACK=0时表示一个连接请求报文段,SYN=1,ACK=1表示一个连接接收报文段
-
终止FIN:用于释放连接,报文段中FIN控制位为1表示已经将数据发送完毕。等待关闭连接
-
窗口:占2个字节,表示发送该报文段的一方能够接收的字节数,表明期望接受到的数据包字节数,用于拥塞控制。窗口值范围为[0:2^16−1]
-
校验和:占2个字节,用于检验报文段是否出错。发送方根据发送的报文段计算检验和填入报文段首部,接收方根据接收的报文段重新计算,如果不匹配,表明报文段出错
-
紧急指针:占2个字节,表示紧急数据的个数。在紧急状态下(URG打开),指出窗口中紧急数据的位置(末端)。
-
选项:用于支持一些特殊的变量,比如最大分组长度(MSS),MSS指的是数据的最大长度而不是TCP报文段长度。在将数据发送之前,会根据MSS将数据进行合理的切分,即单次发送的报文段中的数据不能超过MSS,所以MSS应该适当调大一些以降低网络中的报文段个数
查缺补漏
MSS(Maximum Segment Size):MSS 是TCP选项中最经常出现,也是最早出现的选项。MSS选项占4byte。MSS是每一个TCP报文段中数据字段的最大长度,注意:只是数据部分的字段,不包括TCP的头部。TCP在三次握手中,每一方都会通告其期望收到的MSS(MSS只出现在SYN数据包中)如果一方不接受另一方的MSS值则定位默认值536byte。
MSS值太小或太大都是不合适。太小,例如MSS值只有1byte,那么为了传输这1byte数据,至少要消耗20字节IP头部+20字节TCP头部=40byte,这还不包括其二层头部所需要的开销,显然这种数据传输效率是很低的。MSS过大,导致数据包可以封装很大,那么在IP传输中分片的可能性就会增大,接受方在处理分片包所消耗的资源和处理时间都会增大,如果分片在传输中还发生了重传,那么其网络开销也会增大。因此合理的MSS是至关重要的。MSS的合理值应为保证数据包不分片的最大值,对于以太网MSS可以达到1460byte,在IP层中有一个类似的概念,MTU(Maximum Transfer Unit)MTU=MSS+TCP Header + IP Header
为什么需要MSS?
主要是为了最大程度的保证传输的高效和稳定性
那么MTU和MSS又有什么必然联系呢?虽然MTU限制了IP层的报文大小,但分层网络模型本来不就是为了对上层提供透明的服务么?即使一个很大的TCP报文传递给IP层,IP层也应该可以经过分段等手段成功传输报文才对。
理论上来说是没错的,UDP中就不存在MSS,UDP生成任意大的UDP报文,然后包装成IP报文根据底层网络的MTU分段进行发送。MSS存在的本质原因就是TCP和UDP的根本不同:TCP提供稳定的连接。假设生成了很大的TCP报文,经过IP分段进行发送,而其中一个IP分段丢失了,则TCP协议需要重发整个TCP报文,造成了严重的网络性能浪费,而相对的由于UDP无保证的性质,即使丢失了IP分段也不会进行重发。所以说,MSS存在的核心作用,就是避免由于IP层对TCP报文进行分段而导致的性能下降。
通常将MSS设置为MTU-40(20字节IP头部+20字节TCP头部),在TCP建立连接时由连接双方商定,双方得到的MSS值可能并不相同,建立MSS所基于MTU的值基于路径MTU发现机制获取。
参考:
TCP报文段首部格式
TCP Maximum Segment Size (MSS)
TCP Maximum Segment Size (MSS) and Relationship to IP Datagram Size
-
5.3 TCP 三次握手
抛出疑惑:为什么是三次握手而不是二次或者四次握手?
TCP作为一种可靠传输控制协议,核心思想:既要保证数据可靠传输,又要提高传输的效率,而用三次就可以满足以上两方面的需求。
TCP的可靠性就是通过三次握手就是确认通信双方数据原点的初始序列号 (Initial Sequence Number)。
通俗的描述:客户端A发出连接请求,由操作系统动态随机选取一个32位长的序列号(Initial Sequence Number), 假设A的初始序列号是1000, 以该序列号为原点,对自己将要发送的每个字节进行编号,1001, 1002..., 并把自己的初始序列号INS告诉B, 什么样的编号的数据是合法的,方便服务端B对A的每一个编号的字节数据进行确认。如:如果A收到B确认编号为2001,则意味着字节编号为1001-2000,共1000个字节已经安全到达。
同理B也是类似的操作,假设B的初始序列号ISN为2000,以该序列号为原点,对自己将要发送的每个字节的数据进行编号,2001,2002,2003…,并把自己的初始序列号ISN告诉A,以便A可以确认B发送的每一个字节。如果B收到A确认编号为4001,则意味着字节编号为2001-4000,共2000个字节已经安全到达。
第一次握手:
客户端向服务端发送连接请求报文段,报文段的头部中SYN=1, ACK=0, seq=x。请求发送后,客户端进入SYN-SENT状态
- SYN=1, ACK=0 标识该报文段为连接请求报文
- seq=x, 标识本次TCP通信客户端数据字节流的初始序列号
- TCP规定:SYN=1的报文段不能有数据部分,但要消耗掉一个字节,一个序号
第二次握手:
服务端处于监听状态LISTEN,收到连接请求报文后,如果同意连接,返回一个应答 SYN=1, ACK=1, seq=y, ack=x+1, 进入SYN-RCVD状态
第三次握手:
当客户端收到服务器的应答后,还要向服务端发送一个确认报文段,表示服务端发来的连接同意应答已经成功收到,且收到服务端的出示序列号y
确认报文为:ACK=1, seq=x+1, ack=y+1。
为什么连接建立需要三次握手,而不是2次握手?
防止失效的连接请求报文段被服务端接收,从而产生错误,失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』---《计算机网络》谢希仁版
三次握手中存在的漏洞:SYN flood!,攻击者通过向服务器发起大量的SYN报文,把服务器的SYN报文连接的队列生生耗尽,导致正常的连接请求得不到处理,目前只能进行减缓,别没有解决补丁
-
在web应用程序中可以使用安全的CSRF令牌环节问题。CSRF攻击将在服务器造成持久的变化而没有处理要求,除非使用了有效的CSRF令牌。
-
首保丢弃:可通过丢弃客户端的第一个SYN报文来达到防御的目的,TCP是一种可靠的协议,为了确保所有的数据包都能到达服务器,设计了一个重传机制。真实的客户端访问,在一定的时间内如果没有收到服务器的回复,将会再次发送SYN报文。
-
内核层面进行缓解:
- 增大tcp_max_syn_backlog
- 减小tcp_synack_retries
- 启用tcp_syncookies: 当启用tcp_syncookies时,backlog满了后,linux内核生成一个特定的n值,而不并把客户的连接放到半连接的队列backlog里(即没有存储任何关于这个连接的信息,不浪费内存)。当客户端提交第三次握手的ACK包时,linux内核取出n值,进行校验,如果通过,则认为这个是一个合法的连接。(加密的INS)
注:tcp_max_syn_backlog 在 syn_cookies 开启时是无效的,这两个选项存在冲突
5.4 TCP四次挥手
第一挥手
若A认为数据发送完成,就会向B发送连接释放请求,该请求只有报文头,头重携带的主要参数为:FIN=1, seq=u, 此时A进入FIN-WAIT-1状态
- FIN=1即TCP报文段中的控制位FIN置1表示该数据报为连接释放请求
- seq=u, u-1是A向B发送的最后一个字节的序号
第二次挥手
B收到连接释放请求后,会通知相应的应用程序,告诉它连接已经释放,此时B进入CLOSE_WAIT状态, 报文头:ACK=1, seq=v, ack=u+1
- ACK=1, 除了TCP连接请求报文段以外,TCP通信过程中数据报的ACK控制为都为1
- seq=v, v-1表示B向A发送的最后一个字节的序号
- ack=u+1 表示希望收到第u+1个字节开始的报文段,已经成功接收了签u个字节数据
A收到该应答后进入 FIN_WAIT_2状态,等待B发送连接释放请求
第二次挥手后,A->B方向的连接已经释放,A不会再发送数据,但B->A方向的连接仍然存在。
第三次挥手
当B向A发送完所有数据后,向A发送连接释放请求,请求头: FIN=1, ACK=1, seq=w, ack=u+1 B进入 LAST_ACK状态
第四次挥手
A收到释放请求后,向B发送确认应答,A进入TIME_WAIT状态。该状态会持续2MSL(Maximum Segment Lifetime)时间,(报文最大生存时间),若该时间段内B没有发送请求的话,就进入CLOSED状态,关闭TCP。当B收到确认应答后,也进入CLOSED状态, 关闭TCP。
为什么A要先进入TIME-WAIT状态,等待2MSL时间后才进入CLOSED状态?
为了保证B能收到A的确认应答。
若A发完确认应答后直接进入CLOSED状态,那么如果该应答丢失,B等待超时后就会重新发送连接释放请求,但此时A已经关闭了,不会作出任何响应,因此B永远无法正常关闭。在模拟tcpserver的时候,如果是服务器先close的时候,在2MSL中(也就是2-4分钟之内并不会马上释放端口)不过在实际应用中可以通过设置 SO_REUSEADDR选项达到不必等待2MSL时间结束再使用此端口。
参考TCP 为什么是三次握手,而不是两次或四次?-[大闲人柴毛毛]