Netty学习笔记

NIO基础

NIO:non-blocing io 非阻塞io

NIO的三大组件:channel、buffer、selector

ByteBuffer的3个重要属性:position(读写指针)、limit(限制指针)、capacity(容量)

读写过程图:

buffer的写入方式

buffer的读取方式

buffer和字符串的转换

//1.字符串转ByteBuffer,使用后还是写状态
ByteBuffer buffer = ByteBuffer.allocate(16);
buffer.put("FED126".getBytes());

//2.Charset,使用后会变成读状态
ByteBuffer buffer2 = StandardCharsets.UTF_8.encode("qwe");

//3.wrap,使用后会变成读状态
ByteBuffer buffer3 = ByteBuffer.wrap("zxc".getBytes());

//buffer转字符串
String s = StandardCharsets.UTF_8.decode(buffer2).toString();

文件编程

FileChannel

/**
* 通道数据传输
*/
public static void testTranform(){
    try (
        FileChannel from = new FileInputStream("fed.txt").getChannel();
        FileChannel to = new FileOutputStream("to.txt").getChannel();
    ) {
        long size = from.size();
        for (long left = size; left > 0;){
            left -= from.transferTo((size - left), left, to);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Path

Files

遍历文件夹和文件

/**
* 遍历文件夹和文件
*/
public static void testWalkFileTree() throws IOException {
    AtomicInteger dirCount = new AtomicInteger();
    AtomicInteger fileCount = new AtomicInteger();
    Files.walkFileTree(Paths.get("D:\\maven-repository"), new SimpleFileVisitor<Path>(){
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            System.out.println(dir);
            dirCount.incrementAndGet();
            return super.preVisitDirectory(dir, attrs);
        }

        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            System.out.println(file);
            fileCount.incrementAndGet();
            return super.visitFile(file, attrs);
        }
    });
    System.out.println(dirCount + ":"+ fileCount);
}

事件类型:accept-有连接时触发、connect-客户端连接后触发、read-可读事件、write-可写事件

阻塞和非阻塞服务IO的使用:

public class Service {
    public static void main(String[] args) throws IOException {
        //0.创建ByteBuffer存储数据
        ByteBuffer buffer = ByteBuffer.allocate(16);
        //1.创建服务器
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); //非阻塞
        //2.绑定监听端口
        ssc.bind(new InetSocketAddress(8086));
        //3.连接集合
        List<SocketChannel> channels = new ArrayList<>();
        while (true){
            System.out.println("服务启动....");
            //4.建立和客户端的连接
            SocketChannel sc = ssc.accept();
            sc.configureBlocking(false);
            System.out.println("客户端连接....");
            channels.add(sc);
            for (SocketChannel channel : channels){
                //5.接受客户端发送的数据
                channel.read(buffer); //阻塞方法,线程停止运行
                buffer.flip();
                while(buffer.hasRemaining()){ //是否还有剩余未读的数据
                    System.out.println((char)buffer.get());
                }
                buffer.clear();
            }
        }
    }
}

selector的使用:

public class TestSelector {
    public static void main(String[] args) throws IOException {
        //创建selector
        Selector selector = Selector.open();

        //1.创建服务器
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false); //非阻塞

        //建立selector和channel的联系,通过事件驱动的方式
        SelectionKey sscKey = ssc.register(selector, 0, null);
        //设置关注的事件
        sscKey.interestOps(SelectionKey.OP_ACCEPT);

