以下内容来自与B站up上传的视频,跟着学习敲的,图片引用了视频中的。
网络协议
TCP/IP:网络之间的通信,狭义的认识就是TCP/IP这两种协议,广义的认识,TCP/IP是IP协议的通信过程中,使用到的协议族的统称。
TCP/IP:是以传输控制协议(TCP)和国际协议(IP)为核心,包含了其它重要协议,相互合作,各司其职,共同保障网络通信的顺畅进行。
网络层:IP协议,IPv4地址,点分十进制,每台接入互联的设备都有一个独一无二的IP地址,
1.网络编程基础
1.网络通信
通信双方,必须满足两个条件,才能通信:
-
物理媒介(物理层面)
任何信息的传输都需要物理媒介
eg:网线、光纤、空气
-
协议(软件层面)
从应用的角度,协议理解为通信的规则,是数据传输和解释的规则
eg:假设,A B双方想要传输文件,可以约定双方规则
第一次:传输文件名,接收方接收到文件名后,应答ok给传输方 第二次:发送文件大小,接收方接收到文件大小后,应答ok给传输方 第三次:发送文件数据,接收方接收到文件大小多个字节后,应答ok给传输方
2.网络协议层次模型
层次:把不同的功能封装成不同的模块
为什么要分层?
把庞大的问题划分为若干个较小的局部文件,计算机网络功能非常复杂,采用分层结构将诸多功能合理的划分在不同层次,对等层之间指定协议,以实现功能。
每一层都简历在它的下层之上,像它的上一层提供一定的服务,并且把怎么实现这个服务的细节对上层屏蔽。

常见的网络层次模型:OSI七层模型 和TCP/IP四层模型(应用更广泛)
2.1OSI七层模型
OSI七层模型是ISO国际化标准组织指定,它针对广域网通信(也就是不同网络之间通信)设计的,将网络通信的功能划分为七个层次。
2.1.1物理层
通过光信号和电信号传输数据, 它负责管理计算机通信设备和网络媒体之间的互通,实现相邻节点之间比特(0/1)的传输。
物理层确保原始的数据可以在各种物理传输媒体上传输
典型设备:网线、光纤、集线器(hub,集线器连接的网络本质上就是一个广播域)、中继器

2.1.2数据链路层
在同一局域网内实现可靠的点对点数据传输 ,负责数据包的封帧及MAC寻址
互联设备之间传输和识别帧,对数据进行封装成数据帧并传递并进行错误的检测;
典型设备:交换机(switch)
MAC地址:是数据链路层的核心标识符,用于在同一物理网络内唯一标识设备,MAC地址就是唯一硬件身份,由设备网卡出厂时固化,全球范围内唯一。
MAC地址由48位(6个字节)组成,通常是16进制表示,前24是制造商编号,后面24位由制造商自行分配

2.1.3 网络层
互联网由无数个子网络组成的,比如中国和意大利肯定不在同一个子网络上,没有办法通过直接查找MAC地址来进行通信,网络层引入了一套新的地址,使得能够区分不同的计算机是否属于同一个子网络,这套地址称为网络地址,也称为IP地址。
一般通信的主机不是直接链接的,但是在物理层面连通的(中见经过多个中间节点(路由器/交换机/中继器等));
网络层负责将数据传输到目标地址,这一层主要负责IP地址寻址和路由选择,把分组(又称为数据报)从源点转发到目标节点。
典型设备:路由器(router)
该层协议:IP因特网协议:定义了数据包的格式及路由规则。

