BIO介绍
BIO(blocking IO)同步阻塞
在JD1.4之前,建立网络连接时采用BIO模型
BIO的流程比较简单,在服务端创了一个ServerSocket去监听等待连接,客户端去创建一个Socket去连接服务端,建立连接之后,客户端和服务端就可以进行网络数据传输
同步阻塞的理解:
在BIO编程中,方法acceot()等待客户端的连接,而客户端何时发起连接是未知,accept等待操作一旦发起之后,需要一直阻塞等待连接,直到有客户端的连接之后才能accept返回,整个过程是阻塞的。除了accept之外,还有connect、read、write等方法都会阻塞。只有等到数据完成之后才能继续执行
BIO编码
通过BIO模型模拟echo命令,客户端发送一个命令,服务端响应当前的数据,并能够多次接收数据
public class Server {
public static void main(String[] args) {
serverHandler(9999);
}
public static void serverHandler(int port) {
ServerSocket serverSocket = null;
try {
//1、创建ServerSocket实例
serverSocket = new ServerSocket();
//2、绑定端口
serverSocket.bind(new InetSocketAddress(port));
System.out.println("服务端绑定端口:"+port+" 并启动啦");
//3、监听客户端的连接.会阻塞直到有客户端连接上
Socket socket = serverSocket.accept();
System.out.println("有新客户端连接:"+socket.getRemoteSocketAddress());
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();//读数据
OutputStream outputStream = socket.getOutputStream();//写数据
//多次接收用户端发送的消息,并返回数据
while (true) {
//读数据
int len = inputStream.read(bytes);
String msg = new String(bytes,0,len);
System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 发送数据:"+msg);
//给客户端回写数据
String msg1 = "【echo】"+msg;
outputStream.write(msg1.getBytes());
outputStream.flush();
//特殊标志位表示结束 exit
if (msg != null && "exit".equals(msg.trim())) {
//特殊结束符
System.out.println("服务端准备结束");
break;
}
}
//关闭socket
socket.close();
System.out.println("客户端连接已断开");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (serverSocket != null) {
try {
serverSocket.close();
System.out.println("服务端已关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class Server {
public static void main(String[] args) {
serverHandler(9999);
}
public static void serverHandler(int port) {
ServerSocket serverSocket = null;
try {
//1、创建ServerSocket实例
serverSocket = new ServerSocket();
//2、绑定端口
serverSocket.bind(new InetSocketAddress(port));
System.out.println("服务端绑定端口:"+port+" 并启动啦");
//3、监听客户端的连接.会阻塞直到有客户端连接上
Socket socket = serverSocket.accept();
System.out.println("有新客户端连接:"+socket.getRemoteSocketAddress());
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();//读数据
OutputStream outputStream = socket.getOutputStream();//写数据
//多次接收用户端发送的消息,并返回数据
while (true) {
//读数据
int len = inputStream.read(bytes);
String msg = new String(bytes,0,len);
System.out.println("客户端:"+socket.getRemoteSocketAddress()+" 发送数据:"+msg);
//给客户端回写数据
String msg1 = "【echo】"+msg;
outputStream.write(msg1.getBytes());
outputStream.flush();
//特殊标志位表示结束 exit
if (msg != null && "exit".equals(msg.trim())) {
//特殊结束符
System.out.println("服务端准备结束");
break;
}
}
//关闭socket
socket.close();
System.out.println("客户端连接已断开");
} catch (IOException e) {
e.printStackTrace();
}finally {
if (serverSocket != null) {
try {
serverSocket.close();
System.out.println("服务端已关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
服务端如何处理多个用户的请求?
在accept接收多用户连接时,通过循环来接,借助于多线程处理
主线程主要接收客户端的连接(accept),子线程需要进行IO读写
多用户的请求:
多线程+BIO实现echo命令
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
public class MutilThreadServer {
public static void main(String[] args) {
serverHandler(9999);
}
public static void serverHandler(int port) {
ServerSocket serverSocket = null;
try {
//1、创建ServerSocket实例
serverSocket = new ServerSocket();
//2、绑定端口
serverSocket.bind(new InetSocketAddress(port));
System.out.println("服务端绑定端口:"+port+" 并启动啦");
//3、监听客户端的连接.会阻塞直到有客户端连接上
while (true) {
Socket socket = serverSocket.accept();
System.out.println("有新客户端连接:"+socket.getRemoteSocketAddress());
//将新用户连接交给子线程处理并启动子线程
new ServerHandler(socket).start();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if (serverSocket != null) {
try {
serverSocket.close();
System.out.println("服务端已关闭");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
/**
* 子线程完成读写操作
*/
public class ServerHandler extends Thread {
Socket socket;
//构造函数,将新用户的连接socket交给子线程
public ServerHandler(Socket socket){
this.socket = socket;
}
@Override
//子线程完成读写操作
public void run() {
try {
//
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();//读数据
OutputStream outputStream = socket.getOutputStream();//写数据
//多次接收用户端发送的消息,并返回数据
while (true) {
//读数据
int len = inputStream.read(bytes);
String msg = new String(bytes,0,len);
System.out.println("线程:"+Thread.currentThread().getName()+" 客户端:"+socket.getRemoteSocketAddress()+" 发送数据:"+msg);
//给客户端回写数据
String msg1 = "【echo】"+msg;
outputStream.write(msg1.getBytes());
outputStream.flush();
//特殊标志位表示结束 exit
if (msg != null && "exit".equals(msg.trim())) {
//特殊结束符
System.out.println("线程:"+Thread.currentThread().getName()+"服务端准备结束");
break;
}
}
//关闭socket
socket.close();
System.out.println("线程:"+Thread.currentThread().getName()+"客户端连接已断开");
} catch (Exception e){
}
}
}
BIO支持高并发的缺点:
线程资源是有限的,不能无限制的创建新的资源,那么对于BIO的高并发是大打折扣的
1.线程需要消耗的内存资源有限,不能无限制创建线程
2.线程如果涉及上下文的调整,这个过程也是耗时的,如果线程执行过程中,线程上下文切换耗时t1,线程执行耗时t2,如果t1>t2,线程的执行效率会降低