找工作-——网络IO

网络层

主要任务是把网络协议数据单元或分组从源计算机经过适当的路径发送到目的地计算机。从源计算机到目的计算机可能要经过若干个中间节点,这需要在通信子网中进行路由选择。

网络层与数据链路层有很大的差别,数据链路层仅把数据帧从线缆或信道的一端传送到另一端(即在相邻节点间进行数据传送),网络层的目的是实现两个端系统之间的数据透明传送,具体功能包括路由选择、阻塞控制和网际互连等。

网络层向上只提供简单灵活的、无连接的、尽最大努力交付的数据报服务。
无连接的:网络在发送分组时不需要先建立连接。每一个分组(即 IP 数据报)独立发送,与其前后的分组无关(不进行编号)。
尽最大努力交付:网络层不提供服务质量的承诺。即所传送的分组可能出错、丢失、重复和失序(不按序到达终点),当然也不保证分组传送的时限。 
尽最大努力交付的好处:
  由于传输网络不提供端到端的可靠传输服务,这就使网络中的路由器可以做得比较简单,而且价格低廉(与电信网的交换机相比较)。
  如果主机(即端系统)中的进程之间的通信需要是可靠的,那么就由网络的主机中的运输层负责(包括差错处理、流量控制等)。
  采用这种设计思路的好处是:网络的造价大大降低,运行方式灵活,能够适应多种应用。
  因特网能够发展到今日的规模,充分证明了当初采用这种设计思路的正确性。 
网际协议 IP 是 TCP/IP 体系中两个最主要的协议之一。与 IP 协议配套使用的还有四个协议:地址解析协议 ARP (Address Resolution Protocol)、逆地址解析协议 RARP(Reverse Address Resolution Protocol)、网际控制报文协议 ICMP(Internet Control Message Protocol)、网际组管理协议 IGMP(Internet Group Management Protocol)
地址解析协议 ARP:不管网络层使用的是什么协议,在实际网络的链路上传送数据帧时,最终还是必须使用硬件地址。 当主机 A 欲向本局域网上的某个主机 B 发送 IP 数据报时,就先在其 ARP 高速缓存中查看有无主机 B 的 IP 地址。如有,就可查出其对应的硬件地址,再将此硬件地址写入 MAC 帧,然后通过局域网将该 MAC 帧发往此硬件地址。
IP 数据报的格式 :
  一个 IP 数据报由首部和数据两部分组成。
  首部的前一部分是固定长度,共 20 字节,是所有 IP 数据报必须具有的。
  在首部的固定部分的后面是一些可选字段,其长度是可变的。
  首部长度最大可达60字节。
传输层
主要任务:
根据下面通信子网的特性最佳的利用网络资源,并以可靠和经济的方式在两端主机的进程之间,建立一条运输连接,以透明地传送报文,也就是说,运输层向上一层进行通信的两个进程之间提供一个可靠的端到端的服务,使它们看不见运输层以下的数据通信细节。在通信子网内的各个交换节点以及连接各通信子网的路由器都没有运输层。运输层只能存在于通信子网外面的主机中。运输层以上的各层就不再关心信息传输问题了。 
从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层。
当网络的边缘部分中的两个主机使用网络的核心部分的功能进行端到端的通信时,只有位于网络边缘部分的主机的协议栈才有运输层,而网络核心部分中的路由器在转发分组时都只用到下三层的功能。 
运输层为应用进程之间提供端到端的逻辑通信(但网络层是为主机之间提供逻辑通信)。
运输层还要对收到的报文进行差错检测。
运输层需要有两种不同的运输协议,即面向连接的 TCP 和无连接的 UDP。   
当运输层采用面向连接的 TCP 协议时,尽管下面的网络是不可靠的(只提供尽最大努力服务),但这种逻辑通信信道就相当于一条全双工的可靠信道。
当运输层采用无连接的 UDP 协议时,这种逻辑通信信道是一条不可靠信道。 
UDP 在传送数据之前不需要先建立连接。对方的运输层在收到 UDP 报文后,不需要给出任何确认。虽然 UDP 不提供可靠交付,但在某些情况下 UDP 是一种最有效的工作方式。
TCP 则提供面向连接的服务。TCP 不提供广播或多播服务。由于 TCP 要提供可靠的、面向连接的运输服务,因此不可避免地增加了许多的开销。这不仅使协议数据单元的首部增大很多,还要占用许多的处理机资源。  
用户数据报协议 UDP(User Datagram Protocol)
UDP 传送的数据单位协议是 UDP 报文或用户数据报。 
UDP 只在 IP 的数据报服务之上增加了很少一点的功能,即端口的功能和差错检测的功能。
  UDP 是无连接的,即发送数据之前不需要建立连接。
  UDP 使用尽最大努力交付,即不保证可靠交付,同时也不使用拥塞控制。
  UDP 是面向报文的。(发送方 UDP 对应用程序交下来的报文,在添加首部后就向下交付 IP 层。UDP 对应用层交下来的报文,既不合并,也不拆分,而是保留这些报文的边界。应用层交给 UDP 多长的报文,UDP 就照样发送,即一次发送一个报文。接收方 UDP 对 IP 层交上来的 UDP 用户数据报,在去除首部后就原封不动地交付上层的应用进程,一次交付一个完整的报文。应用程序必须选择合适大小的报文。)
  UDP 没有拥塞控制,很适合多媒体通信的要求。
  UDP 支持一对一、一对多、多对一和多对多的交互通信。
  UDP 的首部开销小,只有 8 个字节
