柚子快报邀请码778899分享:简述 BIO 、NIO 模型

http://yzkb.51969.com/

BIO : 同步阻塞I/O(Block IO)

         服务器实现模式为每一个连接一个线程,即客户端有连接请求时服务器就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,此处可以通过线程池机制进行优化。

import java.io.*;

import java.net.ServerSocket;

import java.net.Socket;

import java.util.ArrayList;

import java.util.List;

/**

* 多人聊天室 - 服务端

*/

public class BioServer {

static List clientList = new ArrayList<>();

public static void main(String[] args) throws Exception {

int port = 8080;

ServerSocket serverSocket = new ServerSocket(port);

while (true) {

Socket client = serverSocket.accept();

System.out.println("客户端: " + client.getPort() + " 连接成功!");

clientList.add(client);

forwardProcess(client);

}

}

/**

* 转发处理

*/

public static void forwardProcess(Socket socket) {

new Thread(new Runnable() {

@Override

public void run() {

while (true) {

forwardMsg(socket);

}

}

}).start();

}

/**

* 转发消息

*/

public static void forwardMsg(Socket socket) {

try {

String msg = readMsg(socket);

System.out.println(msg);

for (Socket client : clientList) {

if (client != socket) {

writeMsg(client, msg);

}

}

} catch (IOException e) {

throw new RuntimeException(e);

}

}

/**

* 写入消息

*/

public static void writeMsg(Socket socket, String msg) throws IOException {

PrintStream ps = new PrintStream(socket.getOutputStream());

ps.println(msg);

ps.flush();

}

/**

* 读取消息

*/

public static String readMsg(Socket socket) throws IOException {

InputStream inputStream = socket.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

String msg;

if ((msg = br.readLine()) != null) {

msg = socket.getPort() + " 说: " + msg;

}

return msg;

}

}

import java.io.*;

import java.net.Socket;

import java.util.Scanner;

public class BilClient {

public static void main(String[] args) throws IOException {

String ip = "127.0.0.1";

int port = 8080;

Socket client = new Socket(ip, port);

readProcess(client);

OutputStream os = client.getOutputStream();

PrintStream ps = new PrintStream(os);

Scanner scanner = new Scanner(System.in);

while (true) {

String input = scanner.nextLine();

ps.println(input);

ps.flush();

}

}

/**

* 读取处理

*/

public static void readProcess(Socket socket) {

new Thread(() -> {

while (true) {

try {

System.out.println(readMsg(socket));

} catch (IOException e) {

throw new RuntimeException(e);

}

}

}).start();

}

/**

* 读取消息

*/

public static String readMsg(Socket socket) throws IOException {

InputStream inputStream = socket.getInputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));

String msg;

if ((msg = br.readLine()) != null) {

}

return msg;

}

}

​NIO: 同步非阻塞式IO,服务器实现模式为一个线程处理多个请求(连接),即客户端发送的连接请求会被注册到多路复用器上,多路复用器轮询到有 I/O 请求就会进行处理。 Channel,翻译过来就是“通道”,就是数据传输的管道,类似于“流”,但是与“流”又有着区别。

既可以从Channel中读取数据,又可以写数据到Channel,但流的读写通常是单向的——输入输出流通道可以异步读写通道中的数据总是先读取到buffer(缓冲区),或者总是需要从一个buffer写入,不能直接访问数据非阻塞特性:Channel在设计上采用了非阻塞的特性,它不会像传统的流一样在读写操作上阻塞线程,而是立即返回结果,告诉调用者当前的状态。这使得程序可以在等待数据准备的过程中同时进行其他操作,实现了非阻塞IO。事件通知机制:Channel通常搭配选择器(Selector)来使用,选择器能够检测多个Channel的就绪状态,如是否可读、可写等,并通过事件通知(例如轮询或回调)及时地通知程序哪些Channel处于就绪状态,从而可以进行相应的读写操作。这种机制支持程序实现异步IO模型。操作系统底层支持:Channel的异步读写也依赖于操作系统底层的异步IO支持。Java NIO中的Channel实际上是对操作系统底层异步IO的封装和抽象,利用了操作系统提供的异步IO机制来实现其自身的异步读写功能。Buffer是一个对象,里面是要写入或者读出的数据,在java.nio库中,所有的数据都是用缓冲区处理的。

在读取数据时,它是直接读到缓冲区中的;在写入数据时,也是直接写到缓冲区中,任何时候访问Channel中的数据,都是通过缓冲区进行操作的。缓冲区实质上是一个数组,通常是一个字节数组ByteBuffer,当然也有其他类型的:Selector被称为选择器,Selector会不断地轮询注册在其上的Channel,如果某个Channel上发生读或写事件,这个Channel就被判定处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取到就绪Channel的集合,进行后续的I/O操作。

一个多路复用器Selector可以同时轮询多个Channel,JDK使用了epoll()代替了传统的select实现,所以并没有最大连接句柄的限制,这意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.*;

import java.nio.charset.Charset;

import java.util.Set;

/**

* NIO 聊天室 服务端

*/

