为什么要有网络呢?

在一开始科学家们都是自己在计算机当中做实验但是难免需要共同进行科研。假设现在一个业务需要三个人共同完成,那么现在就有问题了:
- 由于第一个人完成工作前,其他两人无法开始,这导致工作流程是串行的,效率较低。
- 人工传输数据耗时,进一步延长了科研周期。
那有什么办法可以进行加快效率呢?把所有的主机都用一根网线连接起来,这样被连接起来的计算机就可以进行通信啦~
局域网概念:我们把这些计算机通过网线连接起来可以实现通信的地理范围成为局域网。
🌰: 我们自己家庭就可以看作是一个局域网,在自己家中无论是电脑还是手机都可以在局域网范围内连接wifi。

当一个局域网当中练的主机数量太多了这时候就需要使用交换机了。无论在家庭当中还是学校我们都是在一个局域网当中进行通信,当我们距离变长的时候此时就需要路由器了让我们通信的距离可以变得更远。
认识协议
什么是协议呢?
1. 协议本身是一种约定。
在早些年,还没有手机,人们打电话只能使用座机来进行交流,座机有一个特点:拨号的人不用付花费,接电话的人需要花钱且花费特别贵。假如你现在是一个大学生 ,你需要离家千里去上大学,你就跟家里人说:爸, 现在电话费太贵了,这样把每次我打电话回来的时候,响一声代表报平安的意思,两声就是我没钱了,三声是我有事需要很你们商量。
那么有了这一个约定的时候,日后你和家里人通信的时候只需要按照这个约定来执行就可以了。所以协议就是一个约定。
对于计算机而言也是有约定的,计算机传输媒介主要是光信号和电信号,通过"频率"和"强弱"来表示0和1(计算机内部只有二进制),想要传递不同的信息,就要约定双方的数据格式。
思考:要想让两台主机能够互相通信,定制这样的协议就够了吗?
你用频率表示0和1,我用强弱表示0和1,就好比我说中国话,你说英语,虽然我们可能遵守同一套通信规则,但还是不能互相通信。
所以这个时候就需要有人站出来指定更多的更细致的协议,让所有参与者都遵守这个规则。
2. 计算机内部需要海量的协议:
- 硬件可能来自不同的厂商
- 操作系统可能也不同
对于计算机而言光是硬件做约定是不行的,上层还有各种软件。以磁盘为例:我们OS访问磁盘是读磁盘还是写磁盘?是按照4字节为单位读,还是8字节?所以计算机内部硬件与硬件,硬件与软件都是有协议的,协议无处不在。
那什么是协议呢?
协议其实就是软件,人们把协议定出来,而实现的时候其实就是硬件的驱动程序和接口软件,使不同的硬件和软件可以按照统一的标准进行交互。
让不同的设备通过一个共同的标准能够通信,这就是网络协议。
以下是文心一言生成结果:
问:能定制协议标准的组织或者公司
答:
组织
- IEEE(电气和电子工程师协会)
- IEEE是一个由计算机和工程领域专家组成的庞大技术组织。
- 在通信协议领域贡献突出,制定了全世界电子、电气和计算机科学领域约30%的标准。
- 其中包括IEEE 802系列标准,涵盖了从局域网(LAN)到广域网(WAN)等多种网络技术。
- ISO(国际标准化组织)
- ISO是由多个国家的标准化团体组成的国际组织。
- 在开放系统互连(OSI)模型方面的工作尤为著名,定义了网络通信的七层协议结构。
- 尽管OSI模型在实际应用中不如TCP/IP协议族普遍,但它在学术和理论研究中仍占有重要地位。
- ITU(国际电信联盟)
- ITU是联合国下属的专门机构,负责制定电信领域的国际标准。
- ITU-T制定的标准涵盖了电话和网络通信,与ISO合作确保了通信技术的全球兼容性和互操作性。
- ETSI(欧洲电信标准学会)
- 由欧洲共同体各国政府资助,是一个由电信行业的厂商与研究机构参加并从事研究开发到标准制定的组织。
- 3GPP(第三代合作伙伴计划)
- 由众多公司和机构组成的为制定通信标准的标准化组织,包括欧洲的ETSI、日本的ARIB和TTC、中国的CCSA等。
- 负责制定移动通信标准,如5G标准等。
- IETF(互联网工程师任务组)
- 负责开发和推广互联网协议(特别是构成TCP/IP协议族的协议)的志愿组织。
- 通过RFC发布新的或取代老的协议标准。
公司
一些公司也具备自研各种标准软件协议栈的能力,并可进行定制化改动,以满足特定需求或推动技术发展。例如:
- 泰凌微:自研了低功耗蓝牙、Zigbee、Thread及Matter等标准的软件协议栈,并可进行定制化改动,这是其核心竞争力之一。
此外,还有一些其他公司或组织也可能具备定制协议标准的能力,但具体取决于其在相关领域的专业知识和技术实力。
需要注意的是,定制协议标准是一个复杂而严谨的过程,需要充分了解行业需求和技术发展趋势,同时还需要遵守相关法律法规和国际标准组织的规范。
网络协议是分层的
协议本身也是软件,在设计上为了更好的进行模块化与解耦合也是被设计成层状结构。(任何软件都是层状结构的)

