【转载】Netty入门使用教程

原创:转载需注明原创地址 https://www.cnblogs.com/fanerwei222/p/11827026.html
本文介绍Netty的使用, 结合我本人的一些理解和操作来快速的让初学者入门Netty, 理论知识会有, 但是不会太深入, 够用即可, 仅供入门! 需要想详细的知识可以移步Netty官网查看官方文档!

理论知识 : Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序

当然, 我们这里主要是用Netty来发送消息, 接收消息, 测试一下demo, 更厉害的功能后面再慢慢发掘, 我们先看看这玩意怎么玩, 后面再深入

需要工具和Java类:

netty-4.1.43

netty服务器类      SayHelloServer.java

netty服务端处理器类   SayHelloServerHandler.java

netty客户端类      SayHelloClient.java

netty客户端处理器类   SayHelloClientHandler.java

服务器main方法测试类  MainNettyServer.java

客户端main方法测试类  MainNettyClient.java

首先先来一张演示图, 最下面也会放:
我们看完以下部分就能实现这个东西了!
在这里插入图片描述
话不多说, 先贴代码:

package netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import netty.handler.SayHelloServerHandler;

/**
 * sayhello 服务器
 */
public class SayHelloServer {

    /**
     * 端口
     */
    private int port ;

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

    public void run() throws Exception{
        /**
         * Netty 负责装领导的事件处理线程池
         */
        EventLoopGroup leader = new NioEventLoopGroup();
        /**
         * Netty 负责装码农的事件处理线程池
         */
        EventLoopGroup coder = new NioEventLoopGroup();
        try {
            /**
             * 服务端启动引导器
             */
            ServerBootstrap server = new ServerBootstrap();

            server
                    .group(leader, coder)//把事件处理线程池添加进启动引导器
                    .channel(NioServerSocketChannel.class)//设置通道的建立方式,这里采用Nio的通道方式来建立请求连接
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //构造一个由通道处理器构成的通道管道流水线
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            /**
                             * 此处添加服务端的通道处理器
                             */
                            socketChannel.pipeline().addLast(new SayHelloServerHandler());
                        }
                    })
                    /**
                     * 用来配置一些channel的参数,配置的参数会被ChannelConfig使用
                     * BACKLOG用于构造服务端套接字ServerSocket对象,
                     * 标识当服务器请求处理线程全满时,
                     * 用于临时存放已完成三次握手的请求的队列的最大长度。
                     * 如果未设置或所设置的值小于1,Java将使用默认值50
                     */
                    .option(ChannelOption.SO_BACKLOG, 128)
                    /**
                     * 是否启用心跳保活机制。在双方TCP套接字建立连接后(即都进入ESTABLISHED状态)
                     * 并且在两个小时左右上层没有任何数据传输的情况下,这套机制才会被激活。
                     */
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            /**
             * 服务端绑定端口并且开始接收进来的连接请求
             */
            ChannelFuture channelFuture = server.bind(port).sync();
            /**
             * 查看一下操作是不是成功结束了
             */
            if (channelFuture.isSuccess()){
                //如果没有成功结束就处理一些事情,结束了就执行关闭服务端等操作
                System.out.println("服务端启动成功!");
            }
            /**
             * 关闭服务端
             */
            channelFuture.channel().closeFuture().sync();
            System.out.println("服务端即将关闭!");
        } finally {
            /**
             * 关闭事件处理组
             */
            leader.shutdownGracefully();
            coder.shutdownGracefully();
            System.out.println("服务端已关闭!");
        }

    }
}
package netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * 服务端入站处理器适配器的继承类
 * 用来处理服务端的一些事情
 * 根据需要来实现一些方法
 */
public class SayHelloServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        System.out.println("直接打印服务端需要处理的信息: " + buf.toString(CharsetUtil.UTF_8));
        ByteBuf res = Unpooled.wrappedBuffer(new String("塔台收到!塔台收到!信息如下, 请确认 " + buf.toString(CharsetUtil.UTF_8)).getBytes());
        /**
         * 给客户端回复消息
         */
        ctx.writeAndFlush(res);
    }

    /**
     * 连接成功后,自动执行该方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("服务器首次处理!");
        /**
         * 这种发送的消息格式是错误的!!!!!
         * 消息格式必须是ByteBuf才行!!!!!
         */
        ctx.writeAndFlush("Hello is server !");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        /**
         * 异常捕获
         */
        cause.printStackTrace();
        ctx.close();
    }
}
package netty.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 netty.handler.SayHelloClientHandler;

/**
 * sayhello 客户端
 */
public class SayHelloClient {

