【视频笔记】【速通Netty】
【速通Netty】(上)
一、BIO模型服务端一次只能连接一个客户端线程(其他客户端连接只能等待前面连接的断开后才能连上)
BioServer
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BioServer {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
Socket accept = serverSocket.accept();
InputStream inputStream = accept.getInputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) != -1) {// 只要读取不返回-1,就认为连接一直存在,线程阻塞在这里不能退出,BIO模式
String message = new String(buffer, 0, length);
System.out.println(message);
}
System.out.println("客户端断开了连接");
}
}
}
BioClient
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
public class BioClient {
public static void main(String[] args) throws Exception {
Thread thread1 = new Thread(() -> {
try {
sendHello();
} catch (Exception e) {
throw new RuntimeException(e);
}
}, "thread1");
Thread thread2 = new Thread(() -> {
try {
sendHello();
} catch (Exception e) {
throw new RuntimeException(e);
}
}, "thread2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
}
private static void sendHello() throws Exception {
Socket socket = new Socket();
socket.connect(new InetSocketAddress("localhost", 8080));
OutputStream outputStream = socket.getOutputStream();
for (int i = 0; i < 10; i++) {
outputStream.write(("hello_" + Thread.currentThread().getName() + "_" + i).getBytes());
outputStream.flush();
}
socket.close();
}
}
Server端输出
hello_thread2_0
hello_thread2_1
hello_thread2_2
hello_thread2_3
hello_thread2_4
hello_thread2_5
hello_thread2_6
hello_thread2_7
hello_thread2_8
hello_thread2_9
客户端断开了连接
hello_thread1_0hello_thread1_1hello_thread1_2hello_thread1_3hello_thread1_4hello_thread1_5hello_thread1_6hello_thread1_7hello_thread1_8hello_thread1_9
客户端断开了连接
如果没有socket.close(); 则会抛出异常
Exception in thread "main" java.net.SocketException: Connection reset at java.base/sun.nio.ch.NioSocketImpl.implRead(NioSocketImpl.java:328) at java.base/sun.nio.ch.NioSocketImpl.read(NioSocketImpl.java:355) at java.base/sun.nio.ch.NioSocketImpl$1.read(NioSocketImpl.java:808) at java.base/java.net.Socket$SocketInputStream.read(Socket.java:966) at java.base/java.io.InputStream.read(InputStream.java:218) at com.rocky.netty.BioServer.main(BioServer.java:15)
二、NIO
ServerSocketChannel(大马路)下游很多SocketChannel(小马路),每个SocketChannel中有个buffer(通道上面传递的数据)
1. channel.read不能阻塞
2. 的有人知道什么时候能读写
监听器--Selector (不是server端轮流去询问哪个channel有数据,而是回调,通过监听器,有数据的channel回调通知)
(NIO三大件 channel 、buffer、 selector)
NioServer
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;
public class NioServer {
public static void main(String[] args) throws IOException {
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
ssc.bind(new InetSocketAddress("localhost", 8080));
Selector selector = Selector.open();
ssc.register(selector, SelectionKey.OP_ACCEPT);
while (true){
selector.select();// 是一个阻塞函数,select函数被触发后,说明有一个socket的channel进行连接
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
SelectionKey selectionKey = iterator.next();
// 每一次读取完了之后,不知道监听的这个key是不是已经处理完了,所以需要移除掉
iterator.remove();
if (selectionKey.isAcceptable()){//是建联的事件
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
SocketChannel client = serverSocketChannel.accept();
client.configureBlocking(false);
client.register(selector, SelectionKey.OP_READ);
System.out.println(client.getRemoteAddress()+"连接了");
}
if (selectionKey.isReadable()){
SocketChannel channel = (SocketChannel) selectionKey.channel();
ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
int length = channel.read(byteBuffer);
if (length == -1){
System.out.println("客户端断开了连接"+channel.getRemoteAddress());
channel.close();
}else{
byteBuffer.flip();
byte[] buffer = new byte[byteBuffer.remaining()];
byteBuffer.get(buffer);
String message = new String(buffer);
System.out.println(message);
}
}
}
}
}
}
使用上面的BioClient
import java.io.OutputStream; import java.net.InetSocketAddress; import java.net.Socket; public class BioClient { public static void main(String[] args) throws Exception { Thread thread1 = new Thread(() -> { try { sendHello(); } catch (Exception e) { throw new RuntimeException(e); } }, "thread1"); Thread thread2 = new Thread(() -> { try { sendHello(); } catch (Exception e) { throw new RuntimeException(e); } }, "thread2"); thread1.start(); thread2.start(); thread1.join(); thread2.join(); } private static void sendHello() throws Exception { Socket socket = new Socket(); socket.connect(new InetSocketAddress("localhost", 8080)); OutputStream outputStream = socket.getOutputStream(); for (int i = 0; i < 10; i++) { outputStream.write(("hello_" + Thread.currentThread().getName() + "_" + i).getBytes()); outputStream.flush(); } socket.close(); Thread.sleep(3000L); } }
打印如下, 可以看到可以同时处理2个连接
/127.0.0.1:7903连接了
/127.0.0.1:7904连接了
hello_thread2_0hello_thread2_1
hello_thread2_2hello_thread2_3
hello_thread1_0hello_thread1_1hello_thread1_2hello_thread1_3hello_thread1_4
hello_thread2_4
hello_thread2_5
hello_thread1_5hello_thread1_6
hello_thread2_6
hello_thread1_7
hello_thread2_7
hello_thread2_8
hello_thread1_8
hello_thread2_9
hello_thread1_9
客户端断开了连接/127.0.0.1:7903
客户端断开了连接/127.0.0.1:7904
上述ByteBuffer.allocate(1024);改为ByteBuffer.allocate(4);一次读取四字节,则打印出来的只要截断的4个字符(半包现象)
hell
o_th
改为ByteBuffer.allocate(16);一次读取十六字节,则打印出来的16个字符(可能是部分消息, 也可能是完整的一条消息+下一条不完整的消息)(粘包现象)
面向流的时候,如何把流的数据,切分成不同的有意义的消息


