netty的聊天demo

    netty是一个基于nio的通讯框架,如果想要看懂netty那么就先要对nio有一定了解,但这篇文章并不讨论nio。

    传统的socket通信的话一个线程只能应对一个客户端,但是nio技术中就可以在一条线程开启多个channel通道应对多个客户端,从而降低服务端的压力。
    直接上代码:

public class Server {

    private int port;

    public Server(int port) {
        this.port = port;
    }

    public void run() throws Exception {
        //创建两个EventLoopGroup对象,boss用于接收所有进来的连接然后下发给worker。
        // 而worker是用于专门对这个客户端进行服务,有多少个客户端就有多少worker。
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            //创建一个server端引导项进行各项属性的设置。
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup);
            b.channel(NioServerSocketChannel.class);
            b.childHandler(new ServerInitializer());
            b.option(ChannelOption.SO_BACKLOG, 128);
            b.childOption(ChannelOption.SO_KEEPALIVE, true);

            System.out.println("server 启动");

            // 同步绑定端口,开始接收进来的连接
            ChannelFuture f = b.bind(port).sync();

            // 同步等待服务器  socket 关闭 。
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();

            System.out.println("server关闭了");
        }
    }

    public static void main(String[] args) throws Exception {
        new Server(16666).run();
    }
}
    netty的server端的大体完成了,但还需要一个ChannelInitializer以及SimpleChannelInboundHandler。
public class ServerInitializer extends
        ChannelInitializer<SocketChannel> {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        /**
         * 分割器,遇到换行符则进行分隔并对这个1024字节数据进行处理。这可以用于解决黏包问题,当然更高级的黏包解决方案就需要自己开动脑筋了。
         * 但如果你的客户端并不发送带有\n换行符,那么就有可能客户端一直发送消息但你就是没能及时处理
         * 导致buffer被写满抛出tooLongFrameLength异常。
         */
        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
        /**
         * 解码器,如果你的项目是要接收字节数组这种数据的话就不需要StringEncoder,否则它就会将接收到的数据进行默认环境下编码成字符串;
         */
        pipeline.addLast("decoder", new StringDecoder());
        /**
         * 编码器,将你要发送给客户端的数据编码为字符串。
         */
        pipeline.addLast("encoder", new StringEncoder());
        /**
         * handler
         */
        pipeline.addLast("handler", new ServerHandler());

        System.out.println("客户端:" + ch.remoteAddress() + "连接上");
    }
}

public class ServerHandler extends SimpleChannelInboundHandler<String> {
    //创建一个全局的channelGroup以方便调用。
    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        channels.writeAndFlush(incoming.remoteAddress() + " 加入\n");
        //将连接上的channel添加进Group中去,以便调用
        channels.add(ctx.channel());
    }

    /**
     * 客户端close时调用该方法。
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();

        channels.writeAndFlush(incoming.remoteAddress() + " 离开\n");
    }

    /**
     * 读取内容。
     *
     * @param ctx
     * @param s
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        Channel incoming = ctx.channel();
        for (Channel channel : channels) {
            if (channel != incoming) {
                //向客户端写入数据
                channel.writeAndFlush("服务端已收到\n");
                System.out.println("客户端说:" + s);
            } else {
                //向客户端写入数据
                channel.writeAndFlush("服务端已收到\n");
                System.out.println("客户端说:" + s);
            }
        }
    }

    /**
     * 检测是否在线方法。
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println("客户端:" + incoming.remoteAddress() + "在线");
    }

    /**
     * 客户端掉线时调用该方法
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel incoming = ctx.channel();
        System.out.println("客户端:" + incoming.remoteAddress() + "掉线");
    }

    /**
     * 该channel发生异常时调用的方法,
     *
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        Channel incoming = ctx.channel();
        System.out.println("客户端:" + incoming.remoteAddress() + "异常");
        // 当出现异常就关闭连接
        cause.printStackTrace();
        ctx.close();
    }
}
    ------------------分割线---------------

接下来的是客户端代码:

public class Client {

    public static void main(String[] args) throws Exception{
        new Client("localhost", 16666).run();
    }

    private final String host;
    private final int port;

    public Client(String host, int port){
        this.host = host;
        this.port = port;
    }

    public void run() throws Exception{
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            //创建一个引导项,设置各种属性项
            Bootstrap bootstrap  = new Bootstrap()
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ClientInitializer());
            //同步连接到服务端
            Channel channel = bootstrap.connect(host, port).sync().channel();
            //读取控制台的信息。
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            while(true){
                //channel向服务端发送讯息。
                channel.writeAndFlush(in.readLine() + "\r\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }

    }

}
public class ClientInitializer extends ChannelInitializer<SocketChannel> {

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast("framer", new DelimiterBasedFrameDecoder(1024, Delimiters.lineDelimiter()));
        pipeline.addLast("decoder", new StringDecoder());
        pipeline.addLast("encoder", new StringEncoder());
        pipeline.addLast("handler", new ClientHandler());
    }
}
public class ClientHandler extends  SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
        System.out.println(s);
    }
}
         一个Netty的基本通讯demo就完成了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值