如果有人再问你 Java IO,把这篇文章砸他头上( 三 )


6.1、Socket 简介在现实中,Socket 这个概念没有一个具体的实体,它是描述计算机之间完成相互通信一种抽象定义 。
打个比方,可以把 Socket 比作为两个城市之间的交通工具,有了它,就可以在城市之间来回穿梭了 。并且,交通工具有多种,每种交通工具也有相应的交通规则 。Socket 也一样,也有多种 。大部分情况下我们使用的都是基于 TCP/IP 的流套接字,它是一种稳定的通信协议 。
典型的基于 Socket 通信的应用程序场景,如下图:

如果有人再问你 Java IO,把这篇文章砸他头上

文章插图
 
主机 A 的应用程序要想和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层 TCP/IP 协议来建立 TCP 连接 。
6.2、建立通信链路我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定 。这样就可以通过一个 Socket 实例代表唯一一个主机上的一个应用程序的通信链路了 。
为了准确无误地把数据送达目标处,TCP 协议采用了三次握手策略,如下图:
如果有人再问你 Java IO,把这篇文章砸他头上

文章插图
 
其中,SYN 全称为 Synchronize Sequence Numbers,表示同步序列编号,是 TCP/IP 建立连接时使用的握手信号 。
ACK 全称为 Acknowledge character,即确认字符,表示发来的数据已确认接收无误 。
在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN + ACK 应答表示接收到了这个消息,最后客户机再以 ACK 消息响应 。
这样在客户机和服务器之间才能建立起可靠的 TCP 连接,数据才可以在客户机和服务器之间传递 。
简单流程如下:
  • 发送端 –(发送带有 SYN 标志的数据包 )–> 接受端(第一次握手);
  • 接受端 –(发送带有 SYN + ACK 标志的数据包)–> 发送端(第二次握手);
  • 发送端 –(发送带有 ACK 标志的数据包) –> 接受端(第三次握手);
完成三次握手之后,客户端应用程序与服务器应用程序就可以开始传送数据了 。
传输数据是我们建立连接的主要目的,如何通过 Socket 传输数据呢?
6.3、传输数据当客户端要与服务端通信时,客户端首先要创建一个 Socket 实例,默认操作系统将为这个 Socket 实例分配一个没有被使用的本地端口号,并创建一个包含本地、远程地址和端口号的套接字数据结构,这个数据结构将一直保存在系统中直到这个连接关闭 。
如果有人再问你 Java IO,把这篇文章砸他头上

文章插图
 
与之对应的服务端,也将创建一个 ServerSocket 实例,ServerSocket 创建比较简单,只要指定的端口号没有被占用,一般实例创建都会成功,同时操作系统也会为 ServerSocket 实例创建一个底层数据结构,这个数据结构中包含指定监听的端口号和包含监听地址的通配符,通常情况下都是*即监听所有地址 。
之后当调用 accept() 方法时,将进入阻塞状态,等待客户端的请求 。
如果有人再问你 Java IO,把这篇文章砸他头上

文章插图
 
我们先启动服务端程序,再运行客户端,服务端收到客户端发送的信息,服务端打印结果如下:
如果有人再问你 Java IO,把这篇文章砸他头上

文章插图
 
注意,客户端只有与服务端建立三次握手成功之后,才会发送数据,而 TCP/IP 握手过程,底层操作系统已经帮我们实现了!
当连接已经建立成功,服务端和客户端都会拥有一个 Socket 实例,每个 Socket 实例都有一个 InputStream 和 OutputStream,正如我们前面所说的,网络 I/O 都是以字节流传输的,Socket 正是通过这两个对象来交换数据 。
当 Socket 对象创建时,操作系统将会为 InputStream 和 OutputStream 分别分配一定大小的缓冲区,数据的写入和读取都是通过这个缓存区完成的 。
写入端将数据写到 OutputStream 对应的 SendQ 队列中,当队列填满时,数据将被发送到另一端 InputStream 的 RecvQ 队列中,如果这时 RecvQ 已经满了,那么 OutputStream 的 write 方法将会阻塞直到 RecvQ 队列有足够的空间容纳 SendQ 发送的数据 。
值得特别注意的是,缓存区的大小以及写入端的速度和读取端的速度非常影响这个连接的数据传输效率,由于可能会发生阻塞,所以网络 I/O 与磁盘 I/O 在数据的写入和读取还要有一个协调的过程,如果两边同时传送数据时可能会产生死锁的问题 。


推荐阅读