2.1.4 传输层
每台主机上会运行多个进程,因此我们还需要确保数据到底是发送到网络设备的哪一个进程,那就需要传输层,提供端到端(进程到进程之间的通信,同一个主机上不同的网络应用通过端口号区分)的数据传输,单位是数据报文。
这一层的目的确保数据可靠的,有序的传输,并且可以提供端到端的机制。
2.1.4.1 TCP协议
TCP:transport control protocol,传输控制协议,是一种面向连接、面向字节流 的传输层协议,它能够提供可靠通信, 数据无误、数据无丢失、数据无失序、数据无重复到达(建立三次握手、四次挥手、重发机制),数据单元(段)。
面向连接:双方在通信之间必须要先建立连接(三次握手),确认连接之后才能开始进行通信。
面向字节流:指TCP对待传输数据的方式将其视为连续的、无结构的、无边界的字节序列,而非独立的报文(UDP),TCP眼中只有字节流,没有数据包,将数据流切分段,用序列号为每个字节定位。
eg:发送了消息1"hello" 发送了消息2"world" TCP只保证数据按照你的字节顺序发送出去:h e l l o w o r l d 接收方接收到的数据有可能是两条消息"hello" "world" 接收方接收到的数据可能是两条消息"helloworld" 接收方接收到的数据可能是两条消息"hellowo""rld" ... TCP粘包
2.1.4.2 解决TCP粘包的问题
-
固定长度:每条消息都严格一样长(比如每条消息都是20个字节),接收方只要直到读取了20个字节就是一条消息。
缺点:不灵活,浪费空间。
-
分隔符:在每条消息的结尾加上一个特殊的,不会在正常内容中出现的字符(eg: \r\n),接收方只要持续读取,当读到了分隔符的位置,就知道一条消息结束了。
-
长度前缀(最常用、最推荐):在每条实际消息之间,先添加一个固定长度的消息头,先发送4字节的长度,在发送实际的消息。
eg: "hello" 0x05 h e l l o "kkkk" 0x04 k k k k
2.1.4.3 TCP首部
TCP如何来实现数据无误、数据无丢失、数据无失序、数据无重复到达,通过解析TCP头部信息探究。
截图1:07:26s
| 功能 | |
|---|---|
| 源端口号(2字节) | 表示发送方应用程序使用的端口号 (端口唯一标识网络应用) |
| 目的端口号(2字节) | 表示接收方应用程序使用的端口号 |
| 序列号(SEQ)(4字节) | 用于标识发送数据的序列号,用于对数据进行排序和重组,每个TCP数据段都有一个唯一的序列号,标识当前数据包第一个字节在数据流中的位置 |
| (TCP using cumulative ack) 确认号(4字节) | 如果设置了ACK标志位,则该字段有效,用于确认接收到的数据,表示接收方期望下一个接收到的字节的序列号 |
| (TCP using cumulative ACK) 数据偏移(4 bits) | 该字段表示TCP首部长度,是一个4位的二进制数,指明TCP首部中包含多少个32位的字,一般固定位5。因此首部长度为20字节 |
| 保留(6 bits) | 未使用,保留为0 |
| URG标志(1 bit) | 紧急指针(urgent pointer) 是否有效的标志,如果设置了URG标志,则表面紧急指针指定的数据会优先传递处理 |
| ACK标志(1 bit) | 确认标识位,用于确认接收到的TCP报文。如果设置了改标志,则在ACK字段中确认号有效;否则,确认号无效,可以忽略ACK字段 |
| PSH标志(1 bit) | 推送标识位,用于告诉接收方应该尽早将数据交给上层应用程序处理 |
| RST标志(1 bit) | 复位标识位,用于终止残留的连接,相当于发送一个规范的非法序列,不需要对方回应设计的 |
| SYN标志(1 bit) | 同步标识位,用于发起一个新的TCP连接。在三次握手过程中用来建立和确认双方通信的初始序列号(ISN) |
| FIN标志(1 bit) | 结束标识位,用于释放一个TCP连接。当此标志置位后,表明发送方已经没有数据需要传输了,等待接收方的确认即可安全关闭连接 |
| 窗口大小(2字节) | 用于控制数据流量和避免缓冲区溢出的标志。发送方和接收方通过该字段进行流量控制 |
| 校验和(2字节) | 用于检测TCP首部和TCP数据是否出现错误或被改动,并确保数据是由正确的发送方发出的 |
| 紧急指针(2字节) | 如果设置了URG标志,则改字段表面紧急数据在整个数据中的位置 |
| 可选项 | 可选字段,长度不定,在必要时包含一些附加信息,如时间戳、SACK等。可以使用NOP字段填充必须为32位的限制 |
2.1.4.4三次握手
1)SYN:同步标志位,发起新的连接请求,SYN=1,标识进行一个连接请求 2)ACK:认为,ACK=1,确认有效 ACK=0,确认无效 3)FIN:断开一个连接 4)seq:序号,标识从计算机A发送数据到计算机B的字节编号,当前数据包的第一个字节在数据流中的位置(字节序) 5)ack:ack=已经接收到的最后字节的编号(seq字节序 + 负载数据长度) + 1 ack,确认号,确认已经收到之前的包,期望下次收到的包序号为 ack=已经接收到的最后字节的编号(seq字节序 + 负载数据长度) + 1