    private int port;
    private String host = "127.0.0.1";
    private Channel channel;

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

    /**
     * 客户端运行方法
     * @throws InterruptedException
     */
    public void run() throws InterruptedException {
        /**
         * 负责装客户端的事件处理线程池
         */
        EventLoopGroup clientWorker = new NioEventLoopGroup();
        try {
            /**
             * netty客户端引导启动器
             */
            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                    .group(clientWorker)//把事件处理线程池添加进启动引导器
                    .channel(NioSocketChannel.class)//设置通道的建立方式,这里采用Nio的通道方式来建立请求连接
                    //.option(ChannelOption.SO_KEEPALIVE, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            /**
                             * 此处添加客户端的通道处理器
                             */
                            socketChannel.pipeline().addLast(new SayHelloClientHandler());
                        }
                    });
            /**
             * 客户端绑定端口并且开始发起连接请求
             */
            ChannelFuture future = bootstrap.connect(host, port).sync();
            if (future.isSuccess()){
                System.out.println("客户端连接服务器成功!");
            }
            /**
             * 将通道设置好, 以便外面获取
             */
            this.channel = future.channel();
            /**
             * 关闭客户端
             */
            future.channel().closeFuture().sync();
            System.out.println("客户端即将关闭!");
        } finally {
            /**
             * 关闭事件处理组
             */
            clientWorker.shutdownGracefully();
            System.out.println("客户端已关闭!");
        }
    }

    public Channel getChannel(){
        return this.channel;
    }
}
package netty.handler;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.nio.charset.Charset;
import java.util.Date;

/**
 * sayhello 客户端处理器
 */
public class SayHelloClientHandler extends ChannelInboundHandlerAdapter {

    /**
     * 通道信息读取处理
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        ByteBuf m = (ByteBuf) msg; // 将消息转化成bytebuf
        try {
            System.out.println("客户端直接打印接收到的消息: " + m.toString(Charset.defaultCharset()));
            long currentTimeMillis = (m.readUnsignedInt() - 2208988800L) * 1000L;
            System.out.println(new Date(currentTimeMillis));
            /**
             * 给服务端回复消息
             */
            ctx.writeAndFlush("客户端收到! 消息为: " + m.toString(Charset.defaultCharset()));
        } finally {
            m.release();
        }
    }

    /**
     * 连接成功后,自动执行该方法
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        /**
         * 往服务端发送消息
         * 消息格式必须是ByteBuf才行!!!!!
         * 如果是其他格式服务端是接收不到的!!!!
         */
        String helo = "你好呀!";
        ByteBuf byteBuf = Unpooled.wrappedBuffer(helo.getBytes());
        ctx.channel().writeAndFlush(byteBuf);
        System.out.println("首次连接完成!");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        ctx.close();
    }
}
package netty;

import netty.server.SayHelloServer;

/**
 * Netty server 使用main类
 */
public class MainNettyServer {

    /**
     * 端口
     */
    private static int port = 8686;

    public static void main(String[] args) throws Exception {
        /**
         * 启动netty服务器
         */
        SayHelloServer sayHelloServer = new SayHelloServer(port);
        sayHelloServer.run();
    }
}
package netty;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import netty.client.SayHelloClient;

import java.util.Scanner;

/**
 * 客户端main方法类
 */
public class MainNettyClient {
    public static void main(String[] args) throws InterruptedException {
        /**
         * 创建netty客户端
         */
        SayHelloClient client = new SayHelloClient(8686);
        /**
         * 新建一个线程让它单独去跑,我们可以main方法测试一下发送消息和接受消息
         */
        Thread clientThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    client.run();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        clientThread.start();
        /**
         * 如果不新建一个线程去跑客户端的话, 以下的代码就执行不到
         * 这里用while是因为客户端的channel并不能立马生成, 会在client启动后一段时间才生成获取到
         * 所以需要延迟一点获取channel, 否则channel为null
         */
        Channel channel = null;
        boolean isStart = false;
        while (!isStart) {
            if (null != client.getChannel()) {
                channel = client.getChannel();
                isStart = true;
            }
        }
        String helo = "你好呀!我这里是客户端, 收到请回答";
        ByteBuf byteBuf = Unpooled.wrappedBuffer(helo.getBytes());
        channel.writeAndFlush(byteBuf);
        /**
         * 我们通过控制台输入来给服务端发送消息
         * 此处只做模拟使用
         */
        for (int i = 0; i < 10 ; i++) {
            Scanner scanner = new Scanner(System.in);
            String text = scanner.nextLine();
            channel.writeAndFlush(Unpooled.wrappedBuffer(text.getBytes()));
        }
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值