我们分为两种视角来看待协议是分层:
- 小白视角
在我们普通人看来在我们通话的时候我们本质是直接与对方进行交流。
- 工程师视角
而其实我们是通过电话与对方的电话通信才能交流的,当我们想要换一种方式的时候,只需要更换下层的支持即可,像这样对下层的修改不会影响到上层就是认为用户和底层支持是解耦合的。(可维护性)
所以我们写程序通常追求高内聚低耦合,在同一层当中我们互相联系,但是不同层关系很小。
OSI七层网络协议模型
O S I : ( o p e n s y s t e m I n t e r c o n n e c t i o n , 开放式交互系统 ) OSI: (open\ system\ Interconnection\ ,\ 开放式交互系统) OSI:(open system Interconnection , 开放式交互系统)七层网络模型称为开放式系统互联参考模型, 是一个逻辑上的定义和规范 。

其实在网络角度上,OSI七层模型非常非常的完美,但是在实际操作的过程中, 会话层、 表示层是不可能接入到操作系统中的, 所以在工程实践中, 最终落地的,只有5层协议。(定协议的和代码实现的可能不是同一拨人)
TICP/IP 五层(四层)协议模型
TCP/IP 是一组协议的代名词, 它还包括许多协议, 组成了 TCP/IP 协议簇, 以下是五层的内容:
- 物理层:负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、 早期以太网采用的的同轴电缆(现在主要用于有线电视)、 光纤, 现在的 wifi 无线网使用电磁波等都属于物理层的概念。 物理层的能力决定了最大传输速率、 传输距离、 抗干扰性等. 集线器(Hub)工作在物理层.
- 数据链路层:负责设备之间的数据帧的传送和识别. 例如网卡设备的驱动、 帧同步(就是说从网线上检测到什么信号算作新帧的开始)、 冲突检测(如果检测到冲突就自动重发)、 数据差错校验等工作. 有以太网、 令牌环网, 无线 LAN 等标准. 交换机(Switch)工作在数据链路层。
- 网络层:负责地址管理和路由选择. 例如在 IP 协议中, 通过 IP 地址来标识一台主机, 并通过路由表的方式规划出两台主机之间的数据传输的线路(路由). 路由器(Router)工作在网路层.
- 传输层:负责两台主机之间的数据传输. 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标主机。
- 应用层:负责应用程序间沟通, 如简单电子邮件传输(SMTP) 、 文件传输协议(FTP) 、 网络远程访问协议(Telnet) 等. 我们的网络编程主要就是针对应用层 。

所以OSI七层模型和TIP/IP协议本质其实是,TCP/IP把OSI七层模型的前三层压缩为一层。
为什么会有TCP/IP协议呢?