客户端 服务端
你好,我希望来建立连接
好的,我已经准备好了,可以开始建立连接
好的,马上来连接你
===> 双方建立连接成功
第一次握手:
客户端主动希望与服务器建立连接,它将会发送一个特殊的TCP报文段 这个报文段的SYN=1 它会发送一个随机的初始序列号,seq=x 这个报文段不携带应用层的数据 客户端发送完SYN同步报文后,客户端进入SYN-SENT(同步已发送)状态,等待服务器的确认
第二次握手:
服务器一直处于监听(LISTEN)状态,一旦监听状态下,有监听到来自客户端的SYN请求后,如果服务器同意连接的,回复客户端一个报文 SYN=1 ACK=1确认有效 seq=y ack=x+1(表示收到了序号为x的包,期望收到下一个为x+1的包) 这个报文同样不携带应用层数据 服务器发送完报文后,进入SYN-RCVD(同步已收到)状态
第三次握手:
客户端收到服务器的报文后,再次向服务器发送一个确认报文 ACK=1 seq=x+1 ack=y+1(表示收到序号为y的包,期望下次收到为y+1的序号的包) 客户端发送完报文后,进入ESTABLISHED(连接已建立)状态 established 服务器收到这个报文后,进入到ESTABLISHED(连接已建立)状态 至此,三次握手完成,双向通信通道已经成功建立,双方都可以开始传输数据了
2.1.4.5 四次挥手

第一次挥手:
客户端主动断开连接,希望关闭连接,它会发送一个报文段 FIN=1 seq=u 客户端发送完FIN报文后,进入FIN-WAIT-1(终止等待1)状态
第二次挥手:
服务器收到客户端FIN报文后,直到客户端想要关闭连接了 服务器立即发送一个ACK确认报文,这个报文中ACK=1 ack=u+1 seq=v 服务器发送完ACK确认报文后,服务器进入到CLOSE-WAIT(关闭等待)状态,TCP处于半关闭状态(即客户端到服务器的通道已经关闭,客户端不能再发送数据,但是服务器还可以给客户端发送数据)
第三次挥手:
服务器把所有的数据全部发送完后,服务器也会开始要完全关闭连接 也发送一个FIN报文,FIN=1 seq=w 服务器发送完FIN报文后,进入LAST-ACK(最后确认)状态,等待一个客户端的ACK报文
第四次挥手:
客户端收到服务器的FIN报文后 它会像服务器发送一个ACK报文,ACK=1 ack=w+1 客户端发送完ACK后,进入TIME-WAIT状态 服务器接收到ACK后,进入CLOSED状态
2.1.4.6 UDP协议
UDP:user datagram protocol 用户数据报协议,面向无连接的,不可靠的协议,并且面向数据报的
面向无连接:在数据发送前,不需要建立连接
不可靠:数据传输的时候,不会有数据回复/重发(提高发送数据效率)
相对TCP而言,UDP没有那么复杂的机制来确保数据可靠,优点是效率高
"直播",实时应用
数据单元:数据报,独立报文
eg:发送消息1”hello“ 发送消息2”world“ UDP将两个消息分别封装成独立的报文,因此接收方能够直接知道这是两条独立消息,UDP中不会存在粘包的问题
2.1.5 会话层
管理进程间的会话,建立建立,管理或者断开与应用程序之间的通信,组织和协调两个绘画之间的通信,并且对数据进行管理
数据单元:会话数据
eg:FTP下载终端后从哪个断点继续重传
2.1.6 表示层
把数据转换为能与接收者的系统格式兼容并适合传输的格式(eg:双方系统不一样的,双方通信编码不一样),主要是对发送/接收的数据进行加密解密,进行格式的转换,确保一个系统的应用程序发送的数据,能够被另外一个系统的应用程序识别。
数据单元:表示数据
2.1.7 应用层
提供为应用软件而设计的接口,以设置与另一个应用软件之间进行通信,应用层以报文为数据传输单位
eg:http http sftp SSH DNS等
http超文本传输协议 ftp文件传输协议 DNS域名解析协议 qq协议