        //2.绑定监听端口
        ssc.bind(new InetSocketAddress(8086));
        while (true){
            System.out.println("线程启动...");
            //没有事件,线程阻塞
            selector.select();
            //处理事件,要么取消,要么处理
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()){
                //只是复制出来的key
                SelectionKey key = iterator.next();
                //区分事件类型
                if(key.isAcceptable()){
                    ServerSocketChannel channel = (ServerSocketChannel)key.channel();
                    SocketChannel sc = channel.accept(); //没有事件处理会返回null
                    sc.configureBlocking(false);
                    SelectionKey scKey = sc.register(selector, 0, null);
                    scKey.interestOps(SelectionKey.OP_READ);
                }else if(key.isReadable()){
                    try {
                        SocketChannel channel = (SocketChannel)key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(16);
                        int read = channel.read(buffer);//阻塞方法,线程停止运行
                        if(read == -1){ //正常断开
                            System.out.println("线程正常断开...");
                            key.cancel();
                            continue;
                        }
                        buffer.flip();
                        System.out.println(Charset.defaultCharset().decode(buffer).toString());
                        buffer.clear();
                    }catch (Exception e){
                        System.out.println("线程异常断开...");
                        key.cancel(); //客户端断开需要将key在真正的keys集合中移除
                    }
                }
                //用完要删除key
                iterator.remove();
            }
        }
    }
}

ByteBuffer的大小分配

监听channel事件

获取cpu的个数

NIO和BIO的区别

IO模型

注意:异步是没有阻塞情况的,异步是非阻塞的

Netty:异步的事件驱动的网络应用框架,用于网络编程,底层使用nio的方式开发

netty简单服务端和客户端通信代码

public class HelloService {
    public static void main(String[] args) {
        //1.启动器,负责组装netty组件,启动服务器
        new ServerBootstrap()
                //2.BossEvenLoop,WorkerEvenLoop(selector,thread)
                .group(new NioEventLoopGroup())
                //3.选择服务器的ServerSocketChannel实现
                .channel(NioServerSocketChannel.class)
                //4.boss 负责处理连接 worker(child)负责处理读写,决定worker能执行哪些操作
                .childHandler(
                    //5.channel代表和客户端进行数据读写的通道,Initializer初始化,负责添加别的handler
                    new ChannelInitializer<NioSocketChannel>() {
                    //连接建立后调用initChannel方法
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        //6.添加具体的handler
                        ch.pipeline().addLast(new StringDecoder()); //讲byte转换为字符串
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){ //自定义handler
                            @Override //读事件
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                System.out.println(msg);
                            }
                        });
                    }
                })
                .bind(8088); //服务器的端口
    }
}
public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        //启动类
        new Bootstrap()
                //添加EvenLoop
                .group(new NioEventLoopGroup())
                //选择客户端channel实现
                .channel(NioSocketChannel.class)
                //添加处理器
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    //连接建立后调用initChannel方法
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                //连接服务器
                .connect(new InetSocketAddress("localhost", 8088))
                .sync()
                .channel()
                .writeAndFlush("fed1231564");
    }
}

netty中的组件:EventLoop、Channel、Future&Promise、Handler&Piepeline、ByteBuf

EventLoop:

EventLoop的简单使用:

public class TestEventLoop {
    public static void main(String[] args) {
        //1.创建事件循环组
        EventLoopGroup group = new NioEventLoopGroup(2); // io 事件,普通任务,定时任务
        //2.获取下一事件循环对象
        System.out.println(group.next());
        System.out.println(group.next());
        System.out.println(group.next());
        //3.执行普通任务
        group.next().submit(() ->{
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("qwe");
        });
        //执行定时任务
        group.next().scheduleAtFixedRate(()->{
            System.out.println("123");
        },0, 1, TimeUnit.SECONDS);
    }
}
public class EventLoopServer {
    public static void main(String[] args) {
        EventLoopGroup group = new DefaultEventLoopGroup();
        //1.启动器,负责组装netty组件,启动服务器
        new ServerBootstrap()
                //2.BossEvenLoop,WorkerEvenLoop(selector,thread)
                .group(new NioEventLoopGroup(), new NioEventLoopGroup(2))
                //3.选择服务器的ServerSocketChannel实现
                .channel(NioServerSocketChannel.class)
                //4.boss 负责处理连接 worker(child)负责处理读写,决定worker能执行哪些操作
                .childHandler(
                        //5.channel代表和客户端进行数据读写的通道,Initializer初始化,负责添加别的handler
                        new ChannelInitializer<NioSocketChannel>() {
                            //连接建立后调用initChannel方法
                            @Override
                            protected void initChannel(NioSocketChannel ch) throws Exception {
                                ch.pipeline().addLast("handler1", new ChannelInboundHandlerAdapter(){ //自定义handler
                                    @Override //读事件
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        ByteBuf buf = (ByteBuf)msg;
                                        System.out.println(buf.toString(Charset.defaultCharset()));
                                        //交给下一个handler继续处理
                                        ctx.fireChannelRead(msg);
                                    }
                                }).addLast(group, "handler2", new ChannelInboundHandlerAdapter(){ //自定义handler
                                    @Override //读事件
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        ByteBuf buf = (ByteBuf)msg;
                                        System.out.println(buf.toString(Charset.defaultCharset()));
                                    }
                                });
                            }
                        })
                .bind(8088); //服务器的端口
    }
}

