Java随笔记 - Java BIO,Socket通信

Java随笔记 - Java BIO,Socket通信

Java I/O中的Socket通信

  • 在上一篇博客里,对NIO进行了简单介绍,主要是其中的三个核心概念,详情见(Java随笔记 - Java NIO的初步认识,NIO的三个核心概念,Channel和Buffer的简单使用)。本来应该写一篇详解flip函数的文章的,这里插叙一下Java IO的几个点。

  • 早期的Java I/O库,所实现的是一种同步阻塞式的I/O模型,因此也被称为BIO(Blocking I/O)。所谓的同步阻塞式I/O,就是程序在遇到I/O操作时,需要阻塞当前程序的执行,等待I/O操作的完成,再继续执行后续的逻辑操作。也就是说,在等待I/O操作完成的这段时间,线程处于阻塞阻塞状态,不能去做别的事情。对于Socket通信而言,数据的发送和接收都需要阻塞当前的线程,等待数据传输完成后才能继续执行后续的任务。

典型的编程模型

Java随笔记 - Java BIO,Socket通信

  • 服务端通过ServerSocket对象负责在指定的端口监听来自客户端的连接请求。客户端通过创建Socket对象,对服务端发起连接请求。连接成功后,双方通过输入流和输出流进行通信。双方在数据的发送和读取过程中,线程调用Socket的相关API后,都会进入阻塞状态,知道数据的发送或者读取完成。通过上图所展示的简单模型,可以看到,客户端需要创建一个Socket对象来对服务端的ServerSocket发起连接请求,而服务端则用一个专门的线程Acceptor不断循环调用accept( )方法来检查并接收新的连接请求。一旦有新的连接请求进来,该方法会返回一个与这个新的连接对应的Socket对象,通过该对象可以与客户端进行数据传输。然后Acceptor线程会创建一个新的处理线程,把这个新的Socket对象交给它,之后就由这个处理线程通过Socket对象与客户端进行通信和逻辑处理。

  • 在这一编程模型中,一个线程负责处理一个客户端连接,编程逻辑简单、容易理解。但是,其存在的问题也很明显,没建立一条连接就要开启一个相应的线程来进行处理,那如果连接的数目巨大呢?根本不可能同时支撑大量的连接数,而且在客户端数量较大时,服务器的性能也会有所下降。

Socket编程中关键API概述

  • ServerSocket

    • 这是服务端用于监听Socket连接的类,它的构造函数可以指定要监听的IP地址和端口号,也可以在实例化之后通过bind方法进行指定。而不管使用哪个方法进行指定,都只能指定一次,不可进行后续的修改

    • ServerSocket的accept( )方法用于接收来自客户端的连接请求,调用该方法后,线程会被阻塞,直到有连接进来

  • InetAddress和InetSocketAddress

    • InetAddress是Java对IP地址的封装,而InetSocketAddress则是InetAddress的子类,也是其唯一子类,增加了对端口号的封装
  • Socket

    • Socket套接字用于对Socket连接进行读写操作,客户端在发起连接时,可以在构造函数中指定服务端的IP和端口,也可以通过connect( )方法进行指定

补充

  • TCP协议是面向连接的流协议,而Socket并不提供直接的数据收发方法,数据的收发需要另外通过输入/出流的读写操作来进行。输入流用于读取数据,输出流用于发送数据。Socket的getInputStream( )和getOutputStream( )方法分别用于获取输入流和输出流。相应的,在输入流和输出流的处理过程中,操作未完成之前线程是阻塞的。

  • 下篇博客给两个使用BIO进行数据传输的代码示例