对于同一个局域网当中的主机来说是可以进行通信的,但是当距离变长的时候可能就会出现一下问题:
- 我先要发给主机C,得先发给路由?怎么办? (物理层|链路层)
- 网上那么多台主机,怎么定位并找到主机C呢? (网络层)
- 如果我发送的数据丢失了怎么办?(传输层)
- 发送数据不是目的,而是手段。用数据才是目的吧?(应用层)
而对于这些问题又是不同种类的问题,所以问题是不同种类的决定了解决方式也是不同的,所以我们协议才是分层的。
所以为什么要有TCP/IP协议呢?
- TCP/IP协议本质是解决问题的方法
- TCP/IP协议分层是因为问题本身就是分层的。
那么为什么需要TCP/IP协议呢?本质就是通信距离变远了。
网络与OS的关系
我们知道计算机的结构是按照冯诺依曼体系设计的。不妨我们脑洞大一点,把假设现在输入设备和输出设备还有CPU分别相离很远的距离,计算机内部的硬件是通过数据总线进行连接通信的,假设这个数据总线无限长,那么此时我们输入设备输入数据,输出设备不久可以接受数据并把它输出到屏幕了吗?所以冯诺依曼体系就是网络!!。
那么网络和操作系统有关系吗?

对于每一个操作系统而言,ISO组织定义了TCP/IP协议,无论是Linux还是Windows系统,如果你们想要实现网络通信,他们就必须按照协议实现自己的网络协议栈。同时不同系统的网络协议栈实现应该相同(按照协议实现),这样才能保证不同系统可以互相通信。
如果上图:
- 应用层:用户自己实现
- 传输层与网络层:操作系统内核中实现
- 数据链路层:驱动程序中实现
- 物理层:硬件

不同操作系统之间的代码一样吗? 一样!! 因为我们按照相同的协议实现的,代码设计的功能也是一样的。
如上图,假设我们现在主机A要跟主机B进行通信,那么此时我们主机A发送的struct data认识吗?认识!! 所以对于不同的主机而言,由于我们遵守相同的协议,所以我们天然就可以识别不同主机发送过来的消息。
对于操作系统而言,我们计算机内部的协议是海量的,那么OS需不需要对这些协议进行管理呢?需要,如何管理呢? -》 先描述再组织!! 。
所以协议本质就是操作系统中的结构体。
关于协议的朴素理解:协议就是通信双方都认识的结构体数据。
为什么要有网络协议栈呢?
我们知道操作系统本质是由C语言写的,而网卡是硬件,当我们用户需要访问硬件的时候本质是调用系统调用,让操作系统去执行对应的方法从而访问网卡,发送数据。所以为什么要有网络协议栈呢?本质是因为我们用户想要访问硬件,必须通过系统调用从用户层逐层逐层的向下访问。
网络传输基本流程
1. 局域网网络传输流程图 (以太网)

这里先要知道,在同一个局域网当中,不同主机可以直接通信。局域网有很多中,这里的以太网只是其中一种。
假设当前我们主机A要给主机E发消息,那么主机E是如何知道主机A给他发消息了?其实当我们主机A发消息当以太网当中的时候,在这个局域网当中的所有主机全知道了。
🌰:
假设一个班级共有30名学生,以老师点名为例子,当老师在讲台上叫张三回答问题的时候,其实这个班当中所有人都听到了,但是为什么只有张三站起来了呢?因为在老师说出张三的时候,地下的同学都接受到了张三这个信号,他们会与自己的姓名做对比,不是就不管他。
那么计算机是如何对比的呢?其实每一台都有标识唯一性的地址,这个地址我们叫做MAC地址。 通常是在计算机生产出来的时候默认就有的一个序列。

如上图,对于其他主机而言,当判断到地址不匹配的时候,就是就直接丢弃了,所以上层用户可能都不会知道主机收到过这个数据。
同时在以太网中,只允许存在一次有效数据,也就是说以太网本身是一个临界资源 ,当主机A在跟主机E通信的时候,如果有其他主机也要发消息,那么就会发生碰撞,此时就会进行碰撞检测 和碰撞避免, 当两个网络碰撞的时候会各自休眠一段时间,然后再重新发。(我们也管以太网叫做碰撞域)
那么如何进行碰撞检测呢?对于计算机的硬件而言每一次发送信号本质都是通过高电压和低电压发送的,高电压的数据往低电压走,当发生碰撞的时候电压不就更高了吗,当超过一定阈值的时候就会让两台主机进行休眠。
2. 解包和应用

