本文利用NIO实现一个重复回复,客户端发送什么信息,客户端就会收到什么信息。
主要是理解NIO与BIO的区别。客户端采用telnet进行测试,以下连接是Telnet安装的方法。
Telnet的简单使用_武汉小喽啰的博客-CSDN博客_telnet
注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端
注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端
注意!!!当使勇telnet连接成功到黑屏时候一定要用ctrl +] 进入客户端
可以使用?/help 进行命令查看命令很少的。
BIO即阻塞IO测试,发现以下两个地方会被阻塞,第一个客户端连接,第二个是客户端输入信息
- Socket socket = serverSocket.accept();
- InputStream is = socket.getInputStream();
客户端连接服务端流程:
【第一步】启动服务器--服务器运行到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<SelectionKey> 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<SelectionKey> 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();
}
}
}