本文利用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();}}
}