本文利用NIO实现一个重复回复,客户端发送什么信息,客户端就会收到什么信息。
主要是理解NIO与BIO的区别。客户端采用telnet进行测试,以下连接是Telnet安装的方法。
Telnet的简单使用_武汉小喽啰的博客-CSDN博客_telnet
注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端
注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端
注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端
可以使用?/help 进行命令查看命令很少的。
BIO即阻塞IO测试,发现以下两个地方会被阻塞,第一个客户端连接,第二个是客户端输入信息
客户端连接服务端流程:
【第一步】启动服务器--服务器运行到accept命令阻塞
【第二步】连接客户端,服务器接收客户端,阻塞到 getInputStream() 等待客户端发送信息。
可以看出来,一个线程为客户端服务,直到客户端断开连接
【代码】
- package cn.msf.bio;
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
-
- /**
- * @author: msf
- * @date: 2022/11/22
- */
- public class BIO {
- public static void main(String[] args) {
- try {
- ServerSocket serverSocket = new ServerSocket(10101);
- System.out.println("服务端启动");
- while (true) {
- // 获取一个套接字(阻塞)
- Socket socket = serverSocket.accept();
- System.out.println("来了一个客户端");
- hander(socket);
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- private static void hander(Socket socket) {
- byte[] bytes = new byte[1024];
- int len = 0;
- try {
- // 阻塞等待。
- InputStream is = socket.getInputStream();
- // 将信息读到bytes中。
- while ((len = is.read(bytes)) != -1) {
- System.out.println(new String(bytes, 0, len));
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- System.out.println("关闭socket");
- try {
- socket.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
-
- }
-
- }
NIO实现测试
【第一步】建立服务器端,这里使用的命令是
- ServerSocketChannel serverChannel = ServerSocketChannel.open();
- // 设置为不阻塞
- serverChannel.configureBlocking(false);
- // 设置socket的连接接口
- serverChannel.socket().bind(new InetSocketAddress("localhost",port));
【第二步】开启selector相当找了一个服务员,为客户端服务
- // 启动一个选择器
- selector = Selector.open();
- serverChannel.register(selector, SelectionKey.OP_ACCEPT);
【第三步】监听客户端连接
- public void listen() throws IOException {
- System.out.println("服务端启动成功");
- while (true) {
- // 当服务端注册的时间到达后,返回;否则会一直阻塞
- selector.select();
- selector.wakeup();
- Iterator
ite = selector.selectedKeys().iterator(); - while (ite.hasNext()) {
- SelectionKey key = ite.next();
- ite.remove();
- // 处理事件的类型
- handler(key);
- }
- }
- }
【第四步】为不同客户端处理不同的事件。
可以达到一对多服务,不会想BIO一样阻塞在getinputstream哪里只等待一个客户端
- private void handler(SelectionKey key) throws IOException {
- if (key.isAcceptable()) {
- // 说明是注册事件
- handlerAccept(key);
- } else if (key.isReadable()) {
- handlerRead(key);
- }
- }
【完整代码】
- package cn.msf.nio;
-
- import java.io.IOException;
- import java.net.InetSocketAddress;
- import java.nio.ByteBuffer;
- import java.nio.channels.*;
- import java.nio.charset.StandardCharsets;
- import java.util.Date;
- import java.util.Iterator;
-
- /**
- * @author: msf
- * @date: 2022/11/23
- */
- public class NIOServer {
- private Selector selector;
-
- public static void main(String[] args) throws IOException {
- NIOServer server = new NIOServer();
- server.initServer(8000);
- server.listen();
- }
-
-
- public void initServer(int port) throws IOException {
- // 获得一个ServerSocket 通道
- ServerSocketChannel serverChannel = ServerSocketChannel.open();
- // 设置为不阻塞
- serverChannel.configureBlocking(false);
- // 设置socket的连接接口
- serverChannel.socket().bind(new InetSocketAddress("localhost",port));
-
- // 启动一个选择器
- selector = Selector.open();
- serverChannel.register(selector, SelectionKey.OP_ACCEPT);
- }
-
- public void listen() throws IOException {
- System.out.println("服务端启动成功");
- while (true) {
- // 当服务端注册的时间到达后,返回;否则会一直阻塞
- selector.select();
- selector.wakeup();
- Iterator
ite = selector.selectedKeys().iterator(); - while (ite.hasNext()) {
- SelectionKey key = ite.next();
- ite.remove();
- // 处理事件的类型
- handler(key);
- }
- }
- }
-
- private void handler(SelectionKey key) throws IOException {
- if (key.isAcceptable()) {
- // 说明是注册事件
- handlerAccept(key);
- } else if (key.isReadable()) {
- handlerRead(key);
- }
- }
-
- public void handlerAccept(SelectionKey key) throws IOException {
- // 当有事件来了,获得之前的服务端
- ServerSocketChannel server = (ServerSocketChannel) key.channel();
- // 获得客户端的通道--相当于客户端
- SocketChannel channel = server.accept();
- channel.configureBlocking(false);
- System.out.println("客户端连接");
- channel.register(this.selector,SelectionKey.OP_READ);
- }
-
- private void handlerRead(SelectionKey key) throws IOException {
- // 服务器可读消息,得到事件发生的Socket通道。
- SocketChannel channel = (SocketChannel) key.channel();
- // 创建缓冲区
- ByteBuffer buffer = ByteBuffer.allocate(1024);
- int read = channel.read(buffer);
- if (read > 0) {
- byte[] data = buffer.array();
- String msg = new String(data).trim();
- System.out.println("服务器收到消息: " + msg);
- ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes(StandardCharsets.UTF_8));
- // 将消息发送给客户端
- channel.write(outBuffer);
- } else {
- channel.close();
- }
-
- }
- }