【分布式专题】-- 分布式基石(TCP/IP通信协议)
http请求,在整个网络各层的请求过程:
当应用程序通过TCP传输数据,数据被送入协议栈中,然后逐层添加头部协议信息,最后到物理层转成比特流的形式传输。
- 当用户访问浏览器,发送请求
tcp/ip四层传输模型:
应用层----- +TCP头+http请求报文
网络层------ +IP头+TCP头+http请求报文 (IP头协议是不可靠的,所以网络层加上了可靠的tcp协议)
应用层------ +MAC头 + IP头+TCP头+http请求报文
物理层------ 电信号(0101001100111) ——--------——>目标服务器收到后逐层(自下往上)解析
(1)什么是协议?
两个服务器之间通信需要 达成的一个通信约定。
(2)用户在应用层做了什么??
- 根据DNS拿到IP地址
- 根据ip端口访问对应服务器
- 拿到服务器的内容(json,xml,jpg)返回给浏览器
(3)TCP(网络层的协议)
TCP协议 (三次握手,四次挥手)
tcp能够检测和恢复主机与主机之间的通信,以及可能发生的报文丢失或发送错误,重复发送等问题
tcp提供了一个可信赖的字节流通道,所以用户不需要考虑数据丢失重复发送等问题、
tcp是一个全双工协议,客户端与服务端均可以主动发起挥手动作,所以任何一方执行close()操作即可产生挥手。
(4)UDP协议
提供了尽力而为的报文协议,不会解决报文丢失和混乱的问题
(5)TCP流量控制:
滑动窗口协议(可靠的)
提供一个在线动态演示滑动窗口机制:
https://media.pearsoncmg.com/aw/ecs_kurose_compnetwork_7/cw/content/interactiveanimations/selective-repeat-protocol/index.html
tips:
为什么tcp连接是三次握手,四次挥手?
(1) 三次握手?
建立连接时候— 当server端接收到client端的syn连接请求报文后,可以直接发送syn+ack报文,其中ack是用来确认应答的,syn报文使用来同步的。
(2) 四次挥手?
关闭连接时候— 当serve端收到fin报文是,很可能并不会立即关闭socket(因为可能有消息没有处理完毕),所以只能回复一个ack报文,确认一下,告知已收到fin报文;只有当server端所有的报文都已处理完毕,才能发送fin报文,所以不能ack报文和fin报文不能一起发送,所以需要四次挥手。
应用层是如何使用tcp/udp通信的
基于协议进行通信,协议层以上是应用层,应用层其实很复杂,我们 只是被websocket机制屏蔽了。
1.什么是socket?
抽象层,应用程序通过它来发送和接收消息;相当于应用程序打开了一个文件句柄,将数据写入到磁盘上
使用socket可以将我们的应用程序添加到网络上,并可以与其他的应用程序进行通信
不同类型的socket与底层的协议是有关系的
eg:两大类socket
(1)基于stream socket (tcp)
(2)基于datagram socket (udp)
2.通信的性能问题:
-
BIO — 阻塞IO client端从发送请求到接收返回消息中间的过程是阻塞等待的。
基于多线程 (消耗系统资源比较多,有性能瓶颈)eg:tomcat7之前的版本吧 -
NIO — 非阻塞IO
2.1 传统BIO模型(正常的通信过程)
TCP发送数据阻塞过程:
由于tcp响应服务器一次只能处理一个客户端请求,当一个客户端向一个已经被其他客户端占用的服务器发送链接请求时,虽然在连接建立后可以向服务端发送数据,但在服务端处理完之前的请求之前,不会对新的客户端做出响应,这种类型的服务器被称为“迭代服务器”。它会按顺序处理客户端请求,也就是服务端必须要处理完一个请求才能对下一个请求进行响应。实际应用中这种方式是不现实的,所以我们需要一种方法来独立处理每一个连接,并且他们之间互不干扰。所以这里引入了java的多线程技术。
(1)如何提高性能?
1)引入了TCP通信的全双工工作模式以及TCP的滑动窗口机制。这种机制也是依赖于发送端和接收端两个独立的Buffer和Buffer的填充状态。
tcp发送和接收消息都是基于缓冲区的。
tcp的全双工模式是以tcp滑动窗口的滑动和依赖于上面独立的两个缓冲区的填充状态来处理的。
接收缓冲区会把数据缓存到内核里面,如果recv一直没有读取的话,数据会一直存在接收缓冲区内。不管进程是否读取了socket,对端发来的数据都会经过内核接收并缓存到socket的内核缓存接收去。
接收端要做的读取数据的工作其实是把内核缓存中的数据复制到应用层的buffer里面;
进程调用发送端发送数据其实是把应用层缓冲区的数据复制到socket的内核发送缓冲区,然后在send层此时已经返回了。所以呢,send返回是,数据也不一定就发送到接收端。
发送端不知道数据其实已经发送过去了,(因为发送数据还在内核缓冲区里面),当接收缓冲区满了之后,接收端依旧没有确认,发送缓冲区便不能继续发送数据了,这是一个阻塞的过程。(当滑动窗口满了之后没有确认,发送端是不能发送数据的,即便发过来了之后接收方也会丢弃这些数据)。
(2)如何使用非阻塞提高性能。
非阻塞要解决的是I/O进程与socket解耦的问题,因此引入了事件机制。
关键在于–事件通知 ,通过事件机制去达到一个解耦。
这里可以联想老张用普通水壶和响水壶烧水的例子。
怎么做到的呢?
NIO的底层存在着一个IO机制去调度线程,
它不断扫描每一个socket缓冲区,
当发现某个写入缓冲区为空时会产生一个socket可写事件,此时程序便可以对socket进行一个写入,没有写入完毕可以等下次事件通知时候继续写。
反之,接收端去发现缓冲区有数据时候,产生一个可读事件通知,通知recv去读数据。
tips:
阻塞与非阻塞是针对服务端的;
同步与异步是针对客户端的;
2.2 NIO模型
上面讲的实际上是传统的BIO模型,一个请求一个线程的方式若涉及到上千个客户端访问时,会产生很多问题,比如扩展性和系统资源开销等,所以我们需要一种方式来轮询一组客户端,来查找哪个连接需要服务,这便是NIO。关于NIO有几个概念先了解一下。
缓冲区
在nio中,所有的数据都是用缓冲区处理,读写都是从从缓冲区来,任何时候访问NIO中的数据都是通过缓冲区进行操作。
通道
Channel通道,就像一个自来水管一样,可以通过它读取和写入数据,Channel是全双工的,所以数据是双向流动。
多路复用
Selector 多路复用器-- 是NIO的基础
selector会不断轮询注册商的channel,如果某个channel上有了新的tcp连接接入,读,写事件,这个channel就处于就绪状态,会被selector轮询出来,然后通过selectionkey可以获取就绪的channel进行I/O操作; 一个多路复用器可以同时轮询多个channel。通过这个机制可以计入成千上万个客户端。