传输控制协议 TCP(Transmission Control Protocol)
TCP 传送的数据单位协议是 TCP 报文段(segment)
TCP 最主要的特点
  • TCP 是面向连接的运输层协议。
  • 每一条 TCP 连接只能有两个端点(endpoint),每一条 TCP 连接只能是点对点的(一对一)。
  • TCP 提供可靠交付的服务。
  • TCP 提供全双工通信。
  • 面向字节流。

TCP 连接是一条虚连接而不是一条真正的物理连接。

TCP 根据对方给出的窗口值和当前网络拥塞的程度来决定一个报文段应包含多少个字节(UDP 发送的报文长度是应用进程给出的)。

TCP 可把太长的数据块划分短一些再传送。TCP 也可等待积累有足够多的字节后再构成报文段发送出去。

使用上述的确认和重传机制,我们就可以在不可靠的传输网络上实现可靠的通信。
接收方一般采用累积确认的方式。即不必对收到的分组逐个发送确认,而是对按序到达的最后一个分组发送确认,这样就表示:到这个分组为止的所有分组都已正确收到了。
累积确认有的优点是:容易实现,即使确认丢失也不必重传。缺点是:不能向发送方反映出接收方已经正确收到的所有分组的信息。
在某段时间,若对网络中某资源的需求超过了该资源所能提供的可用部分,网络的性能就要变坏——产生拥塞(congestion)。
拥塞控制所要做的都有一个前提,就是网络能够承受现有的网络负荷。
拥塞控制是一个全局性的过程,涉及到所有的主机、所有的路由器,以及与降低网络传输性能有关的所有因素。
流量控制往往指在给定的发送端和接收端之间的点对点通信量的控制。
流量控制所要做的就是抑制发送端发送数据的速率,以便使接收端来得及接收。 
建立TCP连接:
1)第一次握手:建立连接时,客户端A发送SYN包(SYN=j)到服务器B,并进入SYN_SEND状态,等待服务器B确认。
2)第二次握手:服务器B收到SYN包,必须确认客户A的SYN(ACK=j+1),同时自己也发送一个SYN包(SYN=k),即SYN+ACK包,此时服务器B进入SYN_RECV状态。
3)第三次握手:客户端A收到服务器B的SYN+ACK包,向服务器B发送确认包ACK(ACK=k+1),此包发送完毕,客户端A和服务器B进入ESTABLISHED状态,完成三次握手。
完成三次握手,客户端与服务器开始传送数据。
断开TCP连接:
由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。首先进行关闭的一方将执行主动关闭,而另一方执行被动关闭。

(1)客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送(报文段4)。
(2)服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1(报文段5)。和SYN一样,一个FIN将占用一个序号。
(3)服务器B关闭与客户端A的连接,发送一个FIN给客户端A(报文段6)。
(4)客户端A发回ACK报文确认,并将确认序号设置为收到序号加1(报文段7)。

中断连接端可以是Client端,也可以是Server端

假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"我Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK,"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"。Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。“,Server端收到ACK后,"就知道可以断开连接了"。Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。Ok,TCP连接就这样关闭了!

1.为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

这是因为服务端的LISTEN状态下的SOCKET当收到SYN报文的建连请求后,它可以把ACK和SYN(ACK起应答作用,而SYN起同步作用)放在一个报文里来发送。但关闭连接时,当收到对方的FIN报文通知时,它仅仅表示对方没有数据发送给你了;但未必你所有的数据都全部发送给对方了,所以你可以未必会马上会关闭SOCKET,也即你可能还需要发送一些数据给对方之后,再发送FIN报文给对方来表示你同意现在可以关闭连接了,所以它这里的ACK报文和FIN报文多数情况下都是分开发送的。

2.为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

这是因为虽然双方都同意关闭连接了,而且握手的4个报文也都协调和发送完毕,按理可以直接回到CLOSED状态(就好比从SYN_SEND状态到ESTABLISH状态那样);但是因为我们必须要假想网络是不可靠的,你无法保证你最后发送的ACK报文会一定被对方收到,因此对方处于LAST_ACK状态下的SOCKET可能会因为超时未收到ACK报文,而重发FIN报文,所以这个TIME_WAIT状态的作用就是用来重发可能丢失的ACK报文。

粘包问题可以用下图来表示:

1. 先接收到data1,然后接收到data2.