速通Netty】(下)

ServerSocketChannel只有一个(大马路),只注册accept事件,只有一个线程
SocketChannel有多个(小马路),注册read/write事件,有多个线程(线程组)




netty中把这些组件组合起来的东西叫做bootstrap
NettyServer
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
public class NettyServer {
public static void main(String[] args) {
// serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上
// pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组;
// 因为是循环读取事件,所以是event loop
serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup());
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 每一个socketChannel里一个pipeline
socketChannel.pipeline().addLast(new StringDecoder()).addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
System.out.println(msg);
}
});
}
});
ChannelFuture bindFuture = serverBootstrap.bind(8080);
bindFuture.addListener(future -> {
if(future.isSuccess()){
System.out.println("Server started on port 8080");
}else{
System.out.println("Server failed to start on port 8080");
}
});
}
}
注: 可以改写为链式调用
NettyClient
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; public class NettyClient { public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(new NioEventLoopGroup()); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringEncoder()); } }); ChannelFuture channelFuture = bootstrap.connect("localhost", 8080); channelFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("connect success"); channelFuture.channel().writeAndFlush("hi, i'm socket client"); } }); } }
服务端NettyServer打印
Server started on port 8080
hi, i'm socket client
客户端NettyClient打印
connect success
# 以换行符\n为一个分割,每一个分割就是一条消息
server
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class NettyServer { public static void main(String[] args) { // serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上 // pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组; // 因为是循环读取事件,所以是event loop serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 每一个socketChannel里一个pipeline socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringDecoder()).addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception { System.out.println(msg); } }); } }); ChannelFuture bindFuture = serverBootstrap.bind(8080); bindFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("Server started on port 8080"); } else { System.out.println("Server failed to start on port 8080"); } }); } }
client
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; public class NettyClient { public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(new NioEventLoopGroup()); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringEncoder()); } }); ChannelFuture channelFuture = bootstrap.connect("localhost", 8080); channelFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("connect success"); channelFuture.channel().writeAndFlush("hi, i'm socket client\n"); } }); } }
# 每秒发送一次消息到服务端
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class NettyServer { public static void main(String[] args) { // serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上 // pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组; // 因为是循环读取事件,所以是event loop serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 每一个socketChannel里一个pipeline socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringDecoder()).addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception { System.out.println(msg); } }); } }); ChannelFuture bindFuture = serverBootstrap.bind(8080); bindFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("Server started on port 8080"); } else { System.out.println("Server failed to start on port 8080"); } }); } }
client
import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoop; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.string.StringEncoder; import java.util.concurrent.TimeUnit; public class NettyClient { public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(new NioEventLoopGroup()); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new StringEncoder()); } }); ChannelFuture channelFuture = bootstrap.connect("localhost", 8080); channelFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("connect success"); EventLoop eventLoop = channelFuture.channel().eventLoop(); eventLoop.scheduleAtFixedRate(() -> { channelFuture.channel().writeAndFlush("hi, i'm socket client\n"); }, 0, 2, TimeUnit.SECONDS); } }); } }
# server写数据返回给客户端
需要在上面代码基础上,server端添加出站处理器,才能往channel里写数据;client需要添加入站处理器,才能从channel读取内容解码处理成消息
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; public class NettyServer { public static void main(String[] args) { // serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上 // pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组; // 因为是循环读取事件,所以是event loop serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 每一个socketChannel里一个pipeline socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringDecoder()) .addLast(new StringEncoder()).addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); // 没有上面的出站处理器,addLast(new StringEncoder()),是不能往channel里写消息的 ctx.channel().writeAndFlush("hi client,i'm server\n"); } }); } }); ChannelFuture bindFuture = serverBootstrap.bind(8080); bindFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("Server started on port 8080"); } else { System.out.println("Server failed to start on port 8080"); } }); } }
client
import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.concurrent.TimeUnit; public class NettyClient { public static void main(String[] args) { Bootstrap bootstrap = new Bootstrap(); bootstrap.group(new NioEventLoopGroup()); bootstrap.channel(NioSocketChannel.class); bootstrap.handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringEncoder()).addLast(new StringDecoder()).addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); } }); } }); ChannelFuture channelFuture = bootstrap.connect("localhost", 8080); channelFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("connect success"); EventLoop eventLoop = channelFuture.channel().eventLoop(); eventLoop.scheduleAtFixedRate(() -> { channelFuture.channel().writeAndFlush("hi server, i'm client\n"); }, 0, 2, TimeUnit.SECONDS); } }); } }
ctx.writeAndFlush("")

