实现一个基于JAVA NIO的客户端和服务端, 接收用户输入,服务端接收丢弃。
学好Selector,是多路复用的基础。
服务端
import lombok.extern.slf4j.Slf4j;
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.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
/**
* nio discard server
*
* @author jimo
* @version 1.0.0
* @date 2020/7/12 14:02
*/
@Slf4j
public class NioDiscardServer {
/**
* start server
*/
public static void startServer() throws IOException {
// 1.获取选择器
final Selector selector = Selector.open();
// 2.获取通道
final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 3.设置为非阻塞
serverSocketChannel.configureBlocking(false);
// 4.绑定连接
serverSocketChannel.bind(new InetSocketAddress("127.0.0.1", 8080));
log.info("服务器启动成功");
// 5.注册通道到选择器,注册“接收新连接”IO事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
// 6.轮询感兴趣的事件
while (selector.select() > 0) {
// 7.获取选择键合集
final Iterator<SelectionKey> selectKeys = selector.selectedKeys().iterator();
while (selectKeys.hasNext()) {
// 8.获取单个选择键
final SelectionKey selectionKey = selectKeys.next();
// 9.判断key是什么事件
if (selectionKey.isAcceptable()) {
// 10.如果key是“连接就绪”事件,则获取客户端连接
final SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
// 11.将该新连接的可读事件,注册到选择器上
socketChannel.register(selector, SelectionKey.OP_READ);
log.info("接收客户端连接:{}", socketChannel);
} else if (selectionKey.isReadable()) {
// 12.如果是可读事件,则读取数据
final SocketChannel channel = (SocketChannel) selectionKey.channel();
// 13.读取数据,然后丢弃
final ByteBuffer buffer = ByteBuffer.allocate(1024);
int len;
while ((len = channel.read(buffer)) > 0) {
buffer.flip();
log.info("接收到客户端数据:{}", new String(buffer.array(), 0, buffer.limit()));
buffer.clear();
}
channel.close();
}
// 14.移除选择键
selectKeys.remove();
}
}
// 15.关闭连接
serverSocketChannel.close();
}
/**
* main
*/
public static void main(String[] args) throws IOException {
startServer();
}
}
客户端
@Slf4j
public class NioDiscardClient {
/**
* start client
*/
public static void startClient() throws Exception {
final InetSocketAddress address = new InetSocketAddress("127.0.0.1", 8080);
// 1.获取通道
final SocketChannel socketChannel = SocketChannel.open(address);
// 2.切换为非阻塞状态
socketChannel.configureBlocking(false);
// 3.不断自旋,等待连接完成
while (!socketChannel.finishConnect()) {
log.info("正在努力连接...");
}
log.info("连接服务器成功");
// 4.分配缓冲区
final ByteBuffer buffer = ByteBuffer.allocate(1024);
// 5.接收用户输入
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()) {
final String input = scanner.next();
if ("bye".equalsIgnoreCase(input)) {
break;
}
buffer.put(input.getBytes());
buffer.flip();
// 发送到服务器
socketChannel.write(buffer);
buffer.clear();
}
log.info("客户端关闭");
// 发一个关闭信号
socketChannel.shutdownOutput();
socketChannel.close();
}
/**
* main
*/
public static void main(String[] args) throws Exception {
startClient();
}
}
测试
// 客户端(杀掉重启多次,服务端显示的端口不同)
com.jimo.netty.demo.nio.socket.NioDiscardClient 连接服务器成功
hee
bye
com.jimo.netty.demo.nio.socket.NioDiscardClient 客户端关闭
// 服务端
com.jimo.netty.demo.nio.socket.NioDiscardServer 服务器启动成功
com.jimo.netty.demo.nio.socket.NioDiscardServer 接收客户端连接:java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:53568]
com.jimo.netty.demo.nio.socket.NioDiscardServer 接收到客户端数据:hee
com.jimo.netty.demo.nio.socket.NioDiscardServer 接收客户端连接:java.nio.channels.SocketChannel[connected local=/127.0.0.1:8080 remote=/127.0.0.1:53879]
com.jimo.netty.demo.nio.socket.NioDiscardServer 接收到客户端数据:hehe