分布式系统的基石-TCP/IP通讯协议

一、http协议在整个网络中的请求过程

    当应用程序用 T C P 传送数据时,数据被送入协议栈中,然后逐个通过每一层直到被当作一串比特流送入网络。其中每一层对收到的数据都要增加一些首部信息 。

分布式系统的基石-TCP/IP通讯协议

 

    当目的主机收到一个以太网数据帧时,数据就开始从协议栈中由底向上升,同时去掉各层协议加上的报文首部。每层协议盒都要去检查报文首部中的协议标识,以确定接收数据的上层协议。这个过程称作分用 。

分布式系统的基石-TCP/IP通讯协议

为什么有了 MAC 层还要走 IP 层呢?
    mac地址就像人的身份证号,每个身份证号区别一个人,但是只知道一个人的身份证号是不能找到他人在哪的,mac地址类似,它是机器生产厂商生产时生成的,知道一个设备的mac地址,并不能在网络中将数据发给他,除非它和发送方处于同一个网段内。所以要实现机器之间的通信, 还需要有 ip 地址的概念, ip 地址表达的是当前机器在网络中的位置,类似于城市名+道路号+门牌号的概念。通过 ip 层的寻址,我们能知道按何种路径在全世界任意两台 Internet 上的的机器间传输数据 。
 

二、IP协议和TCP/UDP协议

    协议:协议相当于两个需要通过网络通信的程序达成的一种约定,它规定了报文的交换方式和包含的意义。 比如(HTTP)为了解决在服务器之间传递超文本对象的问题,这些超文本对象在服务器中创建和存储,并由 Web 浏览器进行可视化,完成用户对远程内容的感知和体验 。

    IP协议:T C P 和 U D P 是两种最为著名的传输层协议,他们都是使用 I P 作为网络层协议。 IP 协议提供了一组数据报文服务,每组分组报文都是由网络独立处理和分发,就像寄送快递包裹一样,为了实现这个功能,每个 IP 报文必须包含一个目的地址的字段; 就像我们寄送快递都需要写明收件人信息,但是和我们寄送快递一样,也可能会出现包裹丢失问题,所以 IP 协议只是一个“尽力而为”的协议,在网络传输过程中,可能会发生报文丢失、报文顺序打乱,重复发送的情况。 IP 协议层之上的传输层,提供了两种可以选择的协议, TCP、 UPD。这两种协议都是建立在 IP 层所提供的服务基础上,根据应用程序的不同需求选择不同方式的传输;

    TCP/IP:TCP 协议能够检测和恢复 IP 层提供的主机到主机的通信中可能发生的报文丢失、重复及其他错误。 TCP 提供了一个可信赖的字节流通道,这样应用程序就不需要考虑这些问题。同时, TCP 协议是一种面向连接的协议,在使用 TCP进行通信之前,两个应用程序之间需要建立一个 TCP 连接,而这个连接又涉及到两台电脑需要完成握手消息的交换。

    UDP/IP:UDP 协议不会对 IP 层产生的错误进行修复,而是简单的扩展了 IP 协议“尽力而为”的数据报文服务,使他能够在应用程序之间工作,而不是在主机之间工作,因此使用 UDP协议必须要考虑到报文丢失,顺序混乱的问题。

TCP是如何做到可靠传输的?

    建立可靠的连接:TCP通过三次握手(客户端和服务端总共发送3个包来确认连接的建立),四次挥手的机制建立了可靠传输的机制。

 三次握手

 

分布式系统的基石-TCP/IP通讯协议

 

握手过程:

    1.主机A发送位码为SYN=1,seq表示自己的系列号,主机B由SYN=1知道,A要求建立联机。

    2.主机B收到请求后要确认联机信息,向A发送ack=(主机A的seq+1),即准备接收系列为seq+1开始的包,SYN=1,ACK=1(确认),并把自己的开始系列号seq=K发送给主机A。

    3.主机A收到信息后确认,ACK标志位置1,ack=K+1,表示开始接收主机B系列号为K+1开始的数据包。此时可靠通讯建立完成。

为什么要三次握手?

    第一次握手,主机A什么都不能确认,主机B确认了对方发送正常,自己接收正常。

    第二次握手,主机A确认了自己发送、接收正常,对方发送、接收正常,主机B确认了自己接收正常,对方发送正常。

    第三次握手,主机A确认了自己发送、接收正常,对方发送、接收正常,主机B确认了自己发送、接收正常,对方发送、接收正常。

四次挥手

    分布式系统的基石-TCP/IP通讯协议

为什么要四次挥手?

    一方发送FIN=1表示其已经发送完所需发的数据,但还允许对方把还未发送完的数据发过来。此时主机B则先发送个确认信息ACK=1,等它确定把数据发完之后才发送FIN=1。而三次握手中主机B收到主机A的建立连接报文时可以直接发送ACK+SYN报文。其中ACK报文时用来应答的,SYN报文是用来同步的。

为什么主机A最后(TIME_WAIT)还有等待2MSL(最大报文生存时间)?

    这是因为主机A最后发送的ACK报文未到达主机B,站在主机B角度,它发送FIN+ACK报文后未收到响应,就会再重新发送一次报文给主机A。而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。

