第一部分:网络基础
目录
1、网络背景
1.1、网络协议
1.2、网络分层
1.2.1、OSI模型
1.2.2、TCP/IP模型
2、网络传输
2.1、封装与分用
2.2、网络中的地址
2.2.1、IP地址
2.2.2、MAC地址
2.2.3、端口号
2.3、局域网
2.3.1、以太网
2.3.2、令牌环网
2.3.3、无线网
2.4、跨网传输
3、TCP和UDP协议
3.1、TCP协议
3.2、UDP协议
4、网络字节序
注意:前面Linux系统编程部分的文章所使用的环境是CentOS 7.6,从现在开始换成了Ubuntu 24.04。虽然这两个都是Linux操作系统,但是还是有些差别的,不过差别不大。
1、网络背景
网络的本质就是获取和传输数据,之前的系统的学习本质就是在加工和处理数据。
最一开始计算机之间是相互独立的。如下图所示:

后来将多台计算机连接在一起,完成数据共享,如下图所示

局域网:计算机数量更多了,通过交换机和路由器连接在一起。局域网是在较小地理范围内构建的网络,通常用于连接同一建筑或区域内的设备,比如家庭、办公室、学校机房等。一般通过线路连接或短距离无线连接。如下图所示:

广域网:将远隔千里的计算机都连在一起。广域网是覆盖大范围的网络,用于连接不同地区、城市、国家,是实现远距离通信的关键。依赖基站和卫星等进行连接。如下图所示:

所谓 "局域网" 和 "广域网" 只是一个相对的概念,比如一个城市的网也可以看做一个比较大的局域网。
1.1、网络协议
"协议" 是一种约定。计算机之间的传输媒介是光信号和电信号,通过"频率"和"强弱"等来表示 0 和 1 这样的信息,要想传递各种不同的信息,就需要约定好双方的数据格式。
如下图所示:如果没有了协议,就没法顺利的进行通信。

在上面的这个图片中,协议只有两层,但是实际的网络通信会更加复杂,需要分更多的层次,之所以要分层一方面是因为各个协议要解决的问题不同,另一方面就是通过分层可以降低耦合度,方便进行管理。
计算机生产厂商有很多;计算机操作系统,也有很多;计算机网络硬件设备,还是有很多;如何让这些不同厂商之间生产的计算机能够相互顺畅的通信,就需要约定一个共同的标准,大家都来遵守,这就是网络协议。
1.2、网络分层
1.2.1、OSI模型
OSI模型,称为开放式系统互联参考模型,是一个逻辑上的定义和规范,核心作用是将复杂的网络通信流程拆分为7个独立且协作的层级,让不同厂商的设备能按统一规则互通。逻辑上分为了7层,每一层都有相关、相对应的物理设备。
OSI 七层模型是一种框架性的设计方法,其最主要的功能使就是帮助不同类型的主机实现数据传输,它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整,通过七个层次化的结构使不同的系统不同的网络之间实现可靠的通讯。
1、物理层:负责物理介质上的信号传输(如电信号、光信号),定义硬件接口(如网线接口、光纤接口)的物理规范。常见设备有网线、光纤、网卡、集线器(HUB)等。
2、数据链路层:处理物理层的信号,将其封装为 “帧”,解决同一局域网内的设备寻址(用 MAC 地址)和数据差错检测。常见设备有交换机、网桥;协议有以太网(Ethernet)。
3、网络层:实现不同局域网或广域网之间的路由选择(跨网段通信),通过 IP 地址定位目标设备,转发数据包。常见设备有路由器;协议有IP、OSPF、BGP。
4、传输层:为上层应用提供端到端的可靠或不可靠传输服务,控制数据传输的速率、顺序和差错重传。协议有TCP(可靠)、UDP(不可靠)。
5、会话层:建立、管理和终止两个应用程序之间的 “会话连接”,比如控制用户登录后的会话保持、断开。协议有RPC、NetBIOS。
6、表示层:处理数据的格式转换和加密,比如将数据压缩、加密,或把不同格式的文件(如 JPG、MP4)转换为网络可传输的统一格式。功能是数据加密(SSL/TLS)、格式转换。
7、应用层:直接为用户应用程序提供网络服务,是用户能直接感知的层级,比如网页浏览、文件传输、邮件发送。协议有HTTP/HTTPS、FTP、SMTP、DNS。
但是IOS模型,它既复杂又不实用,实际中是按TCP/IP五层模型来的。
1.2.2、TCP/IP模型
TCP/IP是一组协议的代名词,它还包括许多协议,组成了TCP/IP协议簇。TCP/IP模型采用了5层的层级结构。
1、物理层:负责光和电信号的传递方式,比如:现在以太网通用的网线(双绞线)、早期以太网采用的的同轴电缆 (现在主要用于有线电视)、光纤,现在的wifi无线网使用电磁波等都属于物理层的概念。物理层的能力决定了最大传输速率、传输距离、抗干扰性等。集线器就工作在物理层,主要作用就是进行信号放大。
2、数据链路层:负责设备之间的数据帧的传送和识别,例如:网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、冲突检测(如果检测到冲突就自动重发)、数据差错校验等工作,有以太网、令牌环网,无线LAN等标准。数据链路层主要就是用来解决同一个局域网中两台主机之间数据的传递和识别。交换机就工作在数据链路层。
3、网络层:负责地址管理和路由选择,例如:在IP协议中,通过IP地址来唯一标识一台主机,并通过路由表的方式规划出两台主机之间的数据传输的线路。路由器就工作在网络层。
4、传输层:负责两台主机之间的数据传输,例如:传输控制协议 (TCP),能够确保数据可靠的从源主机发送到目标主机。
5、应用层:负责应用程序间沟通,如简单电子邮件传输(SMTP)、文件传输协议(FTP)、网络远程访问协议(Telnet)等,我们的网络编程主要就是针对的就是应用层。(相当于把OSI模型的应用层、表示层以及会话层合并成了TCP/IP模型的应用层)。
之所以叫TCP/IP模型,原因是网络层和传输层很重要,因此采用了网络层和传输层中最有代表性的协议来命名整个模型。很多时候物理层我们考虑的比较少,因此也可以称为TCP/IP四层模型。
一般而言,对于一台主机,它至少实现了从传输层到物理层的内容;对于一台路由器,它至少实现了从网络层到物理层;对于一台交换机,它至少实现了从数据链路层到物理层;对于集线器,它至少实现了物理层。
网络层次和之前系统部分的对应层次,如下图所示:

