网络I/O工作机制

数据从一台主机发送到网络中的另一台主机需要经过很多步骤.首先需要有相互沟通的意向.其次要有能够沟通的物理渠道(物理链路);是通过电话,还是直接面对面交流.再次,双方见面时语言要能够交流,而且双方说话的步调要一致,明白什么时候该自己说话,什么时候该对方说话(通信协议).

TCP状态转化(三次握手四次挥手)

(后面更新)

影响网络传输的因素

  1. 网络带宽
  2. 传输距离
  3. TCP拥塞控制:

JavaSocket的工作机制

什么是Socket?

Socket这个概念没有对应到一个具体的实体,它描述计算机之间完成相互通信的一种抽象功能.

主机A的应用程序要能和主机B的应用程序通信,必须通过Socket建立链接,而建立Socket连接必须由底层TCP/IP来建立TCP连接.建立TCP连接需要底层IP来寻址网络中的主机

但是在一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过TCP或者UPD的地址也就是端口号来指定.这样就可以通过一个Socket实例来唯一代表一个主机上的应用程序的通信链路了

建立通信链路

当客户端与服务端通信的时候,客户端首先要创建一个Socket实例,操作系统将为这个socket实例分配一个没有被使用的端口号,并创建一个包含本地地址,远程地址和端口号的套接字数据结构,这个数据结构将一直保存在系统中直到这个链接关闭.在创建Socket实例的构造函数正确返回之前,进行3次握手协议,TCP协议完成,Socket实例完成.否则抛出IOException错误

与之对应 的服务端将创建一个ServerSocket实例,创建ServerSocket比较简单,只要指定的端口没有被占用,一般实例都会创建成功.同时操作系统也会为ServerSocket实例创建一个底层数据结构,在这个数据结构中包含指定监听的端口号和包含监听地址的通配符号,通常情况下都是”*”,即监听所有地址

之后当调用accept()方法的时候,将进入阻塞状态,等待客户端的请求.

当一个新的请求到来时,将为这个连接创建一个新的套接字数据结构,该套接字数据结构的信息包含的地址和端口信息正是请求源地址和端口.

这个新创建的数据结构将会关联到ServerSocket实例一个未完成的链接数据结构列表中.

注意 此时服务端的与之对应的Socket实例并没有完成创建,而要等到与客户端的3次握手完成后,这个服务端的Socket实例才会返回,并将这个Socket实例对应的数据结构从未完成列表移到完成列表中

所以与ServerSocket所关联的列表中每个数据结构都代表与一个客户端建立的TCP链接

数据传输

传输数据是我们建立连接的主要目的,下面将详细介绍如何通过Socket传输数据

当链接已经建立成功的时候,服务端和客户端都有一个Socket实例

每一个Socket实例都有一个InputStream和一个OutputStream,并通过这两个对象传输数据

同时我们也知道网络I/O都是以字节流传输的.

当创建Socket对象的时候,操作系统会为InputStream和OutputStream分别分配至一定大小的缓存区

数据的写入和读取都是通过这个缓存区完成的

写入端将数据写入到OutputStream对应的SendQ队列中,当队列填满时,数据将被转移到另一端InputStream的RecvQ队列中

如果此时RecvQ已经满了,那么OutputStream的write方法将会阻塞,直到Recv有足够的空间容纳SendQ发送的数据

缓存区的大小十分影响传输效率,由于可能发生阻塞,所以网络I/O和磁盘I/O不同的地方就是写入和读取有一个协调的过程,如果在两边同时传送可能会产生死锁,NIO部分将介绍如何避免

NIO的工作方式

BIO带来的挑战

BIO即阻塞IO,不管是磁盘I/O还是网络I/O,数据在写入OutputStream或者InputStream读取时候都有可能阻塞,一旦有阻塞,线程将失去CPU的使用权,这在当前的大规模访问量和有性能要求的情况下是不能被接受的.虽然当前的网络I/O有一些解决办法,如一个客户端对应一个处理线程,出现阻塞时只是一个线程阻塞而不会影响其他线程工作.

采用线程池的方法可以减少线程创建和回收的成本,但是在一些使用场景下仍然时无法解决的.如服务端需要保持百万的http连接,但不是每时每刻这些连接都在传输数据,在这种情况下不可能同时创建这么多线程来保持连接.即使线程的数量不是问题,也仍然有一些问题是无法避免的.比如我们想给某些客户端更高的服务优先级时,很难通过设计线程的优先级别来完成.

另外一种情况是,每个客户端的请求在服务端可能需要访问一些竞争资源,这些客户端在不同的线程中,因此需要同步,要实现这种同步操作远比单线程复杂的多.以上这些情况都说明,我们需要另外一种新的I/O操作方式.

NIO的工作机制

两个关键类 Channel 和 Selector,他们是NIO中的两个核心概念

Channel可以看作一种交通工具

Selector比作一种车辆调度系统,负责监控每辆车当前的运行状态,是已经出站还是在路上.

还有一个Buffer类,可以比作汽车上的座位.在Buffer中我们可以控制容量,是否扩容以及如何扩容.