channel:

Future&Promise

jdkFuture:

ublic class TestJdkFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService service = Executors.newFixedThreadPool(2);
        Future<Integer> future = service.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                Thread.sleep(1000);
                return 66;
            }
        });
        System.out.println(future.get());//线程阻塞获取结果
    }
}

nettyFuture:

public class TestNettyFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        NioEventLoopGroup group = new NioEventLoopGroup();
        EventLoop loop = group.next();
        Future<Integer> future = loop.submit(() -> {
            System.out.println("执行计算");
            Thread.sleep(1000);
            return 88;
        });
        //同步获取结果
        System.out.println(future.get());
        //异步获取结果
        future.addListener(new GenericFutureListener<Future<? super Integer>>() {
            @Override
            public void operationComplete(Future<? super Integer> future) throws Exception {
                System.out.println("拿到结果:" + future.getNow());
            }
        });
        System.out.println("结束");
    }
}

nettyPromise:

public class TestNettyPromise {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        EventLoop eventLoop = new NioEventLoopGroup().next();
        DefaultPromise<Integer> promise = new DefaultPromise<>(eventLoop);
        new Thread(()->{
            System.out.println("开始计算");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //执行计算放到promise中
            promise.setSuccess(30);
        }).start();
        System.out.println(promise.get());
    }
}

Handler&Piepeline:

快速测试类EmbeddedChannel:

public class TestEmbeddedChannel {
    public static void main(String[] args) {
        ChannelInboundHandlerAdapter h1 = new ChannelInboundHandlerAdapter(){
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                System.out.println("1");
                //System.out.println(StandardCharsets.UTF_8.decode((ByteBuffer)msg));
                ByteBuf buf = (ByteBuf)msg;
                System.out.println(buf.toString(StandardCharsets.UTF_8));
                super.channelRead(ctx, msg);
            }
        };
        ChannelInboundHandlerAdapter h2 = new ChannelInboundHandlerAdapter(){
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                System.out.println("2");
                super.channelRead(ctx, msg);
            }
        };
        ChannelOutboundHandlerAdapter h3 = new ChannelOutboundHandlerAdapter(){
            @Override
            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                System.out.println("3");
                ByteBuf buf = (ByteBuf)msg;
                System.out.println(buf.toString(Charset.defaultCharset()));
                super.write(ctx, msg, promise);
            }
        };
        ChannelOutboundHandlerAdapter h4 = new ChannelOutboundHandlerAdapter(){
            @Override
            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                System.out.println("4");
                super.write(ctx, msg, promise);
            }
        };
        EmbeddedChannel channel = new EmbeddedChannel(h1, h2, h3, h4);
        //模拟入站操作
        channel.writeInbound(ByteBufAllocator.DEFAULT.buffer().writeBytes("fed789".getBytes()));
        //模拟出站操作
        channel.writeOutbound(ByteBufAllocator.DEFAULT.buffer().writeBytes("qwe456".getBytes()));
        //channel.writeInbound(StandardCharsets.UTF_8.encode("fed789456"));
    }
}

Bytebuf:

public class TestByteBuf {
    public static void main(String[] args) {
        ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
        System.out.println(buffer);
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < 300; i++) {
            stringBuilder.append("f");
        }
        buffer.writeBytes(stringBuilder.toString().getBytes());
        System.out.println(buffer);
    }
}

粘包和半包问题

netty的解码器:定长解码器、行解码器、LTC解码器