2.2 TCP/IP四层模型
TCP/IP协议体系结构划分了四层,从高到低为:应用层、传输层、网络层和网络接口层,虽然只有四层,但是包含了OSI七层中所有的功能,也包括了局域网和广域网之间通信的全部功能,目前TCP/IP模型应用比较广泛,而OSI七层由于过于理想化,导致实际应用中较为复杂,所以没有称为主流的模型。
| OSI七层模型 | TCP/IP四层模型 | 对应网络协议 | 数据格式 |
|---|---|---|---|
| 应用层 | 应用层 | HTTP、DNS、SMTP、FTP、TFTP、Telnet、SNMP、SSH等 | 报文 |
| 表示层 | |||
| 会话层 | |||
| 传输层 | 传输层 | TCP、UDP | 段 |
| 网络层 | 网络层 | IP、ARP(IP->MAC)、RARP(MAC->IP)等 | 分组(数据报) |
| 数据链路层 | 网络接口层 | FDDI,Ethernet、Arpanet、PDN、SLIP、PPP | 帧 |
| 物理层 | IEEE802、1A、IEEE802到IEEE802.11 | 比特 |
3.常见的网络相关设备
3.1集线器(hub)
-
可以将多个节点连接起来,组成一个计算机网络,用于在物理层拓展以太网
-
工作在物理层,只是带电信号的传输,仅处理电信号(比特流),无数据链路层的功能(不识别MAC地址)
-
接收到某一个端口的电信号,将其放大整形后,广播到其他端口,集线器不进行碰撞检测,可能会产生消息碰撞。
3.2交换机(switch)
-
可以提供大量的网络接口将多态网络设备连接成局域网
-
可以将多个节点连接起来,组成一个计算机网络。
-
工作在数据链路层
-
通过MAC地址来识别网络中的设备,并根据MAC地址进行查询MAC地址表,实现数据的转发
-
数据帧进入交换机端口,记录源MAC地址,记录到MAC地址表中(表中记录MAC地址及其连接的具体的端口)
-
转发一个数据帧,交换机通过查表,查找该帧的目的MAC连接在哪个端口,能够查询到从端口转发,如果表中没有目的MAC地址,那么就会广播,由主机判断数据是否是发送给自己的
-
3.3路由器
1)IP地址:IPV4(32bits=网络号/网段号+主机号) IPV6 2)在同一个局域网内的主机,网络号相同,主机号必须不相同 3)通常我们会将一个子网中主机位全0的地址称为网络号或者网段号,它代表整个子网本身,不能分配给任何的主机 4)通常将一个子网中主机位全位1的地址称为广播地址,不能分配给任何主机 5)通常将主机号为1的设置为默认网关 eg:子网内某台主机IP:192.168.31.10 子网掩码:255.255.255.0(11111111.11111111.11111111.00000000)高位为1为网络号,地位连接0主机号 理论上子网内可以最大又多少台主机 192.168.31.0~192.168.31.255,最大可以有256台 192.168.31.0 代表这个子网的网络号,不能发配给主机 192.168.31.255 当前子网的广播地址,不能发配给主机 最大只能有254台,如果还需要设置网关IP,还需要-1 默认网关:192.168.31.1 最多只能有253台
-
可以把两个或多个计算机网络互相连接起来,形成更大的计算机网络,称为"互连网",全球最大的互连网是因特网。
-
工作在网络层,隔离广播域 ,可以实现路由选择和数据包的转发和交换,收到一个数据包时,会解析数据包,查看IP地址,识别IP地址,不关心MAC地址
-
路由器的每一个接口都连接着一个独立的广播域,通常就是一个独立的子网,而这个路由器的接口的IP地址,就是该子网内所有设备的"默认网关",一般将网段内的第一个可用的主机IP地址设置为默认网关
-
路由表是路由器中存储的信息,用于决定数据包应该被转发到哪个下一跳或者接口,它包含目标网络地址和下一跳的信号,这里直接与网络层的路径查询有关

