1、BIO介绍
1.1、BIO的概念
BIO(Blocking IO)同步阻塞IO模型,在JDK 1.4之前,建立网络链接采用的只有BIO的模型
需要服务端首先启动建立一个ServerSocket实例,然后客户端启动Socket实例对服务端进行连接通信,服务端通过调用accept方法等待接收客户端的连接请求,一旦接收到连接请求,就可以进行读写操作

在BIO编程中,相应的方法会产生阻塞:accept()、read()、write()、connect(),直至相关的操作等待完成之后才能继续后续代码处理,比如read操作,整个IO操作的过程都会阻塞,直至读取到数据之后才能继续执行(阻塞IO)
1.2、BIO支持高并发解决措施
如果BIO来考虑高并发问题,同时处理多个客户端的连接,就必须要使用多线程,即每次accept阻塞等待来自客户端的连接请求,一旦收到连接请求就将获取的套接字socket通过创建一个新的线程来处理IO操作,然后又继续通过accept接收客户端的连接

2、BIO编程
实现echo命令,客户端输入任何信息,服务端接收到信息并返回给客户端
2.1、只有一个客户端连接
服务端代码:
public class SingleServer {public static void main(String[] args) {ServerSocket serverSocket = null;Socket socket = null;InputStream inputStream = null;OutputStream outputStream = null;try {//创建ServerSocket实例并绑定端口serverSocket = new ServerSocket(7777);System.out.println("服务端绑定端口7777并启动啦");//等待并接受客户端连接socket = serverSocket.accept();//getRemoteSocketAddress() 获取连接的对方的IP和端口System.out.println("有新的客户端连接:" + socket.getRemoteSocketAddress());//进行读写操作inputStream = socket.getInputStream(); //读取客户端数据outputStream = socket.getOutputStream();//给客户端发送数据byte[] bytes = new byte[100];//可多次接受数据int len = 0;//判断当前的一个消息是否结束String recv = null;while ((len = inputStream.read(bytes)) != -1) {recv = new String(bytes, 0, len);System.out.println("客户端发送消息:" + recv);//封装返回给客户端outputStream.write(("[echo]" + recv + "\n").getBytes());outputStream.flush();//判断当前业务结束标识:客户端发送exitif (recv != null && "exit".equals(recv.trim())) {System.out.println("客户端断开即将断开连接");break;}}} catch (IOException e) {e.printStackTrace();} finally {try {//关闭资源if (serverSocket != null) serverSocket.close();if (socket != null) socket.close();if (inputStream != null) inputStream.close();if (outputStream != null) outputStream.close();} catch (IOException e) {e.printStackTrace();}}} }客户端代码:
public class Client {public static void main(String[] args) {Socket socket = null;InputStream inputStream = null;OutputStream outputStream = null;//获取从键盘输入的内容Scanner scanner = new Scanner(System.in);scanner.useDelimiter("\n");try {//创建socket实例socket = new Socket();//连接服务端socket.connect(new InetSocketAddress("127.0.0.1",7777));System.out.println("客户端连接服务端成功");//读写操作outputStream = socket.getOutputStream();inputStream = socket.getInputStream();byte[] bytes = new byte[100];while (scanner.hasNext()) {//获取键盘内容String msg = scanner.next();if (msg == null || "".equals(msg.trim())) continue;//发给服务端outputStream.write(msg.getBytes());outputStream.flush();//接收服务端返回的数据int num = inputStream.read(bytes);System.out.println("服务端返回:"+new String(bytes,0,num));if ("exit".equals(msg.trim())) {//特定结束System.out.println("客户端主动断开连接啦");break;}}} catch (IOException e) {e.printStackTrace();} finally {//关闭资源try {if (socket != null) socket.close();if (outputStream != null) outputStream.close();if (inputStream != null) inputStream.close();} catch (Exception e) {}}} }
2.2、BIO支持高并发
/*** desc:服务端* BIO+ 多线程方案:解决高并发问题* 主线程主要职责:监听客户端的连接,每有一个新客户端的连接获取到连接实例socket,新创建子线程来处理读写IO操作* 子线程职责:处理IO读写操作*/public class MultiThreadServer {public static void main(String[] args) {ServerSocket serverSocket = null;try {//创建ServerSocket实例serverSocket = new ServerSocket(9999);System.out.println("服务端绑定端口9999并启动啦");//等待多客户端的连接,不断循环等待客户端的连接while (true) {Socket socket = serverSocket.accept();System.out.println("有新用户连接啦:"+socket.getRemoteSocketAddress());//每一个新用户连接都交给一个新线程来处理,重点:将Socket交给子线程new Thread(new RunableHandler(socket)).start();}} catch (IOException e) {}}
}
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;public class RunableHandler implements Runnable {private Socket socket;//socket实例,每一个线程单独处理一个socketpublic RunableHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {try {//进行读写操作InputStream inputStream = socket.getInputStream(); //读取客户端数据OutputStream outputStream = socket.getOutputStream();//给客户端发送数据byte[] bytes = new byte[100];//可多次接受数据int len = 0;//判断当前的一个消息是否结束String recv = null;while ((len = inputStream.read(bytes)) != -1) {recv = new String(bytes, 0, len);System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 发送消息:" + recv);//封装返回给客户端outputStream.write(("[echo]" + recv + "\n").getBytes());outputStream.flush();//判断当前业务结束标识:客户端发送exitif (recv != null && "exit".equals(recv.trim())) {System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 断开即将断开连接");break;}}//关闭资源socket.close();inputStream.close();outputStream.close();} catch (Exception e) {}}
}

通过对BIO的高并发的实现可知:在BIO实现的过程中,为了支持多客户端请求,每一个客户端的连接请求都会新分配一个线程来做处理。并不意味着线程创建的越多越好,每创建一个线程,需要消耗的内存资源,而内存资源是有限的,另线程越多,在系统进行调度的过程中上下文的切换也会消耗性能, 通过BIO来实现高并发,创建的线程越多反而会是性能大打折扣。

















