【视频笔记】【速通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

 

posted @ 2025-05-07 22:55  fanblog  阅读(26)  评论(0)    收藏  举报