与ctx.channel().writeAndFlush("")的区别

# 模拟数据存到数据库
pipeline是一个双向链表,它是一个流式的,我们在定义一个入站处理器之后,如果不额外操作的话,pipeline是不会把这个消息向下流转的,所以需要手动写一下
ctx.fireChannelRead(msg)
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class NettyServer {
public static void main(String[] args) {
Map<Channel, List<String>> db = new ConcurrentHashMap<>();
// serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上
// pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容
ServerBootstrap serverBootstrap = new ServerBootstrap();
// 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组;
// 因为是循环读取事件,所以是event loop
serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup());
serverBootstrap.channel(NioServerSocketChannel.class);
serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
// 每一个socketChannel里一个pipeline
socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringDecoder())
.addLast(new StringEncoder()).addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
System.out.println(msg);
// 没有上面的出站处理器,addLast(new StringEncoder()),是不能往channel里写消息的
ctx.channel().writeAndFlush("hi client, i'm server\n");
// pipeline是一个双向链表,它是一个流式的,我们在定义一个入站处理器之后,如果不额外操作的话,pipeline是不会把这个消息向下流转的,所以需要手动写一下
ctx.fireChannelRead(msg);
}
}).addLast(new SimpleChannelInboundHandler<String>() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
List<String> list = db.computeIfAbsent(ctx.channel(), k -> new ArrayList<>());
list.add(msg);
}
@Override
public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelRegistered");
}
@Override
public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelUnregistered");
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelActive");
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
System.out.println("channelInactive");
System.out.println("db content:"+db.get(ctx.channel()));
}
});
}
});
ChannelFuture bindFuture = serverBootstrap.bind(8080);
bindFuture.addListener(future -> {
if (future.isSuccess()) {
System.out.println("Server started on port 8080");
} else {
System.out.println("Server failed to start on port 8080");
}
});
}
}
要实现2个handle里都能注册,需要把注册这个事件传播下去,ctx.fireChannelRegistered
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class NettyServer { public static void main(String[] args) { Map<Channel, List<String>> db = new ConcurrentHashMap<>(); // serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上 // pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组; // 因为是循环读取事件,所以是event loop serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 每一个socketChannel里一个pipeline socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringDecoder()).addLast(new StringEncoder()).addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); // 没有上面的出站处理器,addLast(new StringEncoder()),是不能往channel里写消息的 ctx.channel().writeAndFlush("hi client, i'm server\n"); // pipeline是一个双向链表,它是一个流式的,我们在定义一个入站处理器之后,如果不额外操作的话,pipeline是不会把这个消息向下流转的,所以需要手动写一下 ctx.fireChannelRead(msg); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered-1"); ctx.fireChannelRegistered(); } }).addLast(new SimpleChannelInboundHandler<String>() { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { List<String> list = db.computeIfAbsent(ctx.channel(), k -> new ArrayList<>()); list.add(msg); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered"); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelUnregistered"); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelInactive"); System.out.println("db content:" + db.get(ctx.channel())); } }); } }); ChannelFuture bindFuture = serverBootstrap.bind(8080); bindFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("Server started on port 8080"); } else { System.out.println("Server failed to start on port 8080"); } }); } }
# 消息传播的过程中发生了异常,如何处理?exceptionCaught,至少有一个异常处理的handler,否则netty会把异常吞掉
@Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); ctx.fireExceptionCaught(cause); }
pipeline是双向链表,handler分入站处理器和出站处理器, 双向链表的一个流动的过程
重构抽取一下handle
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; public class NettyServer { public static void main(String[] args) { Map<Channel, List<String>> db = new ConcurrentHashMap<>(); // serverBootstrap 1、管理2个eventLoopGroup, 2、管理serverSocketChannel 3、配置了一个pipeline 4、绑定到一个端口上 // pipeline里加了2个handler,一个解码器handler,一个入站handler读取pipeline的内容 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 一个线程组处理连接请求事件,accept,即boss线程组;另一个线程组处理read/write事件,即worker线程组; // 因为是循环读取事件,所以是event loop serverBootstrap.group(new NioEventLoopGroup(), new NioEventLoopGroup()); serverBootstrap.channel(NioServerSocketChannel.class); serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { // 每一个socketChannel里一个pipeline socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024)).addLast(new StringDecoder()).addLast(new StringEncoder()).addLast(new ResponseHandler()).addLast(new DbHandler(db)); } }); ChannelFuture bindFuture = serverBootstrap.bind(8080); bindFuture.addListener(future -> { if (future.isSuccess()) { System.out.println("Server started on port 8080"); } else { System.out.println("Server failed to start on port 8080"); } }); } static class ResponseHandler extends SimpleChannelInboundHandler<String> { @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { System.out.println(msg); // 没有上面的出站处理器,addLast(new StringEncoder()),是不能往channel里写消息的 ctx.channel().writeAndFlush("hi client, i'm server\n"); // pipeline是一个双向链表,它是一个流式的,我们在定义一个入站处理器之后,如果不额外操作的话,pipeline是不会把这个消息向下流转的,所以需要手动写一下 ctx.fireChannelRead(msg); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered-1"); ctx.fireChannelRegistered(); } } static class DbHandler extends SimpleChannelInboundHandler<String> { private final Map<Channel, List<String>> db; public DbHandler(Map<Channel, List<String>> db) { this.db = db; } @Override protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception { List<String> list = db.computeIfAbsent(ctx.channel(), k -> new ArrayList<>()); list.add(msg); } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelRegistered"); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { System.out.println("channelUnregistered"); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelActive"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("channelInactive"); System.out.println("db content:" + db.get(ctx.channel())); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { super.exceptionCaught(ctx, cause); ctx.fireExceptionCaught(cause); } } }
maven引用包
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.119.Final</version>
</dependency>
参考:【速通Netty】(上) NIO流程科普_哔哩哔哩_bilibili
【速通Netty】(下)Netty组件和编码_哔哩哔哩_bilibili
浙公网安备 33010602011771号