数据传输过程的流量控制和确认机制

    如果接收方不限制发送方发送的数据包的话,发送方将一直发送数据包,接收方来不及接收的话就将导致数据包的丢失。滑动窗口机制刚好解决了这个问题,即实现对发送方的流量的控制。TCP 的窗口单位是字节,不是报文段,发送方的发送

窗口不能超过接收方给出的接收窗口的数值。活动窗口在线演示:https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html

三、通信的性能问题

传统的BIO模型

分布式系统的基石-TCP/IP通讯协议

 

    我们发现 TCP 响应服务器一次只能处理一个客户端请求, 当一个客户端向一个已经被其他客户端占用的服务器发送连接请求时, 虽然在连接建立后可以向服务端发送数据, 但是在服务端处理完之前的请求之前, 却不会对新的客户端做出响应, 这种类型的服务器称为“迭代服务器”。迭代服务器是按照顺序处理客户端请求, 也就是服务端必须要处理完前一个请求才能对下一个客户端的请求进行响应。 但是在实际应用中, 我们不能接收这样的处理方式。所以我们需要一种方法可以独立处理每一个连接, 并且他们之间不会相互干扰。 而 Java 提供的多线程技术刚好满足这个要求。

    要解决NIO的问题,我们先理解下TCP协议的通信过程:

    对于 TCP 通信来说,每个 TCP Socket 的内核中都有一个发送缓冲区和一个接收缓冲区, TCP 的全双工的工作模式及 TCP 的滑动窗口就是依赖于这两个独立的 Buffer 和该Buffer 的填充状态。接收缓冲区把数据缓存到内核,若应用进程一直没有调用Socket 的 read 方法进行读取,那么该数据会一直被缓存在接收缓冲区内。不管进程是否读取 Socket,对端发来的数据都会经过内核接收并缓存到 Socket 的内核接收缓冲区。

 

    read 所要做的工作,就是把内核接收缓冲区中的数据复制到应用层用户的 Buffer 里。进程调用 Socket 的 send 发送数据的时候,一般情况下是将数据从应用层用户的 Buffer 里复制到 Socket 的内核发送缓冲区,然后 send 就会在上层返回。换句话说, send 返回时,数据不一定会被发送到对端。Socket 的接收缓冲区被 TCP 用来缓存网络上收到的数据,一直保存到应用进程读走为止。如果应用进程一直没有读取,那么 Buffer 满了以后,出现的情况是:通知对端 TCP协议中的窗口关闭,保证 TCP 接收缓冲区不会移除,保证了 TCP 是可靠传输的。如果对方无视窗口大小发出了超过窗口大小的数据,那么接收方会把这些数据丢弃。

分布式系统的基石-TCP/IP通讯协议

如何通过非阻塞提高性能?

 

    非阻塞要解决的就是 I/O 线程与 Socket 解耦的问题, 因此, 它引入了事件机制来达到解耦的目的。 我们可以认为NIO 底层中存在一个 I/O 调度线程, 它不断的扫描每个Socket 的缓冲区, 当发现写入缓冲区为空的时候, 它会产生一个 Socket 可写事件, 此时程序就可以把数据写入到 Socket 中。 如果一次写不完, 就等待下一次的可写事件通知; 反之, 当发现缓冲区里有数据的时候, 它会产生一个 Socket 可读事件, 程序收到这个通知事件就可以从Socket 读取数据了。

NIO

    实际上基于上面讲的传统的 BIO 模型, 一个请求一个线程的方式, 如果要涉及到上千个客户端访问时, 会产生很多的问题, 比如扩展性、 系统资源开销等等。 所以我们需要一种方法来轮询一组客户端, 来查找哪个连接需要提供服务, 这个就是我们讲的“NIO”。

一些概念

    缓冲区:在NIO中,所有的数据都是通过用缓冲区处理,读取数据时是直接读到缓冲区,写数据时写到缓冲区。任何时候访问NIO中的数据,都是通过缓冲区进行的操作。

 

    通道:Channel 通道,就像一个自来水管一样,可以通过它读取和写入数据, Channel 是全双工的,所以数据是双向流动。

    多路复用:多路复用器 Selector,是 NIO 的基础,多路复用器提供选择已经就绪的任务的能力,简单来说, Selector 会不断轮询注册上的 Channel,如果某个 Channel 上面有新的 TCP连接接入、读、写事件,这个 Channel 就处于就绪状态, 会被 Selector 轮询出来,然后通过 SelectionKey 可以获取就绪的 Channel 进行 I/O 操作;一个多路复用器可以同时轮询多个 Channel。通过这个机制可以接入成千上万的客户端 。
分布式系统的基石-TCP/IP通讯协议

组播协议

 

    广播:广播是主机向子网内所有主机发送消息,子网内所有主机都能收到来自某台主机的广播信息,属于点对所有点的通信。广播意味着网络向子网每一个主机都投递一份数据包,不论这些主机是否乐意接收该数据包 。

    多播:多播是主机向一组主机发送信息,存在于某个组的所有主机都可以接收到消息,属于点对多点的通信。