注:操作系统本身通常不包含驱动程序,但是它要调用驱动程序来操作硬件,驱动程序往往由硬件厂商开发和提供。
2、网络传输
2.1、封装与分用
数据包:是网络中数据传输的基本单位,可以理解为 “封装了数据和控制信息的‘小包裹’”,负责在不同设备之间传递信息。
不同的协议层对数据包有不同的称谓,在应用层就叫数据,在传输层叫做数据段,在网络层叫做数据报,在链路层叫做数据帧。
数据包的封装,简单说就是数据在网络传输过程中,被各层协议 “层层打包” 的过程,每经过一层协议,都要加上一个数据首部,里面是该层的 “控制信息”(比如地址、校验码等)。就像寄快递时,先装内容物,再套快递袋,最后贴快递单,每一层包装都有特定作用。
当对应的主机收到数据包时,每层会逐层去掉之前封装的 “控制信息(头部或尾部)”,最终提取出原始数据的过程称为分用,也叫解封装,和上面的封装的刚好相反。相当于收到快递后,逐层拆开包装(快递袋、信封),拿到里面的信件。如下面的图片所示。
注:网络通信是要贯穿整个协议栈的,网络协议栈的层状结构中,每一层都是要有协议的。例如:以太网中的两台主机进行通信,如下图所示:

每层都要添加报头,去掉报头的信息称为有效载荷,报文=报头+有效载荷。
注:几乎任何协议都要在报头中提供将有效载荷交付给哪一个上层协议的内容。
2.2、网络中的地址
2.2.1、IP地址
IP协议有两个版本,IPv4和IPv6。一般后面提到IP协议,没有特殊说明的,默认都是指IPv4。
IP地址是在IP协议中,用来标识网络中不同主机的地址,对于IPv4来说,IP地址是一个4字节,即32位的整数,我们通常也使用 “点分十进制” 的字符串表示IP地址,例如192.168.0.1,用点分割的每一个数字表示一个字节,范围是 0 - 255,有了IP地址就方便进行路径规划了。IPv6是16个字节,之所以会有IPv6是因为IPv4严重不足了。
我们可以使用使用如下命令查看IP地址,Linux命令:
ifconfig
其中inet后面的就是内网IP地址,ether后面的就是MAC地址。
Windows下也有一个类似作用的命令:
ipconfig
2.2.2、MAC地址
MAC地址用来识别数据链路层中相连的节点,长度为48位,就是6个字节,一般用16进制数字加上冒号的形式来表示(例如:08:00:27:03:fb:19)。该地址在网卡出厂时就确定了,不能修改,MAC地址在局域网内是唯一的。
2.2.3、端口号
网络中的数据链路层、网络层和传输层主要解决的问题就是将数据安全可靠的交给远端机器,用户使用应用层软件,完成数据的发送和接收,用户使用应用软件首先就要启动应用软件,本质就是启动了进程,因此进程之间进行数据交换才是目的,因此网络通信也就是进程间通信,虽然IP已经定位了一个主机了,但是想要做到进程通信,就还需要能确定唯一的一个进程,所以就有了端口号。
端口号:是传输层协议的内容,端口号是一个2字节16位的整数,端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理,IP地址 + 端口号能够唯一标识网络上的某一台主机的某一个进程,一个端口号只能被一个进程占用,另外,一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定。
我们之前在系统编程的时候,了解过pid表示唯一的一个进程,此处的端口号也是唯一表示一个进程,那么这两者之间是怎样的关系?pid是 “进程的身份证”,用于本机内部管理;端口号是 “网络进程的门牌号”,用于跨主机通信,这样就可以将系统和网络进行解耦合。
2.3、局域网
2.3.1、以太网
是应用最为广泛的局域网技术,我们日常用网线连接电脑、交换机、路由器,本质都是以太网架构。每台主机在局域网上,都要有一个自己的唯一的标识,这个就是MAC地址。