public class NioServer {

private Integer port;

ByteBuffer readBuffer = ByteBuffer.allocate(1024);

ByteBuffer writerBuffer = ByteBuffer.allocate(1024);

private Charset charset = Charset.forName("UTF-8");

public NioServer(Integer port) {

this.port = port;

}

public static void main(String[] args) {

NioServer nioServer = new NioServer(8080);

nioServer.start();

}

private void start() {

try {

// 开启socket

ServerSocketChannel server = ServerSocketChannel.open();

// 设置非阻塞

server.configureBlocking(false);

// 绑定端口

server.socket().bind(new InetSocketAddress(port));

// 开启通道, 得到 Selector (选择器)

Selector selector = Selector.open();

// 注册 selector 监听事件

server.register(selector, SelectionKey.OP_ACCEPT);

System.out.println("启动服务器, 监听端口:" + port + "...");

while (true) {

// 阻塞监控所有注册的通道,当有对应的事件操作时, 会将SelectionKey放入 集合内部并返回事件数量

selector.select();

// 返回存有SelectionKey的集合

Set selectionKeys = selector.selectedKeys();

for (SelectionKey selectionKey : selectionKeys) {

handle(selectionKey, selector);

}

// 处理后清理 selectionKeys

selectionKeys.clear();

}

} catch (Exception e) {

e.printStackTrace();

}

}

/**

* 事件处理

*/

private void handle(SelectionKey key, Selector selector) throws IOException {

// SelectionKey 常用方法

//isAcceptable() 是否是连接继续事件

//isConnectable() 是否是连接就绪事件

//isReadable() 是否是读就绪事件

//isWritable() 是否是写就绪事件

// SelectionKey 常用事件

//SelectionKey.OP_ACCEPT 接收连接继续事件,表示服务器监听到了客户连接,服务器可以接收这个连接了

//SelectionKey.OP_CONNECT 连接就绪事件,表示客户端与服务器的连接已经建立成功

//SelectionKey.OP_READ 读就绪事件,表示通道中已经有了可读的数据,可以执行读操作了(通道目前有数据,可以进行读操作了)

//SelectionKey.OP_WRITE 写就绪事件,表示已经可以向通道写数据了(通道目前可以用于写操作)

//处理连接

if (key.isAcceptable()) {

ServerSocketChannel server = (ServerSocketChannel) key.channel();

SocketChannel client = server.accept();

client.configureBlocking(false);

client.register(selector, SelectionKey.OP_READ);

System.out.println(client.socket().getPort() + " 已建立连接 ...");

}

// 读取消息

else if (key.isReadable()) {

SocketChannel client = (SocketChannel) key.channel();

String msg = client.socket().getPort() + " 说: " + readMsg(client);

System.out.println(msg);

forwardMsg(msg, client, selector);

}

}

/**

* 读取通道消息

*/

private String readMsg(SocketChannel client) throws IOException {

readBuffer.clear();

while (client.read(readBuffer) > 0) ;

readBuffer.flip();

return String.valueOf(charset.decode(readBuffer));

}

/**

* 转发

*/

private void forwardMsg(String msg, SocketChannel client, Selector selector) throws IOException {

for (SelectionKey key : selector.keys()) {

Channel connectedClient = key.channel();

if (connectedClient instanceof ServerSocketChannel) {

continue;

}

if (key.isValid() && !client.equals(connectedClient)) {

writerBuffer.clear();

writerBuffer.put(charset.encode(msg));

writerBuffer.flip();

while (writerBuffer.hasRemaining())

((SocketChannel) connectedClient).write(writerBuffer);

}

}

}

}

import java.io.IOException;

import java.net.InetSocketAddress;

import java.nio.ByteBuffer;

import java.nio.channels.SelectionKey;

import java.nio.channels.Selector;

import java.nio.channels.SocketChannel;

import java.nio.charset.Charset;

import java.util.Scanner;

import java.util.Set;

/**

* NIO 聊天室 客户端

*/

public class NioClient {

private String ip;

private Integer port;

private ByteBuffer writerBuffer = ByteBuffer.allocate(1024);

private ByteBuffer readBuffer = ByteBuffer.allocate(1024);

private Charset charset = Charset.forName("UTF-8");

public NioClient(String ip, Integer port) {

this.ip = ip;

this.port = port;

}

public static void main(String[] args) {

NioClient nioClient = new NioClient("127.0.0.1", 8080);

nioClient.start();

}

public void start() {

try {

// 开启通道

SocketChannel client = SocketChannel.open();

// 设置非阻塞

client.configureBlocking(false);

Selector selector = Selector.open();

client.register(selector, SelectionKey.OP_CONNECT);

client.connect(new InetSocketAddress(ip, port));

while (true) {

selector.select();

Set selectionKeys = selector.selectedKeys();

for (SelectionKey selectionKey : selectionKeys) {

handle(selectionKey, selector);

}

selectionKeys.clear();

}

} catch (Exception e) {

e.printStackTrace();

}

}

private void handle(SelectionKey key, Selector selector) throws IOException {

// 处理连接事件

if (key.isConnectable()) {

SocketChannel client = (SocketChannel) key.channel();

if (client.isConnectionPending()) {

client.finishConnect();

// 处理用户输入

new Thread(() -> {

Scanner scanner = new Scanner(System.in);

while (true) {

String msg = scanner.nextLine();

writerBuffer.clear();

writerBuffer.put(charset.encode(msg));

writerBuffer.flip();

while (writerBuffer.hasRemaining()) {

try {

client.write(writerBuffer);

} catch (IOException e) {

throw new RuntimeException(e);

}

}

}

}).start();

}

client.register(selector, SelectionKey.OP_READ);

}

// 读取消息信息

else if (key.isReadable()) {

SocketChannel client = (SocketChannel) key.channel();

String s = readMsg(client);

System.out.println(s);

}

}

private String readMsg(SocketChannel client) throws IOException {

readBuffer.clear();

while (client.read(readBuffer) > 0) ;

readBuffer.flip();

return String.valueOf(charset.decode(readBuffer));

}

}

柚子快报邀请码778899分享:简述 BIO 、NIO 模型

http://yzkb.51969.com/

文章链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。