4.网络数据传输过程

feiQ为例,用户a给用户b发送数据,解析封装数据及拆包的过程:

eg:家用路由器举例 - WAN(广域网接口):连接外网,WAN口也会有一个网关地址,这个网关是由运营商分配给你的 - LAN(局域网接口):连接子网内的不同的主机
5. IP地址
IP地址,常见的IP地址分类:
-
IPV4地址:32bits
-
IPV6地址:128bits
IPV4地址32bits分成了两个部分:网络号(网段号) + 主机号
**网络号:**用于标识某一个网络(属于哪个网络),用子网掩码的连续的高位1来标识网络号在IP地址中占多少bit
**主机号:**用来标识特定的网段下特定的主机(是属于哪个网络下的一台主机),子网掩码中连续的地位的0来标识主机号在IP地址中站多少bit
IPV4表示:采用点分式表示
eg:192.168.31.10
172.5.0.1
5.1 IP地址划分
IP地址分为A B C D E共5类
A类分配给政府机构
B类分配给公司单位
C类分配给个人
D类分配给组播
E类用于各种实验
地址按照使用范围划分,可以划分为两种:一种叫公网地址,一种叫私网地址
公有IP与私有IP区别:
-
公网IP:是全球互连网范围内唯一的,确保每个设备在通信中的身份识别不重复,可以被其他互连网设备访问和通信,eg:网站、服务器等
-
私网IP:不是全球唯一的,仅在局域网内有效,私有IP地址在局域网内唯一,但同一个局域网内的IP互不相同,不同的局域网内可以有相同的IP地址,为了为企业和家庭提供IP分配的灵活性
-
公网IP由运营商分配,只有公网IP才能上网,但是不可能给每一台主机分配一个公网IP,因为IPV4地址32bits,不够分,才需要私网IP,私网IP要上网,必须经过公网IP
| 特殊地址 | 用途 |
|---|---|
| 0.0.0.0 | 不能作为目的地址使用。它通常用于表示"本网络上的本主机"在尚未获得具体的IP地址时的状态,会使用0.0.0.0作为源地址发送。 |
| 255.255.255.255 | 这是一个特殊的广播地址,它用于在本地网络上发送广播消息,即向同一网络段内的所有设备发送消息。 |
| 127.0.0.0 ~ 127.255.255.255 | 特殊的回环地址范围,也称为本地回环地址或环回地址,它用于指代本机地址,即发送数据包的设备本身就是目的地(自己给自己发)。指代本机地址:127.0.0.1(最常用的回环地址) |
| 169.251.X.X | 微软保留地址,无ip时会分配到这个地址 |
5.2 IP地址分类

