网络分层
考虑最简单的情况:两台主机之间的通讯。这个时候只需要一条网线把两者连起来,规定好彼此的硬件接口,如都用USB、电压10v、频率2.4GHz等,这一层就是物理层,这些规定就是物理层协议。
我们当然不满足于只有两台电脑连接,因此我们可以使用交换机把多个电脑连接起来,如下图:
这样连接起来的网络称为局域网,也可以称为以太网(以太网是局域网的一种)。在这个网络中,我们需要标识每台机器,这样才能指定要和哪台机器通讯。这个标识就是硬件地址MAC。
硬件地址随机器的生产而确定,永久唯一。在局域网中,当我们需要与另一台机器通信时,只需知道其硬件地址,交换机就会将我们的消息传送到相应的机器。
在这里,我们可以不关心底层的网络线路接口如何传输,将物理层抽离出来,在其之上建立一个新的层次,这就是数据链路层。
我们仍然不满足于区域网的规模,需要将所有的区域网联系起来,这个时候就需要用到路由器来连接两个区域网:
然而,随着网络规模的不断扩大,如果我们仍然使用硬件地址作为通信对象的唯一标识,那么需要记住所有机器的硬件地址将变得不切实际
同时,一个网络物件可能会频繁更换设备,这时候硬件地址表维护起来更加复杂。这里使用了一个新的地址来标记一个网络物件:IP地址。
通过一个简单的寄信例子来理解IP地址。
亲爱的A, 最近好吗?希望你一切都好。我想念你,很久没有见面了。我住在北京市,生活还算不错。北京的天气最近有些冷,但是我喜欢冬天的雪景。 我想告诉你一些关于我在北京的生活。我找到了一份新工作,工作环境很好,同事们也很友好。我每天都很忙,但是我很享受我的工作。除了工作,我还参加了一些兴趣小组,结识了一些新朋友。我也经常去参观北京的名胜古迹,感受这座城市的历史和文化。 你最近在上海过得怎么样?工作还顺利吗?有没有什么新鲜事要和我分享?我很期待听到你的近况。 希望我们能尽快见面,一起聊天,分享彼此的生活。如果你有时间,欢迎来北京玩。我会带你去品尝北京的特色美食,一起游览这座美丽的城市。 祝你一切顺利,期待早日收到你的回信。
-
写完信后,我会在信上写上我朋友A的地址,并放到北京市邮局(同时附上目标IP地址信息,并发送给路由器)。
-
邮局会将我的信件运送到上海市当地的邮局(信息将通过路由传递到目标IP区域网的路由器)。
上海市的本地路由器会将我的信件传递给朋友A(在局域网内通信)。
因此,这里的IP地址就是一个网络接入地址(朋友A的住址),我只需要知道目标IP地址,路由器就可以把信息给我带到。在局域网中,就可以动态维护一个MAC地址与IP地址的对映关系,根据目的IP地址就可以寻找到机器的MAC地址进行传送。
这样我们不需要管理底层如何去选择机器,我们只需要知道IP地址,就可以和我们的目标进行通讯。这一层就是网络层。网络层的核心作用就是提供主机之间的逻辑通讯。
这样,在网络中的所有主机,在逻辑上都连接起来了,上层只需要提供目标IP地址和数据,网络层就可以把信息传送到对应的主机。
一个主机有多个程序,程序之间进行不同的网络通讯,如边和朋友开黑边和女朋友聊微信。我的手机同时和两个不同机器进行通讯。
那么当我的手机收到数据时,如何区分是微信的数据,还是王者的数据?那么就必须在网络层之上再新增一层:运输层:
运输层通过套接字(socket)将网络信息进一步拆分,不同的应用程序可以独立进行网络请求,互不干扰。
这就是运输层的最本质特点:提供程序之间的逻辑通讯。这里的程序可以是主机之间,也可以是同个主机,所以在android中,socket通讯也是程序通讯的一种方式。
现在不同机器上的应用程序之间可以独立通信了,那么我们就可以在计算机网络上开发出形形式式的应用:如web网页的http,文件传输ftp等等。这一层称为应用层。
应用层还可以进一步拆分出表示层、会话层,但他们的本质特点都没有改变:完成具体的业务需求。和下面的四层相比,他们并不是必须的,可以归属到应用层中。
最后对计算机网络分层进行小结:
- 最底层物理层,负责两个机器之间通过硬件的直接通讯;
- 数据链路层使用硬件地址在局域网中进行定址,实现局域网通信;
- 网路层透过抽象IP地址实现主机之间的逻辑通讯;
- 运输层在网路层的基础上,对资料进行拆分,实现应用程序的独立网络通讯;
应用层在运输层的基础上,根据具体的需求开发各种功能。
这里需要注意的是,分层并不是在物理上的分层,而是逻辑上的分层。透过对底层逻辑的封装,使得上层的开发可以直接依赖底层的功能而无需理会具体的实现,简便了开发。
这种分层的思路,也就是责任链设计模式,透过层层封装,把不同的职责独立起来,更近方便开发、维护等等。
TCP面向位元组流特性
TCP并不是把应用层传输过来的资料直接加上首部然后传送给目标,而是把资料看成一个位元组流,给他们标上序号之后分部分发送。这就是TCP的 面向位元组流
TCP将以流的形式从应用层读取数据并存放在自己的发送缓存区中,同时为这些字节标上序号
TCP将从发送方缓冲区选择适量的字节组成TCP报文,并通过网络层传送给目标
目标会读取字节并存放在自己的接收方缓冲区中,并在合适的时候交付给应用层
面向字节流的好处是不需要一次存储过大的数据占用太多内存,坏处是无法知道这些字节代表的意义,例如应用层传送一个音频文件和一个文字文件,对于TCP来说就是一串字节流,没有意义可言,这会导致粘包以及拆包问题,后面讲。
可靠传输原理
TCP是一种可靠的传输协议,也就是说,一旦将数据交给它,它肯定能够完整无误地传送到目标地址,除非网络出现故障。它所实现的网络模型如下:
对于应用层来说,它是一个可靠传输的底层支持服务;而运输层底层采用了网络层的不可靠传输。虽然在网络层甚至数据链路层就可以使用协议来保证数据传输的可靠性,但这样网络的设计会更加复杂,效率会随之降低。把数据传输的可靠性保证放在运输层,会更加合适。
可靠传输原理的重点总结一下有:滑动窗口、超时重传、累积确认、选择确认、连续ARQ。
停止等待协议
要实现可靠传输,最简便的方法就是:我传送一个数据包给你,然后你回复我收到,我继续传送下一个数据包。传输模型如下:
这种“一来一去”的方法来保证传输可靠就是停止等待协议(stop-and-wait)。不知道还记不记得前面TCP首部有一个ack字段,当他设置为1的时候,表示这个报文是一个确认收到报文。
然后再考虑另一种情况:丢包。网络环境不可靠,导致每次传送的数据包可能会丢失。如果机器A发送的数据包丢失了,那么机器B将永远无法接收到数据,机器A也将永远处于等待状态。
解决这个问题的方法是:超时重传。当机器A发送一个数据包时开始计时,时间到还没收到确认回复,就可以认为是发生了丢包,便再次传送,也就是重传。
但是重新传输会导致另一种问题:如果原先的数据包并没有丢失,只是在网络中待的时间比较久,这个时候机器B会收到两个数据包,那么机器B是如何辨别这两个数据包是属于同一份数据还是不同的数据?
这就需要前面讲过的方法:给数据字节进行编号。这样接收方就可以根据数据的字节编号,得出这些数据是接下来的数据,还是重传的数据。
在TCP头部有两个字段:序号和确认号,它们表示发送方数据的第一个字节的编号,以及接收方期望的下一份数据的第一个字节的编号。
连续ARQ协议
停止等待协议已经可以满足可靠传输了,但有一个致命缺点:效率太低。发送方发送一个数据包之后便进入等待,这个期间并没有干任何事,浪费了资源。解决的方法是:连续发送数据包。模型如下:
与停止等待最大的不同在于,它会持续不断地传送数据,接收方在不断收到数据后逐一进行确认回复。这样极大地提高了效率。但同时也带来了一些额外的问题:
传送是否可以无限传送直到把缓冲区所有数据传送完?不可以。因为需要考虑接收方缓冲区以及读取数据的能力。如果传送太快导致接收方无法接受,那么只是会频繁进行重传,浪费了网络资源。所以传送方传送数据的范围,需要考虑到接收方缓冲区的情况。这就是TCP的流量控制。
传送方需要根据接收方的缓冲区大小,设置自己的可传送窗口大小,处于窗口内的数据表示可传送,之外的数据不可传送。
当窗口内的数据收到确认回复时,整个窗口会向前移动,直到发送完所有的数据
TCP首部包含一个窗口大小字段,用于表示接收方的剩余缓冲区大小,以便发送方可以调整自己的发送窗口大小。通过滑动窗口,TCP实现了流量控制,以防发送速度过快导致数据丢失过多。
连续ARQ带来的第二个问题是:网络中充斥着与传送数据包一样数据量的确认回复报文,因为每一个传送数据包,必须得有一个确认回复。提高网络效率的方法是:累积确认。
接收方无需逐个回复,而是在累积一定数量的数据包后,告知发送方已收到此数据包之前的所有数据。例如,收到1234,接收方只需告知发送方已收到4,发送方便知道1234均已收到。
第三个问题是:如何处理丢包情况。在停止等待协议中很简单,直接一个超时重传就解决了。但,连续ARQ中不太一样。
例如:接收方收到了 123 567,六个字节,编号为4的字节丢失了。按照累积确认的思路,只能发送3的确认回复,567都必须丢掉,因为发送方会进行重传。这就是GBN(go-back-n)思路。
但是我们会发现,只需要重传4即可,这样不是很浪费资源,所以就有了:选择确认SACK。在TCP报文的选项字段,可以设置已经收到的报文段,每一个报文段需要两个边界来进行确定。这样发送方,就可以根据这个选项字段只重传丢失的数据了。
可靠传输小结
关于TCP的可靠传输原理已经介绍差不多了。最后进行一个小结:
- 通过连续的ARQ协议和发送-确认回复模式来确保每一个数据包都到达接收方
- 通过给字节编号的方法,来标记每一个数据是属于重传还是新的资料
- 通过超时重传的方式来解决数据包在网络中丢失的问题
- 通过滑动窗口实现流量控制
- 通过累积确认和选择确认的方法来提高确认回复和重传的效率
当然,这只是可靠传输的冰山一角,感兴趣可以再深入去研究。