LTC解码器:

协议设计与解析

@Data
public abstract class Message implements Serializable {

    /*public static Class<?> getMessageClass(int messageType){
        return null;
    }*/

    private int sequenceId;
    private int messageType;

    public abstract int getMessageType();

    public static final int LoginRequestMessage = 0;
    public static final int LoginResponseMessage = 1;

}
@Slf4j
public class MessageCodec extends ByteToMessageCodec<Message> {

    /**
     * netty自定义协议数据写入
     * @param channelHandlerContext
     * @param message
     * @param byteBuf
     * @throws Exception
     */
    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, Message message, ByteBuf byteBuf) throws Exception {
        //魔数
        byteBuf.writeBytes(new byte[]{1,2,3,4});
        //版本
        byteBuf.writeByte(1);
        //序列化算法,jdk-0,json-1
        byteBuf.writeByte(0);
        //指令类型
        byteBuf.writeByte(message.getMessageType());
        //请求序号
        byteBuf.writeInt(message.getSequenceId());
        //无意义,对齐填充
        byteBuf.writeInt(0xff);
        //获取正文内容
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(message);
        byte[] bytes = bos.toByteArray();
        //获取长度
        byteBuf.writeInt(bytes.length);
        //写入内容
        byteBuf.writeBytes(bytes);
    }

    /**
     * netty自定义协议数据读取
     * @param channelHandlerContext
     * @param byteBuf
     * @param list
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
        int magicNum = byteBuf.readInt();
        byte version = byteBuf.readByte();
        byte serializableType  = byteBuf.readByte();
        byte messageType  = byteBuf.readByte();
        int sequenceId  = byteBuf.readInt();
        byteBuf.readInt();
        int length = byteBuf.readInt();
        byte[] bytes = new byte[length];
        byteBuf.readBytes(bytes,0, length);
        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
        Message message  = (Message) ois.readObject();
        log.debug("{},{},{},{},{},{}", magicNum, version, serializableType, messageType, sequenceId, length);
        log.debug("msg:{}", message);
        list.add(message);
    }

}

不可共享解码类:ByteToMessageCodec

可共享解码类:MessageToMessageCodec,必须要配合LengthFieldBasedFrameDecoder一起使用

CountDownLatch:倒数计时器,可用于两个线程之间通信

连接假死,连接假死检测器:IdleStateHandler,入站出站处理器:ChannelDuplexHandler

对象序列化:

public interface Serializer {
    /**
     * 反序列化
     * @param clazz
     * @param bytes
     * @param <T>
     * @return
     */
    <T> T deserialize(Class<T> clazz, byte[] bytes);

    /**
     * 序列化
     * @param object
     * @param <T>
     * @return
     */
    <T> byte[] serialize(T object);

    enum Algorithm implements Serializer{

        Java{
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes) {
                try {
                    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                    return (T) ois.readObject();
                }catch (Exception e){
                    throw new RuntimeException("反序列化失败", e);
                }
            }

            @Override
            public <T> byte[] serialize(T object) {
                try {
                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
                    ObjectOutputStream oos = new ObjectOutputStream(bos);
                    oos.writeObject(object);
                    return bos.toByteArray();
                } catch (IOException e) {
                   throw new RuntimeException("序列化失败", e);
                }
            }
        },

        Json {
            @Override
            public <T> T deserialize(Class<T> clazz, byte[] bytes) {
                String json = new String(bytes, StandardCharsets.UTF_8);
                return new Gson().fromJson(json, clazz);
            }

            @Override
            public <T> byte[] serialize(T object) {
                String json = new Gson().toJson(object);
                return json.getBytes(StandardCharsets.UTF_8);
            }
        }
    }
}

半连接队列:tcp3次握手建立连接的时候还没完全建立连接的客户端会先放到这个队列

全连接队列:已经完成tcp3次握手但是服务端还来不及处理的客户端会放到这个队列

ChannelOption.SO_BACKLOG:设置全连接队列的长度

NioEventLoop组成部分:selector(事件监听器),thread(事件处理线程),queue(任务队列)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值