简介
NIO,BIO的基础知识这里就不过多赘述,本站也有很多文章讲述,自行搜索,我们这里只简单对比总结一下NIO和BIO的特点。
BIO中的阻塞
- ServerSocket.accept()
- InputStream.read,OutPutStream.write
- 无法在同一个线程里处理多个Stream I/O
非阻塞式NIO
- 使用Channel代替Steam
- 使用Selecter监控多条Channel
- 可以在一个线程里处理多个Channel I/O
NIO中重要的概念Channel和Buffer
Channel的基本操作总结图
几个重要的Channel
- FileChannel
- ServerSocketChannel
- SocketChannel
NIO编程模型
基础知识就讲到这里,更多相关知识欢迎在本站【极客文库】搜素。
效果演示
本节我们同样实现多人在线聊天室,功能大体上和之前的章节相同
https://www.geekerstar.com/29447.html
https://www.geekerstar.com/29442.html
只不过本节换成NIO即同步非阻塞IO来实现
首先同样启动一个服务端,两个客户端,服务端将打印客户端连接的信息
然后用客户端分别发送消息
客户端输入”quit”退出
代码演示
服务端代码
import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Set; /** * @author geekerstar * @description 服务端 */ public class ChatServer { private static final int DEFAULT_PORT = 8888; private static final String QUIT = "quit"; private static final int BUFFER = 1024; private ServerSocketChannel server; private Selector selector; private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER); private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER); private Charset charset = StandardCharsets.UTF_8; private int port; public ChatServer() { this(DEFAULT_PORT); } public ChatServer(int port) { this.port = port; } private void start() { try { server = ServerSocketChannel.open(); server.configureBlocking(false); server.socket().bind(new InetSocketAddress(port)); selector = Selector.open(); server.register(selector, SelectionKey.OP_ACCEPT); System.out.println("服务器启动, 监听端口:" + port + "..."); while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey key : selectionKeys) { // 处理被触发的事件 handles(key); } selectionKeys.clear(); } } catch (IOException e) { e.printStackTrace(); } finally { close(selector); } } private void handles(SelectionKey key) throws IOException { // ACCEPT事件 - 和客户端建立了连接 if (key.isAcceptable()) { ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel client = server.accept(); client.configureBlocking(false); client.register(selector, SelectionKey.OP_READ); System.out.println(getClientName(client) + "已连接"); } // READ事件 - 客户端发送了消息 else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); String fwdMsg = receive(client); if (fwdMsg.isEmpty()) { // 客户端异常 key.cancel(); selector.wakeup(); } else { System.out.println(getClientName(client) + ":" + fwdMsg); forwardMessage(client, fwdMsg); // 检查用户是否退出 if (readyToQuit(fwdMsg)) { key.cancel(); selector.wakeup(); System.out.println(getClientName(client) + "已断开"); } } } } private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException { for (SelectionKey key : selector.keys()) { Channel connectedClient = key.channel(); if (connectedClient instanceof ServerSocketChannel) { continue; } if (key.isValid() && !client.equals(connectedClient)) { wBuffer.clear(); wBuffer.put(charset.encode(getClientName(client) + ":" + fwdMsg)); wBuffer.flip(); while (wBuffer.hasRemaining()) { ((SocketChannel) connectedClient).write(wBuffer); } } } } private String receive(SocketChannel client) throws IOException { rBuffer.clear(); while (client.read(rBuffer) > 0) ; rBuffer.flip(); return String.valueOf(charset.decode(rBuffer)); } private String getClientName(SocketChannel client) { return "客户端[" + client.socket().getPort() + "]"; } private boolean readyToQuit(String msg) { return QUIT.equals(msg); } private void close(Closeable closable) { if (closable != null) { try { closable.close(); } catch (IOException e) { e.printStackTrace(); } } } public static void main(String[] args) { ChatServer chatServer = new ChatServer(7777); chatServer.start(); } }
客户端代码
import java.io.Closeable; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedSelectorException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.util.Set; /** * @author geekerstar * @description */ public class ChatClient { private static final String DEFAULT_SERVER_HOST = "127.0.0.1"; private static final int DEFAULT_SERVER_PORT = 8888; private static final String QUIT = "quit"; private static final int BUFFER = 1024; private String host; private int port; private SocketChannel client; private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER); private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER); private Selector selector; private Charset charset = StandardCharsets.UTF_8; public ChatClient() { this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT); } public ChatClient(String host, int port) { this.host = host; this.port = port; } public boolean readyToQuit(String msg) { return QUIT.equals(msg); } private void close(Closeable closable) { if (closable != null) { try { closable.close(); } catch (IOException e) { e.printStackTrace(); } } } private void start() { try { client = SocketChannel.open(); client.configureBlocking(false); selector = Selector.open(); client.register(selector, SelectionKey.OP_CONNECT); client.connect(new InetSocketAddress(host, port)); while (true) { selector.select(); Set<SelectionKey> selectionKeys = selector.selectedKeys(); for (SelectionKey key : selectionKeys) { handles(key); } selectionKeys.clear(); } } catch (IOException e) { e.printStackTrace(); } catch (ClosedSelectorException e) { // 用户正常退出 } finally { close(selector); } } private void handles(SelectionKey key) throws IOException { // CONNECT事件 - 连接就绪事件 if (key.isConnectable()) { SocketChannel client = (SocketChannel) key.channel(); if (client.isConnectionPending()) { client.finishConnect(); // 处理用户的输入 new Thread(new UserInputHandler(this)).start(); } client.register(selector, SelectionKey.OP_READ); } // READ事件 - 服务器转发消息 else if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); String msg = receive(client); if (msg.isEmpty()) { // 服务器异常 close(selector); } else { System.out.println(msg); } } } public void send(String msg) throws IOException { if (msg.isEmpty()) { return; } wBuffer.clear(); wBuffer.put(charset.encode(msg)); wBuffer.flip(); while (wBuffer.hasRemaining()) { client.write(wBuffer); } // 检查用户是否准备退出 if (readyToQuit(msg)) { close(selector); } } private String receive(SocketChannel client) throws IOException { rBuffer.clear(); while (client.read(rBuffer) > 0) ; rBuffer.flip(); return String.valueOf(charset.decode(rBuffer)); } public static void main(String[] args) { ChatClient client = new ChatClient("127.0.0.1", 7777); client.start(); } }
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** * @author geekerstar * @description */ public class UserInputHandler implements Runnable { private ChatClient chatClient; public UserInputHandler(ChatClient chatClient) { this.chatClient = chatClient; } @Override public void run() { try { // 等待用户输入消息 BufferedReader consoleReader = new BufferedReader(new InputStreamReader(System.in)); while (true) { String input = consoleReader.readLine(); // 向服务器发送消息 chatClient.send(input); // 检查用户是否准备退出 if (chatClient.readyToQuit(input)) { break; } } } catch (IOException e) { e.printStackTrace(); } } }
本站所有文章均由网友分享,仅用于参考学习用,请勿直接转载,如有侵权,请联系网站客服删除相关文章。若由于商用引起版权纠纷,一切责任均由使用者承担
极客文库 » 基于NIO实现的简易多人在线聊天室客户端和服务端
极客文库 » 基于NIO实现的简易多人在线聊天室客户端和服务端
常见问题FAQ
- 如果资源链接失效了怎么办?
- 本站用户分享的所有资源都有自动备份机制,如果资源链接失效,请联系本站客服QQ:2580505920更新资源地址。
- 如果用户分享的资源与描述不符怎么办?
- 如何分享个人资源获取赞助积分或其他奖励?
- 如果您发现了本资源有侵权行为怎么办?