如上图,对于一个数据而言当我们用户从应用层发送的时候,每一次都会进行一次的封装。

对于网络中的数据我们可以重新定义一下了,数据 = 报头 + 有效载荷。
同时由于我们都是遵守同一个协议的,所以不同主机之间都可以识别对应的报头,我们管着叫做解包。 那么什么是分用呢?我们每一层当中其实有大量的协议,那么我们发上来的数据究竟发送给哪一个协议呢?同时已经解包过的报头如何和剩余的数据进行分离呢?
所以:在每一个报头当中一定会存在一些字段,这些字段明确了当前报头的长度(方便分离) ,以及有效载荷向上传递的协议究竟是哪一个!。
在同一层当中,主机A发送给主机B的数据经过解包和分用会变为相同的数据格式(当前的报头+有效载荷)。
跨网域传输
MAC地址 VS IP地址:
- MAC地址是在局域网当中区分主机唯一性的标识。
- IP地址是在网络当中区分不同主机唯一性的标识。
在跨网域传输的过程中,(唐僧西游取经例子)。
- MAC地址一直在变化,而IP地址始终不变
- 局域网通信需要MAC地址
跨网络通信流程图:

同时跨网域传输数据需要通过路由器发送。
- 问题一: 为什么需要交给路由器呢?
任何主机只要有网络层,就会有一个IP地址的概念,当我们要往另一个子网发送消息的时候,主机会进行判断,发现目的IP地址和我们自己的MAC地址不在同一个子网当中,这个时候就需要把报文先交给我们的路由器。
- 问题二:怎么交给路由器
路由器现在功能很全面,举个例子:当我们想要连接wifi的时候,第一步就是连接路由器,所以我们其实是已经知道了路由器的IP地址,而现在的路由器一般在我们连接的时候,就会自动帮我们的主机分配IP地址,所以我们主机的IP地址其实是路由器给我们的。
对于两台主机而言,路由器是什么呢?
也是主机!!!,网卡1也有自己的mac地址,他跟我们的主机A实在同一个局域网当中,同理主机B也是一样的。所以主机A和主机B就可以直接与路由器通信。
具体传输流程:

如上图,当我们报文进行传递的时候,Mac地址其实是一直在变化的,但是srcIp->desIp
始终不变。
MAC地址注意事项:
MAC一台主机可能用多个(一台主机可以绑定多张网卡) => 路由器就是这样的。
MAC长度为48个比特位,6字节。一般以 16进制:16进制...这样显示的。
- ether(以太): 00:16:3e:0c:d3:de
为什么要有IP地址呢?
在网络的发展当中局域网先被科学家们发明出来,所以肯定会有不同的局域网实现,如以太网、令牌环网、无线局域网。我们也知道在发展的过程中,当我们把自己的局域网搞出来之后,肯定也会想要让多个局域网直接可以互相通信,那么对于不同的局域网,如何让不同的局域网可以互相通信呢?
令牌环网的实现原理(简单理解):当一个主机拿到类似令牌(特殊处理的代码)的时候,只能有当前主机发送消息,其他主机不能发送消息,知道主机把消息发送目的主机。总之谁拿到令牌,说就可以发送消息,跟加锁的概念类似。
计算机当中的任何一个问题都可以通过添加一层软件层来解决。
所以我们在局域网之上封装了IP,当我们在路由器当中寻找目的IP地址的时候,我不关心局域网是怎么实现的,同一使用封装好的IP地址。所以IP地址屏蔽了底层不同局域网的差异。
对于MAC地址而言就是为了在局域网当中进行通信。
所以最终我们网络通信流程其实是这样的一个图(中间会经过多个路由器):


socket编程
端口号
端口号是一个 2 字节 16 位的整数 。
- 0 - 1023: 知名端口号, HTTP, FTP, SSH 等这些广为使用的应用层协议, 他们的端口号都是固定的.
- 1024 - 65535: 操作系统动态分配的端口号. 客户端程序的端口号, 就是由操作系统从这个范围分配的
数据从主机A发送到主机B是网络通信的目的吗?只是手段而已。真正的目的是用户想要得到数据 (QQ收到对方的消息)。而对于计算机而言,用户操作其实是一个一个的进程在操作。

