目录
1.网络协议
(1)网络的起源
(2)为什么需要协议
(3)协议分层及其设计的解耦
(4)OSI定义的七层网络模型
①分层及其功能
②TCP/IP协议
③传输层协议(TCP和UDP)
2.网络通信
(1)IP、MAC地址
①MAC地址
②IP地址
③通信中MAC地址和IP地址的区别
(2)通信过程
①封装和解包
②IP和MAC在通信过程中的作用
③端口号
④路由和路由器
(3)通信过程中的解耦合设计
①端口号和PID
②网卡发送
(4)网络字节序
(5)以太网和令牌环网的概念
1.网络协议
(1)网络的起源
起初,计算机在一个小范围内通信,即在局域网内通信。但是随着发展计算机有着更大的通信需求,需要通过服务器进行数据共享,于是出现了广域网。总体划分,网络主要分为广域网和局域网(城域网等是后来细化发展出来的)。
人们有通信的需求,相应的,计算机也一定会产生通信的需求。计算机为人服务,而通信就是人很大的需求,因此网络的诞生是必然而不是偶然。
(2)为什么需要协议
不同计算机有着共同的底层,即0和1表示数据,但不同计算机表示0和1的方式不一致(如光电信号的强弱,电信号的有无等),系统的设计也不一样。要让不同底层、系统实现计算机通信,我们要如何抹平硬件的设计差异呢?这就需要若干软件层面的约定,即协议。协议的本质就是一套约定。两台计算机的通信需要约定,就像我们打电话接通后的第一声“喂”就是人们公认的表示接通电话的约定。
实际上,在系统里面,CPU和内存、磁盘之间也存在自己的协议,包括计算机内的硬件、嵌入式设备都有自己的协议,这样才能实现硬件通信。总的来说,协议无处不在,网络协议只是其中一个部分(国际协会做的公认的标准之一)。
在网络中,我们希望主机A、C能够通信,但和系统内的进程间通信不同,网络通信的物理距离变长了,通信的特征变化了会带来新的问题,主机A怎么发到路由器呢?怎么找到主机B呢?数据丢失怎么办呢?B怎么知道我的数据应该怎么处理呢?这就需要新的协议。
HTTP、FTP、SSH等都是广为使用的应用层协议
(3)协议分层及其设计的解耦
要实现通信,需要经过多个软件协议,这些协议是分层的,可以说它们相互是解耦合的。协议分层在设计上更模块化,更好维护。在C++中,继承和多态体系下的类的设计就是分层的,父子分层,可以更好维护层状体系。
假设现在有两个用户通信,用户之间遵循的协议是语言层,而通信设备之间遵循的协议是设备通信层。在用户看来,他们是通过语言来通信的,他们也能听懂彼此的话。这反映出通信也是分层的,用户一直认为是在语言层通信,对设备来说一直都在设备通信层通信。平层的协议当然能够互相理解。简单来说,一层协议就是一个结构体,发送端发送该层的一个结构体,接收端当然知道如何处理,因为写这个结构体的人早考虑好了。
同样的,我们可以以分层的视角来看待系统了,它也是各层之间解耦合的,并且进程间通信也是各层之间互相通信实现的。在编程语言中,类与类之间也是分层的,分层后可以通过不同层之间的接口来调用,这样出问题后可以很快排查、定位问题。
(4)OSI定义的七层网络模型
①分层及其功能
虽然有七层,但定标准和实现的是不同人。OSI规定了标准,要求其它公司写系统需要遵循该网络协议,这些公司实现时发现不需要这么复杂,就省去了,但必要的接口都是按照标准来的,因此OSI还是实现了它的目标。
下面是常见的协议分层(5层)
应用层和传输层还存在表示层和会话层,后续用到再说,大部分情况这5层就足够了。
②TCP/IP协议
虽然说不同层有不同协议,但TCP和IP是这个网络协议栈的核心,于是OSI定义的这个网络协议就可将其称为TCP/IP协议。
③传输层协议(TCP和UDP)
传输层常见的协议有TCP和UDP。
TCP的性质是面向连接、可靠传输、面向字节流。
UDP的性质是不面向连接、不可靠传输、面向数据报。
简单来说,TCP可用于转账、下单等与数据可信度要求高的应用,而UDP用于直播等对小型丢包感知不强的应用。
TCP面向字节流,意思是把数据缓存起来后由用户自己决定如何取出数据,可以一次性读出来,也可以多读几次。而UDP的面向数据报就像寄快递,发的时候就决定了接收数据时一定要全拿走。
2.网络通信
(1)IP、MAC地址
①MAC地址
MAC地址是48bit,共6字节,用16进制 + 冒号表示(如08:00:27:03:89:19)。
Windows在cmd里面用ipconfig /all查看,MAC在出厂时就确定了,在Windows里,可能切换网络后MAC会变化。
MAC地址具有唯一性,原则上说MAC地址可以是任何值,只需要在局域网内不冲突就可以了。
MAC地址是在一个局域网里面标识自己的地址。有了MAC地址,就能实现局域网通信。形象来说,老师在班上教训某个同学,在班上的其他同学都能听到内容,但他们不需要做任何处理,这个MAC就是同学的名字。同一局域网内所有主机都能收到消息,只不过MAC不匹配会丢弃消息。一个班上不能出现同名的人,对应MAC地址在局域网内的唯一性。但在不同班里可能有同名的人。
②IP地址
IP地址有IPv4和IPv6,现在IPv6还未完全普及开,所以后续的IP地址默认都是指的IPv4。IPv4是4字节、32bit的数据。.用来隔开4个0-255的数字。
如192.168.34.45是字符串风格的IP地址,点分十进制提高了可读性。网络中常用char.char.char.char四字节表示IP(如struct IP里面的成员变量有char p1、p2、p3、p4),这两种表示可以互相转换。
在Linux的eth0可查看自己的IP地址
从局域网通信 -> 跨子网通信,不同实验室实现了不同局域网通信方式。IP的出现使得不同的主机可以相互通信,就算局域网不同,但它们IP之上完全一样,IP是对底层网络屏蔽差异化的解决方案。
③通信中MAC地址和IP地址的区别
在网络通信中,通信的源和目的IP地址是永远不变的,MAC地址在经过一个地方后就会变化。类似于旅行,始终存在两条路线,其中长期路线永远不变(对应源IP和目的IP);同时,也有当前的位置和即将前往的目的地,这个一直在变(源MAC地址和目的MAC地址)。
(2)通信过程
①封装和解包
应用层要相互通信,就要先把数据传到物理层(网卡)。经过每层的通信时,数据前面会多封装一个协议报头,类似于快递单和快递盒子 + 数据,每经过一层协议,就会套一层包装。
应用层报头、传输层报头、网络层报头、数据链路层报头。物理层的主要功能是发送数据,它没有严格意义上的报头。报文 = 报头 + 有效载荷,每一层的报文向下传递后其整体又被作为有效载荷处理。
网络层报头包含源IP和目的IP,数据链路层包含当前设备的MAC和下一个设备的MAC
物理层将数据发送后,网卡收到数据后会触发硬件中断,让系统来处理,接收方会像栈一样,一层一层分析报头,一层层解包,去掉该层的报头得到的就是有效载荷。当前层的报头内部必须包含一个字段,记录接下来要将自己的有效载荷交给谁,就像链表的指针一样。
简单来说,每一层得到自己的报文后,都能看懂报头的内容,也知道有效载荷应该交给谁。这些都基于同层协议之间的数据能够理解的基础上,就像人和人的沟通,同层之间肯定没有障碍。
②IP和MAC在通信过程中的作用
在封装过程中,网络层会封装IP(源IP和目的IP),数据链路层会封装MAC帧(源MAC地址和目的MAC地址)。
在通信过程中,主机A可能会经过路由器B(路由指的就是路径选择)才能交到C中。主机A封装时会把目的MAC设置成B的MAC,因此当数据传给B时,B解到数据链路层时发现跟自己的MAC匹配,再解包到网络层。
解包到网络层后就得到了源IP和目的IP,之后路由器会根据源IP和目的IP进行路由,再向下封装,重新封装MAC地址(设置下一个源MAC和目的MAC),继续通过MAC交给另一个子网。如此往复,直到达到目的地址。
整个通信过程中,MAC地址一直在解包和封装,而网络层的IP地址不会变,MAC地址就像脱衣服穿衣服一样,一直根据当前的设备在变化。就像旅游一样,从重庆到海南是源IP和目的IP,而旅行途中要先从贵州开到广西则是源MAC和目的MAC,它们会随着当前位置的改变而改变。
整个过程我们要记住路由器之间的解包是解到网络层停止,不会向上交付,因为目的IP不匹配。
在局域网内,其它主机也会收到消息,但当它们解包到数据链路层时,发现目标MAC不是自己,于是就会直接丢掉数据,应用层根本感知不到自己收到过数据。这就是前面提及的教室里面老师和学生通信时其他同学的处理方式。
③端口号
目的IP在网络中标识主机的唯一性。但在整个通信过程中仅有IP是不够的,因为数据借助IP传输到主机只是手段,数据到达主机内部,再交给主机内的进程才是目的。因此,网络通信的本质就是进程间通信IPC。
数据在网络层向上交付给进程需要端口号(port),这是传输层的内容,端口号共2字节16bit(十进制对应0 - 65535),每一个端口号和进程关联,告诉系统这个数据要交给哪个进程来处理。总的来说,IP + 端口号(socket)合起来才标志某一台主机的某一个具体的进程。两个进程通过网络这个公共资源来进行socket通信。
对于端口号的分配来说,0-1023一般被固定了,1023-65535是OS动态分配的端口号,也是客户端程序的端口号
对于服务端和客户端来讲,如果要通信,IP和端口号都是必不可少的,一般来说手机里面下载的软件里面就保存了要访问的服务器的IP和端口号,第一次建立通信时客户端也要发送自己的IP和端口号,这样服务端才知道如何返回响应。
④路由和路由器
两个不同子网通信必须要经过路由器。根据前面的通信过程我们也能发现,路由器是工作在网络层的,路由器在两端主机看来都各自认为路由器在自己的局域网内,实际上,子网是路由器构建的,子网内主机的IP地址都是路由器给的,首次连接后主机也能知道路由器的MAC、IP地址。路由器硬件内部有以太网和令牌环的驱动程序,可以和不同子网通信。同时路由器自己也有IP地址,并且能够进行判断目的IP是不是在同一局域网(同一子网内主机的IP地址基本是一致的),并可以选择下一步转发数据给谁,这个路径的选择过程就叫路由。
(3)通信过程中的解耦合设计
①端口号和PID
为什么要有端口号而不直接用pid?pid是进程管理的范畴,如果网络使用pid,那么网络和进程管理就耦合了。如果系统修改pid,网络管理也要跟着改,这显然不合理。
引入端口号之后,只需要一个软件层(如哈希表)即可实现pid和端口号的转变。一般来说,系统会维护一个65535个元素的哈希表,下标就是端口号。系统将进程PCB的地址存到表中,当接收到端口号时,直接根据报文查哈希表就能找到PCB,进而通信。
②网卡发送
最后利用网卡发送消息,实际上不需要在系统上大动干戈,只需要将file底层指向的方法改成网卡发送的方法,这样系统直接对文件操作,是可以实现对网络进行操作。这就是系统管理和网络管理的解耦合,加入新功能毫不影响原来系统的设计。
有了系统和网络的解耦合,进程间通信可以替换成网络通信,即网络通信实现本地通信,只需用网络的一套规则,将发送方和接收方都设置成自己即可。
(4)网络字节序
小端:低权值放低地址,高权值放高地址
大端:低权值放高地址,高权值放低地址
网络规定所有发送到网络上的数据必须都是大端的,对于数据0x11223344,采用大端按照从低地址到高地址发送数据,就会先发送1(高权值),一般来说这里就是报头的位置,因此这种处理易于边读边分析。
OS可能采用不同字节序,但这没有关系,OS根据自己的设计转换即可。
(5)以太网和令牌环网的概念
以太网,名字来源于物理学的以太,这算是物理概念的迁移。
令牌环就是谁有令牌环就只能谁说话,令牌就是上锁。
在以太网中同一时间只能存在一份有效报文,这样才能尽量避免光电信号干扰。如果有多个用户之间要通信,就会进行数据的碰撞检测,一般来说系统为了避免碰撞,会选择等一会重发。这个以太网难道不就类似系统中的临界资源吗?碰撞避免难道不就类似加锁吗?并且整个以太网也是一个生产消费模型。
因此一个局域网也是一个碰撞域,主机越少,碰撞的概率就越低;主机越多,网络就可能更差,因为碰撞避免多了,主机休眠时间长了。