概述
服务端提供 IP 和监听端口,客户端通过连接操作想服务端监听的地址发起连接请求,通过三次握手连接,如果连接成功建立,双方就可以通过套接字进行通信。
传统的同步阻塞模型开发中,<strong>ServerSocket 负责绑定 IP 地址,启动监听端口;<strong>Socket 负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。
传统BIO 通信模型:采用BIO 通信模型的服务端,通常由一个独立的 Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理没处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答模型。
该模型最大的问题就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈 1:1 的正比关系,Java 中的线程也是比较宝贵的系统资源,线程数量快速膨胀后,系统的性能将急剧下降,随着访问量的继续增大,系统最终就 死- 掉- 了。
为了改进这种一连接一线程的模型,我们可以使用线程池来管理这些线程,实现1个或多个线程处理N个客户端的模型(但是底层还是使用的同步阻塞I/O),通常被称为“伪异步 I/O 模型“。
BIO通信代码demo
服务端:
<code>package cn.pine.bio;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;/** *@author pine * *类说明:Bio通信的服务端 */public class Server { public static void main(String[] args) throws IOException { /*服务器必备*/ ServerSocket serverSocket = new ServerSocket(); /*绑定监听端口*/ serverSocket.bind(new InetSocketAddress(10001)); System.out.println("Server start......."); while(true){ new Thread(new ServerTask(serverSocket.accept())).start(); } } private static class ServerTask implements Runnable{ private Socket socket = null; public ServerTask(Socket socket) { this.socket = socket; } @Override public void run() { /*拿和客户端通讯的输入输出流*/ try(ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())){ /*服务器的输入*/ String userName = inputStream.readUTF(); System.out.println("Accept clinet message:"+userName); outputStream.writeUTF("Hello,"+userName); outputStream.flush(); }catch (Exception e){ e.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}/<code>
客户端:
<code>package cn.enjoyedu.bio;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.InetSocketAddress;import java.net.Socket;/** *@author pine * *类说明:Bio通信的客户端 */public class Client { public static void main(String[] args) throws IOException { //客户端启动必备 Socket socket = null; //实例化与服务端通信的输入输出流 ObjectOutputStream output = null; ObjectInputStream input = null; //服务器的通信地址 InetSocketAddress addr = new InetSocketAddress("127.0.0.1",10001); try{ socket = new Socket(); /*连接服务器*/ socket.connect(addr); output = new ObjectOutputStream(socket.getOutputStream()); input = new ObjectInputStream(socket.getInputStream()); /*向服务器输出请求*/ output.writeUTF("Mark"); output.flush(); //接收服务器的输出 System.out.println(input.readUTF()); }finally{ if (socket!=null) socket.close(); if (output!=null) output.close(); if (input!=null) input.close(); } }}/<code>
伪异步IO代码demo
服务端:
<code>package cn.enjoyedu.bio;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.InetSocketAddress;import java.net.ServerSocket;import java.net.Socket;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/** *@author Mark老师 享学课堂 https://enjoy.ke.qq.com * *类说明:Bio通信的服务端 */public class ServerPool { private static ExecutorService executorService = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); public static void main(String[] args) throws IOException { //服务端启动必备 ServerSocket serverSocket = new ServerSocket(); //表示服务端在哪个端口上监听 serverSocket.bind(new InetSocketAddress(10001)); System.out.println("Start Server ...."); try{ while(true){ executorService.execute(new ServerTask(serverSocket.accept())); } }finally { serverSocket.close(); } } //每个和客户端的通信都会打包成一个任务,交个一个线程来执行 private static class ServerTask implements Runnable{ private Socket socket = null; public ServerTask(Socket socket){ this.socket = socket; } @Override public void run() { //实例化与客户端通信的输入输出流 try(ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream()); ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())){ //接收客户端的输出,也就是服务器的输入 String userName = inputStream.readUTF(); System.out.println("Accept client message:"+userName); //服务器的输出,也就是客户端的输入 outputStream.writeUTF("Hello,"+userName); outputStream.flush(); }catch(Exception e){ e.printStackTrace(); }finally { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }}/<code>
客户端同BIO一样。
閱讀更多 JAVA破局之路 的文章