Java中IO模型-BIO模型
1、BIO介绍
BIO(Blocking IO)同步阻塞IO模型,在JDK 1.4之前,建立网络链接采用的只有BIO的模型。
需要服务端首先启动建立一个ServerSocket实例,然后客户端启动Socket实例对服务端进行连接通信,服务端通过调用accept方法等待接收客户端的连接请求,一旦接收到连接请求,就可以进行读写操作。
在BIO编程中,相应的方法会产生阻塞:accept()、read()、write()、connect(),直至相关的操作等待完成之后才能继续后续代码处理,比如read操作,整个IO操作的过程都会阻塞,直至读取到数据之后才能继续执行(阻塞IO)。
如果BIO来考虑高并发问题,同时处理多个客户端的连接,就必须要使用多线程,即每次accept阻塞等待来自客户端的连接请求,一旦收到连接请求就将获取的套接字socket通过创建一个新的线程来处理IO操作,然后又继续通过accept接收客户端的连接。
2、BIO编程
实现echo命令,客户端输入任何信息,服务端接收到信息并返回给客户端。
/**
* desc:服务端
* 实现echo功能,接收客户端命令并返回
*
* @user:西财彭于晏 */
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();
//判断当前业务结束标识:客户端发送exit
if (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();
}
}
}
}
/**
* desc:客户端
* @user:西财彭于晏
*/
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) {}
}
}
}
BIO支持高并发
/**
* desc:服务端
* BIO+ 多线程方案:解决高并发问题
* 主线程主要职责:监听客户端的连接,每有一个新客户端的连接获取到连接实例socket,新创建子线程来处理读写IO操作
* 子线程职责:处理IO读写操作
* @user:gongdezhe
* @date:2021/9/11
*/
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实例,每一个线程单独处理一个socket
public RunableHandler(Socket socket) {
this.socket = socket;
}
@Override
public 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();
//判断当前业务结束标识:客户端发送exit
if (recv != null && "exit".equals(recv.trim())) {
System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 断开即将断开连接");
break;
}
}
//关闭资源
socket.close();
inputStream.close();
outputStream.close();
} catch (Exception e) {}
}
}
多线程执行结果打印:
例题:
BIO支持多线程处理:
模拟Linux命令:客户端给定命令:服务端来进行操作,并将结果返回
pwd;显示当前的位置全路径 File.getpath()
makedir:XXX:创建新的路径 file.makedir
ls:展示当前路径下所有的内容 file.list()
exit:退出登录
随机输入其他信息:返回无效命令即可
import java.net.ServerSocket;
import java.net.Socket;
public class TheardServer { //服务端
public static void main(String[] args) {
ServerSocket serverSocket = null;
try{
serverSocket = new ServerSocket(5555);
System.out.println("服务端绑定端口5555并启动成功!");
while(true){
Socket socket = new Socket();
socket = serverSocket.accept();
System.out.println("有新用户连接 "+socket.getRemoteSocketAddress());
new Thread(new RunnableDemo(socket)).start();
}
}catch(Exception e){
e.printStackTrace();
}
}
}
import java.io.File; //服务端用多线程实现
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Arrays;
import java.util.Scanner;
public class RunnableDemo implements Runnable{
private Socket socket;
public RunnableDemo(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
Scanner scanner = new Scanner(System.in);
int len = 0;
String msg = null;
byte[] bytes = new byte[100];
while ((len = inputStream.read(bytes))!=-1){
msg = new String(bytes,0,len);
System.out.println("客户端 "+socket.getRemoteSocketAddress()+" 发送消息 "+msg);
outputStream.write(("[echo] "+msg+"\n").getBytes());
//outputStream.write((scanner.next()).getBytes());
File file=new File(".");
if(msg!=null&&msg.equals("exit")){
System.out.println("客户端 "+socket.getRemoteSocketAddress()+" 即将断开连接!");
break;
} else if(msg!=null&&msg.equals("pwd")){
String b=file.getAbsolutePath();
System.out.println("服务端:当前路径为:"+b);
} else if(msg.startsWith("mkdir:")){
String a=msg.substring(6,msg.length());
File file1 = new File(file.getParent(),a);
if(file1.exists()){
System.out.println("该路径已存在!");
}else{
file1.mkdir();
}
System.out.println("服务端:已创建新路径:"+a);
} else if(msg.equals("ls")){
String[] list = file.list();
StringBuffer stringBuffer = new StringBuffer();
System.out.println("该路径下的文件有:");
for(int i=0;i<list.length;i++){
String s=list[i];
stringBuffer.append(s+"\n");
}
System.out.println(stringBuffer.toString());
}else{
System.out.println("服务端:无效命令!");
}
outputStream.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.util.Scanner;
public class SocketDemo { //客户端
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 = new Socket();
socket.connect(new InetSocketAddress("127.0.0.1",5555));
outputStream = socket.getOutputStream();
inputStream = socket.getInputStream();
System.out.println("客户端连接成功!");
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(Exception e){
e.printStackTrace();
}finally {
try {
socket.close();
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
实现: