一、网络编程基础回顾
1. Socket
Socket
本身有“插座”的意思,不是Java中特有的概念,而是一个语言无关的标准,任何可以实现网络编程的编程语言都有Socket
。在Linux
环境下,用于表示进程间网络通信的特殊文件类型,其本质为内核借助缓冲区形成的伪文件。既然是文件,那么理所当然的,我们可以使用文件描述符引用套接字。
与管道类似的,Linux
系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程间通信,而套接字多应用于网络进程间数据的传递。
可以这么理解:Socket
就是网络上的两个应用程序通过一个双向通信连接实现数据交换的编程接口API。
Socket
通信的基本流程具体步骤如下所示:
(1)服务端通过Listen
开启监听,等待客户端接入。
(2)客户端的套接字通过Connect
连接服务器端的套接字,服务端通过Accept
接收客户端连接。在connect-accept
过程中,操作系统将会进行三次握手。
(3)客户端和服务端通过write
和read
发送和接收数据,操作系统将会完成TCP
数据的确认、重发等步骤。
(4)通过close
关闭连接,操作系统会进行四次挥手。
针对Java编程语言,java.net
包是网络编程的基础类库。其中ServerSocket
和Socket
是网络编程的基础类型。
SeverSocket
是服务端应用类型。Socket
是建立连接的类型。当连接建立成功后,服务器和客户端都会有一个Socket
对象示例,可以通过这个Socket
对象示例,完成会话的所有操作。对于一个完整的网络连接来说,Socket
是平等的,没有服务器客户端分级情况。
2. IO模型介绍
对于一次IO操作,数据会先拷贝到内核空间中,然后再从内核空间拷贝到用户空间中,所以一次read
操作,会经历两个阶段:
(1)等待数据准备
(2)数据从内核空间拷贝到用户空间
基于以上两个阶段就产生了五种不同的IO模式。
- 阻塞IO:从进程发起IO操作,一直等待上述两个阶段完成,此时两阶段一起阻塞。
- 非阻塞IO:进程一直询问IO准备好了没有,准备好了再发起读取操作,这时才把数据从内核空间拷贝到用户空间。第一阶段不阻塞但要轮询,第二阶段阻塞。
- 多路复用IO:多个连接使用同一个select去询问IO准备好了没有,如果有准备好了的,就返回有数据准备好了,然后对应的连接再发起读取操作,把数据从内核空间拷贝到用户空间。两阶段分开阻塞。
- 信号驱动IO:进程发起读取操作会立即返回,当数据准备好了会以通知的形式告诉进程,进程再发起读取操作,把数据从内核空间拷贝到用户空间。第