2. 一次性接收到了data1和data2的全部数据. 

3. 先接收到了data1的全部数据和data2的部分数据,然后接收到了data2的余下的数据. 

4. 先接收到data1的部分数据,然后接收到data1余下的部分以及data2的全部.

后三种就是粘包了

粘包出现原因

在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程)
1 发送端需要等缓冲区满才发送出去,造成粘包
2 接收方不及时接收缓冲区的包,造成多个包接收

什么时候需要考虑粘包问题?

1 :  如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题
2:如果发送数据无结构,如文件传输,这样发送方只管发送,接收方只管接收存储就ok,也不用考虑粘包
3:如果双方建立连接,需要在连接后一段时间内发送不同结构数据,如连接后,有好几种结构:
1)"hello give me sth abour yourself" 
2)"Don't give me sth abour yourself" 
那这样的话,如果发送方连续发送这个两个包出去,接收方一次接收可能会是"hello give me sth abour yourselfDon't give me sth abour yourself" 这样接收方就傻了,到底是要干嘛?不知道,因为协议没有规定这么诡异的字符串,所以要处理把它分包,怎么分也需要双方组织一个比较好的包结构,所以一般可能会在头加一个数据长度之类的包,以确保接收。

TCP短连接和UDP不需要考虑粘包问题。

解决办法

本质上是要在应用层维护消息与消息的边界(下文的“包”可以认为是“消息”)
1、定长包
2、包尾加\r\n(ftp)缺点是如果消息本身含有\r\n字符,则也分不清消息的边界
3、包头加上包体长度
4、更复杂的应用层协议

HTTP:

HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。

HTTP 的主要特点

  HTTP 1.0 协议是无状态的(stateless)。
  HTTP 协议本身也是无连接的,虽然它使用了面向连接的 TCP 向上提供的服务。

在HTTP1.0协议中,客户端与web服务器建立连接后,只能获得一个web资源。
在HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。

HTTP请求包括的内容:

一个完整的HTTP请求包括如下内容:一个请求行、若干消息头、以及实体内容

accept:浏览器通过这个头告诉服务器,它所支持的数据类型
  Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
  Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
  Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
  Host:浏览器通过这个头告诉服务器,想访问哪台主机
  If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
  Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的  防盗链
  Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接

HTTP响应:

个HTTP响应代表服务器向客户端回送的数据,它包括: 一个状态行、若干消息头、以及实体内容 。

HTTP响应中的常用响应头(消息头)
  Location: 服务器通过这个头,来告诉浏览器跳到哪里
  Server:服务器通过这个头,告诉浏览器服务器的型号
  Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
  Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
  Content-Language: 服务器通过这个头,告诉浏览器语言环境
  Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
  Refresh:服务器通过这个头,告诉浏览器定时刷新
  Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
  Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
  Expires: -1  控制浏览器不要缓存
  Cache-Control: no-cache  
  Pragma: no-cache

NIO

阻塞I/O已有了一定了解,我们知道阻塞I/O在调用InputStream.read()方法时是阻塞的,它会一直等到数据到来时(或超时)才会返回;同样,在调用ServerSocket.accept()方法时,也会一直阻塞到有客户端连接才会返回,每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。

阻塞I/O通信模型,它的两点缺点:
1. 当客户端多时,会创建大量的处理线程。且每个线程都要占用栈空间和一些CPU时间
2. 阻塞可能带来频繁的上下文切换,且大部分上下文切换可能是无意义的。

 NIO的工作原理:

NIO通过Selector、Channel、和Buffer来实现非阻塞IO,NIO非阻塞的实现主要采用了Reactor(反应器)设计模式,这个设计模式与Observer设计模式类似,只不过Observer设计模式只能处理一个时间源,而Reactor设计模式可以处理多个事件源。Channel是一个双向的非阻塞通道,在通道的两边都可以进行数据的读写工作。Selector实现了用一个线程来管理多个通道(采用了复用与解复用的方式使得一个线程能够管理多个通道,即可以把多个流合并成为一个流,或者把一个流分成多个流的方式),它类似于一个观察者。在实现时需要把Channel的IO事件(例如connect,read,write等)注册给Selector。Selector内部实现原理为:对所有注册的Channel进行轮询访问,一旦轮询到一个Channel有注册的事件发生,例如有数据来了,它就通过传回SelectionKey的方式来通知开发人员对Channel进行数据的读或写操作。这种通过轮询的方式在处理多线程时不需要上下文的切换,而采用多线程的实现方式在线程之间切换需要上下文的切换,同时也需要进行压栈与弹栈的操作,因此NIO具有较高的执行效率。
1. 由一个专门的线程来处理所有的 IO 事件,并负责分发。 
2. 事件驱动机制:事件到的时候触发,而不是同步的去监视事件。 

Java NIO的服务端只需启动一个专门的线程来处理所有的 IO 事件

posted @ 2016-03-04 18:07  cjt1991  阅读(218)  评论(0编辑  收藏  举报