如上图所示,当主机1发送数据时,局域网中的所有的主机都能收到主机1发送的信息(物理层)。每台主机进行解析后(数据链路层),发现不是发给自己的数据时,就会将数据直接丢弃,如果发现是发给自己的数据,就会向上层递交。所以最终感觉上就是主机8收到了主机1发送的数据。
注:局域网中任意两台主机是可以直接通信的。
上图中,如果多台主机同时发送消息,就可能导致数据的碰撞问题,这样数据就全乱了。所以每台主机都要执行碰撞避免算法,这个算法的原理就类似于汽车错峰出行。一般来说,局域网中的主机越多,数据的传输延迟就会变高,感觉就是网络有点卡。
一般将一个局域网中的所有主机叫做一个碰撞域。交换机可以一定程度上解决数据碰撞问题,本质上就是将碰撞域划分成了一个个小的碰撞域,即划分碰撞域,从而提高提高效率。如下图所示:

当上图中主机1发送信息,交换机也会收到主机1发送的信息,经过交换机处理后,如果发现信息的接收方不在交换机的右边,则交换机会拦住主机1发送的消息,因此交换机右边的主机就收不到主机1发送的信息;如果经过交换机处理后,如果发现信息的接收方在交换机的右边,则不会拦截信息。
注:即便有交换机,上图中的主机还是处在一个局域网中。其实可以将局域网看成是多台主机共享的临界资源。
2.3.2、令牌环网
早期局域网技术,现在已基本退出市场。令牌环网和上面的以太网是类似的,区别是它不像以太网一样基于一些算法等来避免数据碰撞问题,而是通过使用一个标识数据来解决数据碰撞的问题,只有获得标识数据的主机才能发送数据,其他没有标识数据的就不能发送信息。这个标识数据就类似于多线程中的锁。
2.3.3、无线网
也就是常说的 Wi-Fi,具有高度的灵活性和便捷性。是手机、笔记本、平板等移动设备接入网络的核心方式。
简单总结:以太网是 “有线网络的王者”,Wi-Fi 是 “无线网络的王者”,两者互补覆盖几乎所有现代网络场景。
2.4、跨网传输
跨网络的主机进行信息传输,数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器。如下图所示:

注:在IP数据包头部中,有两个IP地址,分别叫做源IP地址,和目的IP地址。传输层协议(TCP和UDP)的数据段中也有两个端口号,分别叫做源端口号和目的端口号,就是在描述 "数据是谁发的,要发给谁"。
路由器可以连接左右两个子网,也就是两侧连接的都是一个局域网。
IP可以表明要接收信息的主机与发送信息的主机不在一个子网,因此主机就知道要把信息发给路由器,把数据交给路由器本质就是局域网通信;最终路由器会接收主机1发送的信息。IP协议屏蔽了底层的网络化的差异,靠的就是工作在IP层的路由器,IP实现了全球主机的软件虚拟层。
从上图中可以看出,MAC地址的作用就是在局域网中将一台机器的信息送到对应的另一台机器上,出局域网后会由路由器进行重新封装。而IP地址是用来帮助进行路径选择的。
注:通讯的过程就是不断的封装和解包的过程。
因此网络通信的基本脉络如下图所示:

3、TCP和UDP协议
TCP和UDP都是传输层的协议,一般以数据通信为目的的代码都是使用传输层的接口。
3.1、TCP协议
此处先对TCP(传输控制协议)有一个直观的认识,细节后面再说。
1、传输层协议。
2、有连接。指要先进行连接,成功后才能发送信息,简单来说就是在通信前要先确保是可以通信的。
3、可靠传输。有连接就是可靠传输的一个特点之一。
4、面向字节流,后面会详细说,此处先知道是面向字节流的即可。
3.2、UDP协议
此处我们也是对UDP(用户数据报协议),细节后面再说。
1、传输层协议。
2、无连接。直接发送,不用先建立连接。
3、不可靠传输。没法确定信息是否发送成功。
4、面向数据报,面向数据包和面向字节流是一个相对的概念。
TCP和UDP都有各自适用的场景,虽然可靠传输是不错,但是可靠传输所要付出的成本是要大于不可靠传输的,可靠传输也更加的复杂。
4、网络字节序
我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,网络数据流同样有大端小端之分,那么如何定义网络数据流的地址呢?
1、发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,因此,网络数据流的地址这样规定:先发出的数据是低地址,后发出的数据是高地址。
2、TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节,不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送和接收数据,如果当前发送主机是小端机, 就需要先将数据转成大端,否则就忽略,直接发送即可。
为使网络程序具有可移植性,同样的程序在大端和小端计算机上编译后都能正常运行,可以调用以下面的函数做网络字节序和主机字节序的转换。例如:
#include <arpa/inet.h>uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
1、其中,h表示host,n表示network,l表示32位整数,s表示16位短整数。比如:htonl表示将32位的整数从主机字节序转换为网络字节序。
2、如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回。如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回。
下一篇文章将讲socket编程。