| IP地址的范围 | 私有地址的范围 | ||
|---|---|---|---|
| A | 0网络号(7Bits)+主机号(24Bits) |
0.0.0.0~127.255.255.255 | 10.0.0.0~10.255.255.255 |
| B | 10网络号(14Bits)+主机号(16Bits) |
128.0.0.0~191.255.255.255 | 172.16.0.0~172.31.255.255 |
| C | 110网络号(21Bits)+主机号(8Bits) |
192.0.0.0~223.255.255.255 | 192.168.0.0~192.168.255.255 |
| D | 1110多播组号(28Bits) |
224.0.0.0~239.255.255.255 | |
| E | 11110留待以后(27Bits) |
240.0.0.1~255.255.255.254 |
IP地址由网络号 + 主机号共32bits构成,那到底网络号占多少位,主机号占多少位,取决于子网掩码?
5.3 子网掩码
子网掩码:用来指定一定ip地址中,哪些bits是网段号,哪些bits是主机号
netsmask 高位的连续的1为网段号,低位连续的0为主机号
eg:
netmask: 255.255.255.0 ==> ip中高3字节为网络号,低1字节为主机号
eg:B类网络的子网掩码是255.255.252.0,则理论上每个子网的主机数最多可以是多少个?
11111111 11111111 11111100 00000000 理论上IPV4地址中,主机号占10位 0000000000 ~ 1111111111 1024台 实际中减去网关、网络号、广播地址 实际中最多只能有1021台
同一网络下主机的子网掩码是相同的,同一子网下网络号也是相同的
设置网卡ip地址
bash
ifconfig etho(网卡名字) 192.168.31.100(设置的IP) netmask 255.255.255.0(设置的子网掩码) up
up: 立即生效
有了MAC地址和ip地址还不能进行通信,还需要端口号(标识是设备上哪一个网络应用),提供端到端的服务
ip地址用来标识网络上一台主机,一个主机上可以有多个网络应用,通过端口号区分不同的网络应用
6. 端口号(传输层)
不管是TCP还是UDP,都有源端口和目的端口两个字段
TCP和UDP采用16bits的端口号来标识网络应用程序
网络传输层的角度:TCP应用和UDP应用
TCP端口和UDP端口是独立的
假设TCP应用的端口为20 与 UDP应用端口为20 不是同一个应用,相互独立的
应用程序的地址: 应用程序对外提供服务的地址:IP地址+端口号 eg:web服务器的地址为192.168.1.100:80
网络连接四元组:MAC地址 + IP地址 + 传输层协议(TCP/UDP) + 端口号
端口号并不是你想指定为什么就是可以指定的,IANA机构管理端口号
公认端口:
-
端口号范围:0~1023,这部分端口已经被一些知名的应用程序使用的,所以在测试自己的程序时候尽量不去使用该范围内的端口
-
特性:这些端口已经绑定了一些服务,eg:HTTP服务的默认端口为80,HTTPS服务默认端口为443,SMTP服义默认端口25......

7.网络字节序的问题
主机字节序(又称为CPU字节序):由CPU指令集决定,它就决定了数据在内存中的存储方式,常见的字节序由大端字节序和小端字节序。
网络字节序: 采用大端字节序作为标准。
如果主机主机字节序与网络字节序不一致的,有可能导致数据解析错误,因此TCP/IP协议规定,发送端需要发送数据之前,先将主机字节序转换为网络字节序,在接收数据时,接收端需要将网络字节序转换为主机字节序,这样转换过程确保了数据在传输过程中的一致性和正确性。
IP地址转换:
cpp
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
//n: network 网络地址
//a: ip的点分式十进制字符串
int inet_aton(const char *cp, struct int_addr *inp);
@cp: 指向要转换的点分式的IP地址字符串 eg: "192.168.31.10"
@inp: 指向一个转换后的struct in_Addr 网络ip地址,用来保存转换后的网络IP
/* Internet address. */
struct in_addr {
uint32_t s_addr; /*address in network byte order*/
};
in_addr_t inet_addr(const char *cp);
@cp: 指向转换的点分式的十进制的IP地址 eg: "192.168.31.10"
返回值:
返回一个IP的网络地址
in_addr_t inet_metwork(const char *cp);
@cp: 指向转换的点分式的十进制的IP地址 eg: "192.168.31.10"
返回值:
返回一个IP的网络地址
char *inet_ntoa(struct in_Arrd in);
@in: 网络字节序的IP
返回值:返回转换后的点分式十进制字符串IP地址的首地址
端口号转换:
cpp
//h: host 主机
//n: network 网络
//s: short
//l: long
#include <arpa/inet.h>
// 把一个32bits整数的主机字节序转换为网络字节序
uint32_t htonl(uint32_t hostlong);
@hostlong: 主机字节序中一个long类型的整数
返回值: 转换后的32bits网络字节序的整数
// 把一个16bits整数的主机字节序转换为网络字节序
uint16_t htons(uint16_t hostshort);
// 把一个32bits整数的网络字节序转换为主机字节序
uint16_t ntohl(uint32_t netlong);
// 把一个16bits整数的网络字节序转换为主机字节序
uint16_t ntohs(uint16_t netshort);