所以本质上是用户想要得到某些数据。可是上层可能有几百个进程,我发送过来的数据如何知道该给哪一个进程呢?

所以在我们传输层的报头当中,会有源端口号 和**目的端口号,**内核当中可以以哈希表的方式以{'端口号' : 'PCB地址'}
的方式确定此时这个数据应该给哪一个进程。
所以在跨网络通信中,我们可以以IP地址 + 端口号的方式找到指定主句的指定进程。这样就可以实现网络通信了。其实对于用户而言我们认为是在自己的应用层与对方的应用层直接通信,**本质可以把网络通信看作是进程通信。**进程之间独立的不能在独立了,访问的也是同一个数据吧?那么其实我们也可以使用网络进行本地间通信。
那么为什么不用进程ID来标识让我们的报头来标识到底要上传给上层的哪一个进程呢?这样的话网络和OS耦合度太高了,维护性差,所以才使用的端口号。
我们把IP地址 + 端口号 的方式找到指定主句的指定进程这种方式叫做套接字
(socket )

传输层的典型代表
当我们想要进行网络编程的时候,由于网络本身就是在OS中实现的,所以不可避免的要去调用系统调用接口,而与用户操作最近的则是传输层,所以我们要通过网络协议栈进行通信,必定调用的是传输层提供的系统调用,来进行的网络通信 。

在传输层当中有两个常用的协议,TCP(Transmission Control Protocol 传输控制协议)
和UDP(User Dategram Protocol 用户数据协议)
。
TCP协议特点:
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
UDP协议特点:
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
可靠传输: 假设一个公司大老板让两个人办事,一个人是TCP另一个人是UDP,TCP办事比较靠谱,有啥问题都先自己给你解决好了,而UDP就像是一个传话筒,直接把事情交给下层去做了。
面向字节流: 就像是家里的自来水,你不知道自来水公司一次性给你供多少水,你也不在乎,可以一杯一杯的用,也可能一桶一桶的用,数据写入多少和我读取没有关系,我怎么读都可以。
面向数据报: 如果写入了10字节的数据,读端只能按10字节的方式进行读取
对于这两种协议我们不能以好坏来评判,网络发展了那么多年了,把这两个保留了下来,肯定有它的道理的,TCP靠谱一点,那么实现的时候会复杂一点,需要的资源也更多,UDP需要的资源可能相对较少。在实现转账等功能的时候数据不能够丢失那我们就可以使用TCP协议,但是像直播的话,偶尔丢包也不会有太大的影响,那我们就可以使用UDP协议。
从代码层面理解什么是有连接与无连接:
- Udp只需要绑定之后直接发送数据即可 (无连接)
- Tcp绑定完之后还需要 accept --> (有连接)
网络字节序
我们知道在计算机内部,如果涉及多字节存储的话,那么会有如何存储的问题,所以我们计算机分为大端存储和小端存储。
- 大端存储:高字节位的放在内存的低地址处,低字节位的放在内存的高地址处。
- 小端存储:低字节位的放在内存的低地址处,高字节位的放在内存的高地址处。
以0x11223344为例子""11""是在高字节位,大端存储就放在低地址处。小端存储则相反。
sockerAPI接口介绍
plain
// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);
// 开始监听 socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
从上述接口当中我们可以发现一个特点,那就是都会有一个struct sockaddr
这个东西,那他究竟是什么呢?

对于网络编程而言,一般都是socket套接字编程,socket有很多种:
- 网络socket
- 本地socket (unix域间scoket) 本地通信
那么每一种通信方式都需要设计一些接口的话就会有太多重复的接口,这样对用户使用不好,所以设计者就在想能不能设计同一的接口呢?所以才会有 struct sockaddr
。
struct sockaddr_in
专门用来网络通信 ++(in 是 inet的缩写)++struct sockaddr_un
用来本地通信的。++(un 是 unix的缩写)++
对于不同接口而言都是传入的 sockaddr
如果你想要网络通信,那么就强转为struct sockaddr_in
。虽然接口上是统一的,但是底层也需要区别到底是网络通信还是本地通信,所以这两种接口体头部都会有 16位地址类型,它表示着究竟是哪种通信方式。