netty helloworld

一切从 helloworld 开始

1.从 helloworld 开始

主函数部分

主函数部分,设置接收链接的 nio 池。处理事件的 nio 池。
输入任意字符退出。

HelloServer

package com.yhy;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.util.Scanner;

public class HelloServer {
    private int PORT = 8080;
    //接收请求的 nio 池
    private EventLoopGroup bossGroup = new NioEventLoopGroup();
    //接收数据的 nio 池
    private EventLoopGroup workerGroup = new NioEventLoopGroup();


    public static void main( String args[] ){
        HelloServer helloServer = new HelloServer();
        try {
            helloServer.start();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Scanner in=new Scanner(System.in); //使用Scanner类定义对象
        in.next();

        helloServer.stop();
    }

    public void start() throws InterruptedException {
        ServerBootstrap b = new ServerBootstrap();
        //指定接收链接的 NioEventLoop,和接收数据的 NioEventLoop
        b.group(bossGroup, workerGroup);
        //指定server使用的 channel
        b.channel(NioServerSocketChannel.class);
        //初始化处理请求的编解码,处理响应类等
        b.childHandler(new HelloServerInitializer());
        // 服务器绑定端口监听
        b.bind(PORT).sync();
    }

    public void stop(){
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
    }
}

初始化类

netty server 在启动的时候需要在初始化类里设置必要的参数。
主要初始化业务处理之前的解码器,业务处理之后的编码器,以及业务处理类。
这里为了简单起见我们使用 StringEncoder 和 StringDecoder 做编解码器。

package com.yhy;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;


public class HelloServerInitializer extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        // 以("\n")为结尾分割的 解码器
        pipeline.addLast("framer",
                new DelimiterBasedFrameDecoder(2048, Delimiters.lineDelimiter()));

        //字符串编码和解码
        pipeline.addLast("decoder",new StringDecoder());
        pipeline.addLast("encoder",new StringEncoder());

        // 自己的逻辑Handler
        pipeline.addLast("handler",new HelloServerHandler());
    }
}

业务处理类

我们在上面初始化服务的时候指定了业务处理类。下面就是业务处理类。指定要处理哪些事件。
一般情况下我们只要继承 SimpleChannelInboundHandler 类。最常见的情况是,你的应用 程序会利用一个ChannelHandler来接收解码消息, 并对该数据应用业务逻辑。 要创建一个这样 的 ChannelHandler, 你只需要扩展基类 SimpleChannel-InboundHandler, 其中 T 是你要处理的消息的 Java 类型。在这个 ChannelHandler 中,你将需要重写基类的一个 或者 多个方法,并且获取一个到 ChannelHandlerContext 的引用, 这个引用将作为输入参数传递 给 ChannelHandler 的所有方法。

package com.yhy;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class HelloServerHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        //处理消息
        System.out.println("channelRead0" + ctx.channel().remoteAddress() + ":" + msg);
        //返回消息
        ctx.channel().writeAndFlush("recvied message");
    }

    //客户端链接的时候触发
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelRegistered:"+ctx.channel().remoteAddress());
    }

    //客户端断开链接的时候触发
    @Override
    public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelUnregistered:"+ctx.channel().remoteAddress());
    }

    //客户端链接上来的时候触发
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelActive:"+ctx.channel().remoteAddress());
    }

    //客户端断开链接的时候触发
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive:"+ctx.channel().remoteAddress());
    }

    //完成一次读操作知 channle 的都数据都读完了(可读字节数为0),区别channelRead0() 这个是每次从channel读到数据的时候都触发
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete:"+ctx.channel().remoteAddress());
    }

    //用户自定义的触发,后面超时无数据会用到
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        System.out.println("userEventTriggered:"+ctx.channel().remoteAddress());
    }

    //Channel的可写状态变化通知事件
    @Override
    public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelWritabilityChanged:"+ctx.channel().remoteAddress());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught:"+ctx.channel().remoteAddress());
    }
}

各种事件的解释

channelRegistered: Invoked when a Channel is registered to its EventLoop and is able to handle I/O.
channelUnregistered: Invoked when a Channel is deregistered from its EventLoop and cannot handle any I/O.
channelActive: Invoked when a Channel is active; the Channel is connected/bound and ready.
channelInactive: Invoked when a Channel leaves active state and is no longer connected to its remote peer.
channelReadComplete: Invoked when a read operation on the Channel has completed.
channelRead: Invoked if data are read from the Channel.
channelWritabilityChanged: Invoked when the writability state of the Channel changes. The user can ensure writes are not done too fast (with risk of an OutOfMemoryError) or can resume writes when the Channel becomes writable again.Channel.isWritable() can be used to detect the actual writability of the channel. The threshold for writability can be set via Channel.config().setWriteHighWaterMark() and Channel.config().setWriteLowWaterMark().
userEventTriggered(…): Invoked when a user calls Channel.fireUserEventTriggered(…) to pass a pojo through the ChannelPipeline. This can be used to pass user specific events through the ChannelPipeline and so allow handling those events.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章