1. 计算机网络背景
网络的本质是获取数据,OS的本质是加工处理数据。
网络发展
计算机一开始被用于军事,军事实验室里很多人,每个人的电脑都是独立的。大家分工协作,每个人处理的细节各不相同。协作的本质是对一份数据或者多份数据进行维护。假设有A,B,C三个终端,A终端的数据需要传输到B终端,需要先拿U盘对A终端的数据进行拷贝,才能拿到B终端。这样很麻烦。
这就是计算机最早所形成的模式——独立模式:计算机之间相互独立。
之后,研究员们发现,我们可以拿一台计算机,专门用于保存数据,并且建立共享数据库,将其他计算机都连接起来,这样每台计算机需要数据,都可以直接从那一台共享数据库的计算机上获取。如果访问之后,修改了数据,再写回到共享的计算机上。
这里专门用于保存数据,并建立共享数据库,供其他计算机访问使用的,那台计算机,就是服务器。
这就是早期的网络互联:将多台计算机连接在一起,完成数据共享;
后来啊,实验室内部的数据交换解决了,但是实验室与实验室之间也需要数据交换啊,研究员们为了解决这个问题,就提出了交换机,路由器的概念,将一个实验室看作是一个子网,实验室与实验室之间的连接,看作子网之间的连接,就有了局域网(LAN)。
后来,随着网络与科技的发展,城市与城市之间,国家与国家之间也需要通信,那么办?
继续扩展呗,我们可以把城市,国家看作一个更大的局域网,这样局域网与局域网之间的连接,就形成了广域网(局域网与广域网两个概念没有明确的界限划分)。
所谓 "局域网" 和 "广域网" 只是一个相对的概念. 比如, 我们有 "天朝特色" 的广域网, 也
可以看做一个比较大的局域网.
这就是网络的大致发展,了解一下就可以了。
计算机是人的工具,人之间要协同工作,就注定了网络的产生是必然的。
2. 初识协议
协议本身是用来解决问题的。我们要理解协议,就需要先理解问题,这些问题是由什么原因导致的,然后针对问题,提出协议。
网路的规模是很大的,我们要将一台计算机的数据,传输到千里之外的另一台计算机上,依靠的就是子网与子网之间的通信,再细化就是主机与主机之间的通信,那么,首先,我们要保证的就是数据能够准确无误的传输到下一台设备上,所以,我们遇到的第一个问题就是,我们要怎么保证数据能够准确无误的到达下一个设备呢?
这个问题我们先往一边,我们继续想,假设现在我们已经能够保证数据能够准确到达下一个设备了,那么这么多主机,子网与子网之间,该怎么定位哪台主机是目标主机呢? 传输距离这么长,如果在传输途中数据丢失了,怎么办?
传输数据的最终目的,是处理数据,我们又该如何处理发来的数据呢?
针对不同的问题,我们就提出了不同的协议来解决所对应的问题,对于如何处理发来的数据,相应的协议就有 https,http,ftp,smtp等。对于对于长距离传输的数据丢失问题,相对应的就是tcp协议,utp协议等。对于如何定位主机位置的问题,相对应的就有ip,mac协议等。
每台计算机的结构是类似的,那是什么导致了这些问题呢?就是单纯的长距离传输。
那协议到底是什么呢? 协议是一种约定,就像是打电话时,我们约定电话铃声响三声代表什么什么的约定。
不过计算机之间的传输媒介是光信号和电信号,通过“频率”和“强弱”来表示0和1这样的信息,要想传递不同的信息,就需要约定好通信双方的数据格式。
思考,只要通信的两个主机,约定好协议就可以了嘛?
定好协议了,但是你用频率表示01,我用强弱表示01,就好比我用中国话,你用葡萄牙语一样,虽然大家可能遵循的是同一套通信规则,但是语言不通,即便是订好了基本的协议,也是无法正常通信的。
所以,完善的协议,需要更多更细致的规定,并且能够让参与的人都遵守。
这就要面临一个问题了:
计算机的生产厂商有很多,计算机的操作系统也有很多,计算机的网络硬件设备还是有很多,如何让这些不同厂家之间生产的计算机能够相互顺畅的通信呢?这就需要有人站出来,约定一个共同的标准,大家都来遵守,这就是网路协议!
一般具有制定协议或者标准 的资格的组织或者公司都必须是业界内公认或者具有江湖地位的组织或者公司,要不然人家并什么听你的呢。
下面是文心一言生成的标准制定组织,大家看一下就可以
问: 能定制协议标准的组织或者公司
答: 能定制协议标准的组织或公司主要有以下几类:
1. 国际标准化组织:
- IEEE(电气和电子工程师协会) : 这是一个由计算机和工程领域专家组成的庞大技术组织, 在通信协议领域贡献突出。 IEEE 制定了全世界电子、 电气和计算机科学领域 30%左右的标准, 包括 IEEE 802 系列标准, 这些标准涵盖了从局域网(LAN) 到广域网(WAN) 等多种网络技术。
- ISO(国际标准化组织) : ISO 是由多个国家的标准化团体组成的国际组织, 它在开放系统互连(OSI) 模型方面的工作尤为著名。 OSI 模型定义了网络通信的七层协议结构, 尽管在实际应用中, TCP/IP 协议族更为普遍, 但 OSI 模型仍然在学术和理论研究中占有重要地位。
- ITU(国际电信联盟) : ITU 是联合国下属的专门机构, 负责制定电信领域的国际标准。 ITU-T 制定的标准涵盖了电话和网络通信, 与 ISO 合作确保了通信技术的全球兼容性和互操作性。
2. 区域标准化组织:
- ETSI(欧洲电信标准学会) : 由欧洲共同体各国政府资助, 是一个由电信行业的厂商与研究机构参加并从事研究开发到标准制定的组织。
- ASTAP(亚洲与泛太平洋电信标准化协会) : 1998 年由日本与韩国发起成立的标准化组织, 旨在加强亚洲与太平洋地区各国信息通信基础设施及其相互连接的标准化工作的协作。
3. 公司:
- 某些公司, 如泰凌微, 也自研各种标准的软件协议栈, 包括低功耗蓝牙、zigbee、 thread 及 Matter 等, 并可进行定制化改动, 这是其核心竞争力之一。 泰凌微还计划重点发展智能电子价签、 智能遥控、 智能家居等市场。
4. 民间国际团体:
- IETF(互联网工程师任务组) : 这是一个负责开发和推广互联网协议(特别是构成 TCP/IP 协议族的协议) 的志愿组织, 通过 RFC 发布新的或者取代老的协议标准。
5. 官方机构:
- FCC(联邦通信委员会) : 美国对通信技术的管理的官方机构, 主要职责是通过对无线电、 电视和有线通信的管理来保护公众利益。 也对包括标准化在内的通信产品技术特性进行审查和监督。
以上这些组织或公司都能在一定程度上定制协议标准, 以满足特定需求或推动技术发展
协议的理念我们了解了,那么协议的具体表示是什么样的呢?
我们以快递为例:我们网购的物品,最后都会发到我们家里,那么快递员是如何知道物品该发往哪的? 通过快递单信息。 假设我们买的是洗面奶,我们收到的只是洗面奶物品吗?实际上我们收到的是一个纸盒,纸盒里面装着洗面奶。除去洗面奶,那些多出来的信息字段(快递单)就叫协议,准确来说是协议的报头,那个纸盒就是发送内容,它可以是任何东西,而报头一直都是那种形式。
协议就是对报头的制定,
协议分层
协议本质上也是软件,在设计上为了更好的进行模块化,解耦合,也是被设计成为了层状结构的。
软件分层的好处
我们以打电话举例,来说明
我们在打电话的时候,逻辑上是人和人,电话和电话在进行沟通,但实际上是人和电话在沟通。
在这个例子中,我们的“协议”只有两层:语言层、通信设备层。
实际上的网络通信协议,设计的会更加复杂,需要分更多的层,但是通过上面的简单例子,我们是能理解,分层可以实现解耦合,让软件维护的成本更低。
那么协议是分层的呢? 一在网络通信时,软件功能,结构规模太大了,需要分层管理,便于维护。比如继承就是典型的分层,你修改子类并不会影响父类。二是协议是用来解决问题的,而不同性质,不同种类的问题有很多,那么我们解决问题呢? 让问题分类分层解决,所以协议也就需要分层了,
同一层,我们可以抽象笼统的认为是直接通信的,但是我们必须要清楚,同层协议并没有直接通信,而是各自使用下层提供的结构能力完成的通信。
那么协议是具体怎么分层来解决问题的呢? 这就涉及到我们后面要将的OSI七层模型,和TCP/IP四层模型了。
OSI七层模型
针对网络通信的各种类型,各种性质的问题,ISO组织就提出了
OSI(Open System Interconnection 开放系统互联)七层网络模型,将问题,协议拆分为7层,物理层,数据链路层,网络层,传输层,会话层,表示层和应用层七层。
需要注意的是 OSI 是一个逻辑上的定义和规范。
- OSI将网络从逻辑上分为了7层,每一层中都有相关、相对应的物理设备,比如路由器、交换机;
- OSI 七层模型是一种框架性的设计方法,其最主要的功能就是帮助不同类型的主机实现数据传输。
- 它的最大优点是将服务、接口和协议这三个概念明确地区分开来,概念清楚,理论也比较完整,通过七个层次化的结构模型使不同的系统不同的网络之间实现可靠的通信;
但是,它的不足是它既复杂又不实用,所以我们还是按照TCP/IP四层模型来讲解,TCP/IP模型才是真正实施到了现实商用上,OSI只是一种理论。
其实在网络角度上,OSI定的协议7层模型其实非常完善,但是在实际操作的过程汇总,会话层和表示层是不可能接入到操作系统中的,所以在工程实践中,最终落地的就是5层协议。
但是,要理解上面的话,需要我们学习完网络才可以理解,这里就知道就可以了。
TCP/IP 五层(或四层)模型
TCP/IP是一组协议的代名词,它还包括许多协议、共同组成了TCP/IP 协议族,在五层协议中,最重要的两层就是网络层和传输层,而网络层中最具代表性的协议是IP协议,传输层最具代表性的是TCP协议,由此,我们就把这一套协议称之为TCP/IP协议簇。
TCP/IP通讯协议采用了5层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。
- 物理层: 负责光/电信号的传递方式. 比如现在以太网通用的网线(双绞 线)、 早期以太网采用的的同轴电缆(现在主要用于有线电视)、 光纤, 现在的 wifi 无线网使用电磁波等都属于物理层的概念。 物理层的能力决定了最大传输速率、 传输距离、 抗干扰性等. 集线器(Hub)工作在物理层.
- 数据链路层: 负责设备之间的数据帧的传送和识别. 例如网卡设备的驱动、 帧同步(就是说从网线上检测到什么信号算作新帧的开始)、 冲突检测(如果检测到冲突就自动重发)、 数据差错校验等工作. 有以太网、 令牌环网, 无线 LAN 等标准. 交换机(Switch)工作在数据链路层.
- 网络层: 负责地址管理和路由选择. 例如在 IP 协议中, 通过 IP 地址来标识一台主机, 并通过路由表的方式规划出两台主机之间的数据传输的线路(路由). 路由器Router)工作在网路层.
- 传输层: 负责两台主机之间的数据传输. 如传输控制协议 (TCP), 能够确保数据可靠的从源主机发送到目标主机.
- 应用层: 负责应用程序间沟通, 如简单电子邮件传输(SMTP) 、 文件传输协议(FTP) 、 网络远程访问协议(Telnet) 等. 我们的网络编程主要就是针对应用层.
物理层我们考虑的比较少,我们只考虑软件相关的内容,因此很多时候我们直接称为TCP/IP四层模型。
我们平时用的网线,叫双绞线。这是物理层的概念。传输使用的是光纤,由于远距离传输,信号会随之衰减,所以,传输是有限度的。为了提升传输的距离,就有了中继器,集线器,用于放大信号。中继器,集线器工作在物理层。
物理层确保真正物理意义上的传输电信号的准确。
集线器是中继器的一种,可以看作为多端口的中继器,这里再了解下543原则。
543原则是网络工程中的概念,即 在以太网组网中,“543 原则” 规定一个网段中最多可以使用 4 个中继器或集线器,来连接 5 个网段,其中只有 3 个网段能连接计算机等网络设备 。这是为避免网络信号因过多中继设备产生延迟、冲突等问题,保障网络正常运行。
数据链路层,解决同一局域网数据帧的传递和识别问题。我们认识的交换机,工作在这一层。
链路层确保数据的传输,那你怎么知道该把数据交给谁?这个问题就是由网咯层来解决的。我们所熟知的路由器就工作在这一层。
网络层,正确的选择传输路径,确保数据能够到达目标终端。
数据传输的过程出现了问题,怎么办? 传输层会进行重新传输。
传输过来的数据还是一堆二进制位怎么办,得转成我们想要的数据形式才有意义。怎么转? 这个问题就由应用层来解决。
便随着计算机的增多,网络拓扑结构越来越复杂,需要处理的问题也越来越复杂。为了处理这些复杂的问题,诞生了路由器,交换机和集线器等设备。路由器工作在网络层,不是说它只能处理网络层的问题,而是它集齐了网络层之下的所有功能。也就是说,路由器还具有交换机和集线器的功能。
注:一般说某设备工作在某层,说的是这个设备实现了本层及其下层的所有功能,所以我们所使用的应用是应用层的,所以我们所使用的主机,一定得实现了应用层以及下层的所有功能,这也就贯穿了整个协议栈。
一般而言
- 对于一台主机,它的操作系统内核实现了从传输层到物理层的内容;
- 对于一台路由器,它实现了从网络层到物理层;
- 对于一台交换机,它实现了从数据链路层到物理层;
- 对于集线器,它只实现了物理层;
但是并不绝对,也存在有工作在网络层的交换机,也具备了网络层的转发能力,很多路由器也实现了部分传输层的内容(比如端口转发);
3. 再识协议
上面的内容,我们只是懂了一些基本概念,还是达不到我们的目标,下面我们再次重新理解协议和协议分层。
为什么要有TCP/IP协议?
首先,即是是单机,你的计算机内部,其实也是存在协议的,比如:其他设备和内存通信,会有内存协议。其他设备和磁盘通信,会有磁盘相关的协议,比如:SATA,IDE,SCSI等,只不过我们感知不到罢了。而且这些协议都在本地主机各自的硬件中,通信的成本、问题都比较少。
其次,网络通信的最大的特点就是主机之间变远了。任何通信特征的变化,一定会带来新的问题,有问题就得解决问题,所以就需要新的协议了。
所以,为什么要有TCP/IP协议?本质就是通信主机距离变远了。
什么是TCP/IP协议?
TCP/IP协议的本质是一种解决方案,TCP/IP就是协议分层解决传输网络问题的具体模型。
TCP/IP协议能分层,前提是因为问题本身能够分层!!
TCP/IP协议与操作系统的关系(宏观上,怎么实现的)
看完下面的图你就明白了。网络层,对应的就是硬件设备网卡。数据链路层由软件驱动自己完成。实际上,数据链路层在软件上就属于网卡驱动的一部分。每台Linux机器的网路功能都是自带的,所以网络驱动程序都是有的。
传输层和网络层是必须在系统内核中实现的,无论OS再怎么不同这部分大家必须遵守相同的协议,必须一样。
传输层和网络层会提供一些系统调用给我们来使用。网络层的系统调用比较复杂,我们通常只会使用传输层的系统调用。
传输层的接口,主动并不会处理数据,一些大神,就对这些接口做封装,封装成http,smtp,DNS,用于处理不同的数据。于是就有了应用层,应用层在OS的上层,由用户实现。
这就是整个网络协议栈与整个OS实现的细节对应,可以看出,整个协议栈,既涉及到硬件,又涉及到驱动,还涉及到OS,甚至用户,所以和这个协议一定是需要IT各行各业都需要支持和配合的。
在我们现实生活中进行网络通信的时候,我们发现不同的操作系统之间也是可以通信的,这不就和我们上面说的相悖了吗?
实际上,所有的主机上安装的操作系统可以不同,事实上也确实是不同的,但是所有主机上的协议栈都必须按照标准进行相同的实现,这就是为什么不同的主机,可以相互通信的秘密!
同理,这也就解释了为什么我们会看到Windows,Linux等不同的OS书籍,但是计算机网络却只有一种的现象。
那两台计算机是怎么进行网络通信的呢? 通过网卡、网络通信本质上是在访问硬件。用户能直接访问网卡吗? 不能。需要通过系统调用,所以网络通信的本质就是一个贯穿协议栈的过程。
所以究竟什么是协议?
截止到目前,我们还没接触过任何协议,但是如何朴素的理解协议,我们已经可以试试了。
OS源代码一般都使用C/C++语言写的。
下面,仔细看看下面的图
问题: 主机B能识别data,并且精准提取a=10,b=20,c=30吗?
回答: 答案是肯定的! 因为双方都有相同的结构体类型 struct protocol。也就是说,用同样的代码实现协议,用同样的自定义数据类型,天然就具有“共识”,能够识别对方发来的数据这不就是约定吗?
关于协议的朴素理解:所谓协议,就是通信双方都认识的结构化的数据类型。
因为协议栈是分层的,所以,每层双方都有协议,并且同层之间,互相可以认识对方的协议,这也就造成了我们感觉同层之间是直接进行通信的假象。
4.网络传输基本流程
局域网(以太网为例)通信原理
首先,我们思考一个问题,两台主机在同一个局域网,是否能够直接通信? 答案是 当然!
局域网通信标准中常用的标准是以太网。
接下来,我们就来介绍下局域网(以太网)主机之间的通信原理,这里我们来引入一个小故事来进行讲解。
上课了,教室里坐满了学生,有一个学生叫张三,有一个学生叫李四。
讲台上的老师说,张三,你来回答这个问题。只有张三站起来了,但是其他同学却没有站起来,为什么? 其他同学没听到吗? 当然听到了,但是老师叫的是张三,而不是我。这个故事体现到局域网通信上,就是每台主机在局域网上,都有自己的唯一标识,一台主机向局域网发送数据,局域网中的主机都会收到该数据。只不过,这个数据并不是发给自己的,所以丢弃了。
那么在局域网中那个标识自己唯一性的标记是什么呢?
mac地址,每台主机在局域网上都会使用唯一的mac地址来保证主机的唯一性
认识MAC地址
在网络通信中,我们提到过最后所有的数据都是通过物理层来进行接受,再依次向上交付到达用户的,网卡就是工作在物理层的,而每个网卡都有一个48bit位的序列号,这个序列号就是MAC地址,全球唯一,由全球各个生产厂家以及组织来进行维护,但MAC地址其实只需要局域网唯一就可以了。
MAC地址通常用来识别数据链路层中相连的节点。
MAC地址长度48为,也就是6个字节,MAC地址的表示,一般使用16进制数字加上冒号的形式(冒号十六进制)来表示例如 08:00:27:03:fb:19。
MAC地址通常在网卡出厂时就确定了,不能修改,MAC地址通常是唯一的(虚拟机中的MAC地址不是真实的MAC地址,可能会有冲突;也有些网卡支持用户自己配置MAC地址)
后续我们在详细谈论数据链路层的时候,会谈MAC帧协议,此处我们做一个简单了解即可。
回到我们的局域网通信,假设A主机要发送数据给主机E。各个主机收到消息之后,就会对比目标主机的MAC地址,是不是自己,如果不是自己就丢弃。当然,如果你不想丢弃,也可设置网卡的工作模式为混杂模式,这样就不会被丢弃了。网卡默认的工作模式是正常模式。
在局域网通信的过程中,主机对收到的报文确认是否是发给自己的,是通过目标MAC地址进行判定的。
在同一个局域网(以太网)中任何时刻,只允许一台机器向网络发送数据,如果同时有多台主机在发送,会发生数据干扰,我们称之为数据碰撞,发生碰撞之后,碰撞的双方主机就会执行碰撞避免算法(一般是随机延迟几秒)
所以,所有发送数据的主机都要进行碰撞检测和碰撞避免。在没有交换机的情况下,一个局域网(以太网)就是一个碰撞域。
任一时刻,局域网(以太网)中只会允许一台主机发送数据。为了提高发送效率,人们采用交换机划分碰撞域,交换机将同一个局域网通过接口划分为多个范围,如果通信的两个主机,在同一个范围内,那么就不会向其他接口转发,只会在本范围内广播,这样,就减少了数据碰撞的概率。
我们可以试着从系统的角度来理解下局域网通信原理,
任一时刻只允许一台主机通信。这不就是保护共享资源(局域网)进行互斥访问吗? 只不过这个互斥访问不是通过加锁保护的。
初步明白了局域网通信原理,再来看一个同一网段内的两台主机进行发送消息的过程吧
同一网段内主机通信过程
假设用户A要给用户B发送信息“你好”。首先,数据由应用层产生,然后不断向下交付,直到物理层,网卡端,物理传送到目标网卡上,然后再在对方主机上向上交付,直到应用层。
而其中每一层都是有协议的,所以当我们在进行上述传送交付流程的时候,要进行封装和解包。详细如下图
下面我们来明确下概念。
首先,应用层会给信息加上版本信息,也即是报头,然后,再交给传输层。在应用层,我们的信息“你好为有效载荷,报头+有效载荷=报文。
我们发送信息,先说的你好,再说的吃饭了没。结果传过去是先说的吃饭没,再说你好,这怎么行呢? 为了保证数据的顺序性,传输层,在信息上加上了序号。这里的序号就是报文。此时,上一层,应用层的报头和信息“你好”则是有效载荷。
那么我们怎么知道数据发给谁呢?网络层也会增加一个报头,指明是谁发的信息(src),发给谁(dst)。
每层都有协议,都会要求添加报头,数据链路层也不例外。通信的本质就是封装和解包的过程。
- 报头部分,就是对应协议层的结构体字段,我们一般叫做报头。
- 除了报头,剩下的叫做有效载荷。
- 故,报文=报头+有效载荷。
扩展两个知识:
几乎任何层的协议,都要提供一种能力,能够将报头和有效载荷分离的能力。
那你怎么知道你该传到哪一层的哪一个协议呢?
几乎所有层的协议,都要在报头中提供,一种决定自己有效载荷交付给上层的哪一个协议的能力。后面这种能力,我们称之为分用的过程。
这两个能力是大部分协议的共性,也是网络通信中封装和解包过程的关键。
然后,我们在明确一下不同层对于完整报文的叫法
- 不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做包(bucket),在数据链路层叫做帧(frame)
- 应用层数据痛殴协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装(Encapsulation)。
- 首部信息中包含了一些类似于首部有多长,负载(payload)有多长,上层协议是什么等信息。
- 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,根据首部中的“上层协议字段”将数据交给对应的上层协议处理。
最后,在整体复盘一下:
在网路传输的过程中,数据不是直接发送给对方主机的,而是先要自顶向下将数据交付给下层协议,最后由底层发送,然后由对方主机的底层来进行接受,在自底向上进行向上交付,下面是一张示意图。
数据包封装和分用
下面为数据封装的过程
下面为数据分用的过程
从今天开始,我们学习任何协议,都要先宏观上建立这样的认识:
1. 要学习的协议,是如何做到解包的? 只有明确了解包,封装也就能理解。
2. 要学习的协议,是如何做到将自己的有效载荷,交付给上层协议的?
跨网络传输
网络中的地址管理 - 认识IP地址
IP协议由两个版本,IPv4和IPv6,我们整个系列,凡是提到IP协议,没有特殊说明的,默认都是指的IPv4.
- IP地址是在IP协议中,用来标识网络中不同主机的地址;
- 对于IPv4而言,IP地址是一个4字节,32位的整数;
- 我们通常也使用”点分十进制“的字符串来标识IP地址,例如 192.168.0.1; 用点分割的每一个数字标识一个字节,范围是0 - 255;
跨网段的主机的数据传输,数据从一台计算机到另一台计算机传输过程传输要经过一个或多个路由器。
下面是一张是示意图
令牌网,也是局域网通信标准之一,该标准要求局域网内主机,只有拿到令牌(本质上是一种特殊格式的帧) 才能够发送数据,依次来实现任意时刻只允许一台设备向局域网发送数据。
首先,我们理解一下IP地址的意义,我们要弄明白下面这两个问题
- 为什么要去目标主机,先要走路由器?
- 目的IP的意义
我们怎么知道数据要跨网络传输呢? 答案就是通过IP地址,IP地址能保证全网的唯一性。
MAC vs IP
IP地址的具体含义是什么呢? 举个简单的例子,唐僧师徒四人要从东土大唐去往西天求经,途中一定会路过各个地方,那么唐僧师徒四人是如何保证自己是行走在正确的道路上的呢?
答案就是 通过不断的问路,找到本地的比较熟悉本地线路的人,告诉他我的目的地址,让他来告诉你下一站去往哪里,也就是说我们的上一站和下一站的地址一直在改变,而最终的目标地址是不会发生改变的
映射到我们的网络模型中,师徒四人也就是我们的数据包,东土大唐就是我们的源IP地址,西天就是目的IP地址,这个本地比较熟悉本地线路的人就是我们的路由器,问路的行为,就是路由器的路由选择算法,上一站地址就是源MAC地址,下一站地址就是目的MAC地址。
需要注意的是,我们在上面的故事中比较关键的点是,我们问路的时候,找的是本地的人来问路,那么这个本地的人是怎么找的呢?
结合对于网络模型的映射,我们发现,本地对应的就是源MAC地址所在的范围,而问路所给出的下一站地址就是目的MAC地址,
问路行为,也就是路由器和源主机怎么进行通信? 本质上不就是局域网内通信。
那么,我们又是怎么知道我们需要问路的? 通过IP地址,因为IP地址的不同就表明了目标主机和源主机不在一个局域网里。
由此我们可以得出结论 :
局域网通信,是需要MAC地址的,通过目的IP来判断是否需要路由,并且也是通过目的IP地址来进行路由的。
同理,我们是怎么找的这个问路的人也就是路由器的?这不就正好也就是说明了,我们主机也是具有路由功能的!
通过刚刚的通信过程,我们知道了令牌网和以太网实现的局域网之间,可以相互通信,这是为什么,为什么标准不统一,底层细节不同,也能够相互通信? 这是因为IP地址实现了解耦合,通过IP协议屏蔽了底层网络的差异化,靠的就是工作在Ip层的路由器。
用户A向用户B发送数据,自上而下进行封装,同理,数据链路层也会给数据封装上Mac地址。通过IP地址的比对,发现两个主机不在同一个同一个局域网内,开始跨网段传输,通过自身的路由,找的本地的路由器,路由器会抛弃掉源Mac地址,封装上自己的Mac地址,以及目标网络的报头(如果目标网络不同,对应令牌环网,就是封装上令牌环的报头)发往用户B所在的局域网,经过解包,用户B的Ip层就得到了用户A发来的报文。这个过程中,用户B知不知道以太网到令牌环网的变化? 用户B的Ip层并不知道,也不关心。IP实现了全球主机的软件虚拟层,一切皆是IP报文。
也就是说,网路底层可以不同,只要路由器能够路由,且支持解包和封装,底层的差异就被屏蔽了,上层全部的IP协议,就准许接入。
对比IP地址和Mac地址的区别
- IP地址在整个路由过程中,一直不变(目前,我们先这样说明,后面再修正)
- Mac地址一直在变
- 目的IP是一种长远目标,目的Mac是下一阶段目标,目的IP是路径选择的重要依据,Mac地址是局域网转发的重要依据。
下图就是网络通信的宏观流程
IP网络层存在的意义: 提供网络虚拟层,让时间的所有网络都是IP网络,屏蔽最底层网络的差异。
5. Socket编程预备
1.理解源IP地址和目的IP地址
- IP在网络中,用来标识主机的唯一性
- 注意:后面我们会讲IP的分类,后面会详细阐述IP的特点
但是这里先思考一个问题: 数据传输到主机是目的吗? 不是的,因为数据是给人使用的。比如:聊天是人在聊天,下载是人在下载,浏览网页时人在楼兰
但是人是怎么看到聊天信息的呢? 怎么执行下载任务呢?怎么浏览网页信息呢?通过启动的QQ,迅雷,浏览器。
而启动的QQ、迅雷、浏览器都是进程。换句话说,进程是人在系统中的代表,只要把数据给进程,人就相当于拿到了数据,所以,我们日常网络通信的本质,就是进程间通信。只不过这里,本质上是通过网络协议栈来利用网络资源让两个不同主机的不同进程看到同一份资源。
所以: 数据传输到主机并不是目的,而是手段。到达主机内部,再交给主机的进程,才是目的。
网络协议中的下三层(传输层,网络层,数据链路层),主要解决的是数据安全可靠传输到远端主机上。
用户使用应用层软件(用户感官上使用的是软件,但是实际上是进程),完成数据发送和接受
但是在系统中,同时会存在非常多的进程,当数据到达目标主机之后,怎么转发给目标进程?
这就表明要在网络的背景下,在系统中,需要标识进程的唯一性。
2.认识端口号
端口号无论对于发送端还是接收端,都能够为唯一的标识该主机上的,一个网络应用层的进程。端口号是传输层协议的内容,但是应用层就要使用,因为应用层是基于传输层的。
- 端口号是一个2字节16位的整数;
- 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理。
- IP地址+端口号能够标识网络上的某一台主机的某一个进程;
- 一个端口号只能被一个进程占用。
端口号范围划分
- 0 - 1023:知名端口号,HTTP,FTP,SSH等这些广为使用的应用层协议,他们的端口号都是固定的。
- 1024 - 65535: 操作系统动态分配的端口号,客户端程序的端口号,就是由操作系统从这范围分配的。
理解“端口号” 和”进程PID“
这里就会有一个疑问,标识一个进程,不是有进程pid吗? 为什么不直接用pid。原因有以下两点:
一、 不是所有的进程都需要使用网络通信,例如单机进程,但是所有进程都要有pid。
二、 系统和网络功能需要解耦合。如果哪天OS不用pid了,你让网络怎么办?
另外,一个进程可以绑定多个端口号,但是一个端口号不能被多个进程绑定;
理解源端口号和目的端口号
我们在上面提到过端口号是传输层的协议内容,而应用层是工作在传输层之上的,使用传输层提供的服务,当主机收到数据时,自下向上依次解包到传输层时,传输层协议(TCP/UDP)的数据段中会有两个端口号,分别叫做 源端口号和目的端口号, 这两个端口号就决定了“数据是哪个端口发的,要发给哪个端口”,传输层就根据这个目的端口号,将数据交给所对应的进程。
理解socket
综上,IP地址用来标识互联网中唯一的一台主机,port用来标识该主机上唯一的一个网络进程。
IP + Port就能表示互联网中唯一的一个进程
所以,通信的时候,本质上是两个互联网进程代表人来进行通信,{srclp,srcPort,dstlp,dstPort} 这样的四元组就能够标识互联网中唯二的两个进程。
所以,网络通信的本质,也就是进程间通信。
我们通常把 ip+port叫做套接字 socket
C++
socket
n.
(电源)插座; (电器上的)插口, 插孔, 管座; 槽; 窝; 托座; 臼; 孔穴
vt.
把…装入插座; 给…配插座
一台主机中的两个软件绑定的端口号是不以言更低。
绑定是什么意思呢? 绑定其实就是将进程的PCB拿到传输层的哈希表中做哈希运算,与相应的端口号建立关系。
在网络通信过程中,网络被进程看作文件。OS把网络收到的有效载荷放到打开的网络文件缓冲区中,上层可以通过文件的方式读取。这是TCP的原理,与UDP有点差别。
客户端和服务器在两个不同的主机上,我们的客户端是如何知道服务器的端口号是多少的?所以,每个服务器的端口号必须是固定的,众所周知的,被客户知晓。
3. 传输层的典型代表
如果我们了解系统,也了解网络协议栈,我们就会清楚,传输层是属于内核的,那么我们要通过网络协议栈进行通信,必定调用的事传输层提供的系统调用,来进行的网络通信。
认识TCP协议
此处我们先对TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识;后面我们再详细讨论TCP的一些细节问题。
- 传输层协议
- 有连接
- 可靠传输
- 面向字节流
认识UDP协议
此处我们也是对UDP(User Datagram Protocol 用户数据报协议) 有一个直观的认识;后面再详细讨论
- 传输层协议
- 无连接
- 不可靠传输
- 面向数据报
什么叫有连接,什么又是无连接呢?
有连接,就是双方在通信之前需要先做一些保证信道流畅的动作,然后,在进行通信。这就叫有连接。
正因为通信之间做过连接了,所以传输的可靠性也就有了保障,当传输时发现有数据丢失或者错误时,传输层会根据一些算法来判断出现了问题,会自动进行补发。也正是因为这个特性,TCP所传输的数据十分的精细,是一个一个的字节,最后形成了一个有序的字节数组,所以也被称为面向字节流,
反之UDP协议,因为没有建立连接,所以在传输的过程中也就无法保证其可靠性,并且相对了传输数据时是一个一个数据报的形式传输的,每一个数据报都是一个字节块,也正因此,UDP的效率是比TCP高的。
那么既然TCP是可靠的,为什么还要有TCP和UDP两种协议呢? 这是因为可靠传输也是有成本的,建立连接的过程,数据发生错误时TCP也是需要维护的,这在一定程度上消耗了资源,而UDP则没有这些成本问题,直接发过去,有没有问题我不管。
不可靠意味着实现简单,方便,你可以理解为不可靠是一种特性,而并非是一种缺陷。
4. 网络字节序
我们都知道数据的存储有大小端之分。假设A主机以大端的方式存储数据,B主机以小段方式存储数据。那当这两个主机之间进行网络通信,该以大端的方式进行网络传输还是小段的方式呢?作为接受数据的主机,我们又怎么知道你传输的是大端还是小段呢? 有人提出能不能在协议中标识出来呢? 当然可以,但这会增加成本,并且经实践最后也发现解决不了。
于是,就有人提出了以下的这个规定,网络通信时统一以大端的方式进行传输。对于使用小端方式存储数据的主机,需要将数据转换成大端方式再进行传输。
详细就是:
- 发送主机通常会将发送缓冲区中的数据按内存地址从低到高的顺序发出;
- 接受主机把网络接收到的字节依次保存在接受缓冲区中,也是按内存地址从低到高的顺序保存;
- 因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。
- TCP/IP协议规定,网络数据流应采用大端字节流,即低地址高字节。
- 不管这台主机是大端机还是小端机,都会按照这个TCP/IP规定的网络字节序来发送/接受数据。
- 如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可。
为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能够正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
- 这些函数名其实很好记的,h表示host,n表示network,l表示32位长整数,s表示16位短整数。
- 例如 htonl 就是表示将32位短长整数从主机字节序转换为网络字节序,例如将IP地址转换后准备发送。
- 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回。
- 如果主机是大端字节序,这些函数则不做任何转换,将参数原封不动地返回。
5. socket 编程接口
Socket常见的API如下, 后面我们就会发现socket的返回值类型,实际上就是文件描述符。
C
// 创建 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);
sockaddr结构
Socket API是一层抽象的网络编程接口,适用于各种底层网络协议,如IPv4,IPv6以及后面要讲的UNIX Domain Socket。 然而,各种网络协议的地址格式并不相同。
套接字编程的种类有三种:
1. 本地Socket(又称Unix 域间socket) 是Unix及类Unix系统里实现进程间通信(IPC)的一种方式,这个我们只了解一下就行,因为本地的进程间通信,我们之间已经讲过了,对于本地的进程间通信,我们更倾向于管道来实现。
2. 原始socket 是一泓特殊的网路编程接口,它运行应用程序直接访问底层的网络协议,可以绕过传输层协议,直接对网络层甚至数据链路层进行操作,具有高度的灵活性,因此也需要高权限才能使用,主要用于编写网络工具,这个我们也只是了解一下。
3. 网络Socket,最常使用的套接字,适用于本地+网络的进程间通信,主要用于用户间的网络通信,我们后续所编写的就是这种的。
以上这些都由OS提供系统接口来供我们使用,但是每一套方案设计一套接口太麻烦了,想要将网络接口统一抽象化。接口统一,要求接口参数是统一的。哪怎么确定你使用的是哪种类型,是网络还是本地呢?
通过设置type值,AF_INET对应struct sockaddr_in,AF_UNIX对应struct sockaddr_un。
统一接口,为什么不设置成void*呢? 因为那个时候还没有void*。
- IPv4和IPv6 的地址格式定义在 netinet/in.h 中IPv4用 sockadd_in 结构体表示,包括16位地址类型,16位端口号和32位IP地址。
- IPv4、IPv6 地址类型分别定义为 常数AF_INET、AF_INET6,这样,只要取得某种sockaddr 结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
- Socket API 可以都用 struct sockaddr*类型表示,在使用的时候需要强制转化成sockadd_in;这样做的好处是程序的通用性,可以接受IPv4,IPv6以及UNIX Domain Socket 各种类型的sockaddr 结构体指针作为参数;
上面这种用法有没有很熟悉,Socket API 都可以使用 struct sockaddr* 来表示,在使用时强转为其他类型,这不就是多态的思想吗?
是的,这种方式就是C语言实现多态的方式。
sockaddr结构
sockaddr_in 结构
虽然,socket API 的接口时 sockaddr,但是我们真正在基于IPv4编程时,使用的数据结构是 sockaddr_in;这个结构体例主要由三部分信息:地址类型,端口号,IP地址。
In_addr 结构
in_addr 用来表示一个IPv4 的IP地址,其实就是一个32位的整数。