4种IO模型
BIO
public class BioSocketClient {
public static void main(String[] args) throws InterruptedException {
for (int times = 0; times < 10; times++) {
Thread thread = new Thread(() -> {
Socket socket;
try {
socket = new Socket("127.0.0.1", 9000);
OutputStream outputStream = socket.getOutputStream();
StringBuilder data = new StringBuilder();
for (int i = 1; i < 1000; i++) {
data.append(i).append(";");
}
outputStream.write(data.toString().getBytes());
outputStream.flush();
socket.shutdownOutput();
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
int read;
if ((read = inputStream.read(bytes)) != -1) {
System.out.println("从服务端获取数据:" + new String(bytes, 0, read));
}
socket.shutdownInput();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
thread.start();
}
}
}
public class BioSocketServer {
public static void main(String[] args) throws IOException, InterruptedException {
ServerSocket serverSocket = new ServerSocket(9000);
while (true) {
Socket socket = serverSocket.accept();
System.out.println("server接受了请求");
Thread thread = new Thread(() -> {
//阻塞,等待client连接
try {
handle(socket);
} catch (IOException e) {
throw new RuntimeException(e);
}
});
thread.start();
}
}
private static void handle(Socket socket) throws IOException {
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
int read;
while ((read = inputStream.read(bytes)) != -1) {
System.out.println("接收到客户端的数据:" + new String(bytes, 0, read));
}
socket.shutdownInput();
OutputStream outputStream = socket.getOutputStream();
outputStream.write("来自server的回复".getBytes());
outputStream.flush();
socket.shutdownOutput();
}
}
多个客户端发来消息,服务端开启多个线程来处理链接。
NIO
NIO三大组件:channel,buffer,selector。如图所示,端与端通过channel通信。buffer作为缓冲区,提供了高效读写的作用。
channel
BIO使用流来读写数。如下
Socket socket = new Socket("127.0.0.1",9000);
OutputStream outputStream = socket.getOutputStream();
outputStream.write("***".getBytes());
NIO通过channel来读写数据。channel就是下面那个东西,大家都成为管道。NIO中,只需要一条管道就可以读写,不需要建立输入/输出流.每条channel都连接了一个文件描述符
//省略socketChannel是怎么来的
ByteBuffer bufferWriter = ByteBuffer.wrap("Hello client".getBytes());
socketChannel.write(bufferWriter);
常用的channel有
- FileChannel,用于文件的IO操作
- DatagramChannel用于UDP的IO操作
- SocketChannel,用于TCPIO操作
- ServerSocketChannel,用于TCP连接的监听操作
Selector
大家叫他选择器。这家伙一出来,就知道,NIO是多路复用IO的实现. 对BIO而言,没有数据到来时,程序阻塞。有数据到来就读数据。 但NIO没有阻塞,他不知道什么时候被连接了,也知道数据什么时候到来。应用程序当然可以主动操作channel读数据,但不一定成功。所以需要告知应用程序,有事件来了。每个通道可以向selector订阅自己感兴趣的事件。java抽象出了如下四个事件
- SelectionKey.OP_CONNECT
- SelectionKey.OP_ACCEPT
- SelectionKey.OP_READ
- SelectionKey.OP_WRITE
//客户端订阅连接事件,selector才会告诉应用程序,已经连接上了服务端
socketchannel.register(selector, SelectionKey.OP_CONNECT);
//服务端订阅接收事件,有连接到来,应用程序可以获取到此事件
serverSocketChannel.register(selecor, SelectionKey.OP_ACCEPT);
//向selector订阅读事件,有消息来了,selector回通知应用程序去读数据
socketchannel.register(this.selector,SelectionKey.OP_READ);
//写事件,意味着buffer未满,可以写入。一般情况下,需要写数据时,才注册写事件,获取到写事件通知,再写数据。然后注销写事件,防止不断收到写事件。
buffer
线程不安全
8种类型:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer、MappedByteBuffer
三个属性
| 属性 | 说明 |
|---|---|
| capacity | 能够写入缓冲区的对象数量 |
| limit | 读写的末尾位置 |
| position | 读写的起始位置 |
| mark | 标记一个位置,让position回到mark位 |