始于足下.|

MuXinu

园龄:2年7个月粉丝:3关注:1

Netty入门程序

本章使用Netty开发一个入门程序,使用ServerBootstrap开发时间服务TimeServer,使用Bootstrap开发客户端TimeClient请求TimeServer获取时间。
 开发 TimeServer之前,先回顾一下使用 NIO 进行服务端开发的步骤。
(1)创建 ServerSocketChannel,配置它为非阻塞模式;
(2)绑定监听,配置 TCP 参数,例如 backlog 大小;
(3)创建一个独立的 IO线程,用于轮询多路复用器 Seleetor;
(4)创建 Selector,将之前创建的 ServerSocketChannel 注册到 Selector 上, 监听SelectionKey.ACCEPT;
(5)启动IO线程,在循环体中执行 Selector.select()方法,轮询就绪的 Channel;
(6)当轮询到了处于就绪状态的 Channel 时,需要对其进行判浙,如果是 OP_ACCEPT状态,说明是新的客户端接入,则调用ServerSocketChannel.accept()方法接受新的客户端;
(7)设置新接入的客户端链路 SocketChannel 为非阻塞模式,配置其他的一些 TCP 参数;
(8)将 SocketChannel 注册到 Seleetor,监听 OP READ 操作位;
(9)如果轮询的 Channel 为 OP READ,则说明 SocketChannel 中有新的就绪的数据包需要读取,则构造 Byte Buffer 对象,读取数据包;
(10)如果轮询的 Channel 为 OP WRITE,说明还有数据没有发送完成,需要继续发送。
 

一、Netty入门程序

 

1.1 Netty开发服务端代码的流程:

复制代码
1.创建EventLoopGroup
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
2.初始化ServerBootstrap
ServerBootstrap bootstrap = new ServerBootstrap();
3.配置ServerBootstrap
bootstrap.group(bossGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.option(ChannelOption.SO_BACKLOG, 128);
bootstrap.childHandler()
4.绑定端口并启动服务
try {
    ChannelFuture future = bootstrap.bind(PORT).sync();
    future.channel().closeFuture().sync();
} catch (InterruptedException e) {
    ...
} finally {
    bossGroup.shutdownGracefully();
    workerGroup.shutdownGracefully();
}
在这个过程中,bind方法会同步阻塞,直到服务端绑定成功。当关闭future的channel时,表示服务端已关闭,这时可以安全地关闭EventLoopGroup,释放资源。
复制代码

1.1.1 服务端完整代码

TimeServer
复制代码
package Netty.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;

/**
 * Created by Idea 14 whih "netty"
 *
 * @Auhor: karl.zhao
 * @Email: karl.zhao@qq.com
 * @Date: 2015-11-28
 * @Time: 15:54
 */
public class TimeServer {
    public void bind(int port)throws Exception{
        /* 配置服务端的NIO线程组 */
        // NioEventLoopGroup类 是个线程组,包含一组NIO线程,用于网络事件的处理
        // (实际上它就是Reactor线程组)。
        // 创建的2个线程组,1个是服务端接收客户端的连接,另一个是进行SocketChannel的
        // 网络读写
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup WorkerGroup = new NioEventLoopGroup();

        try {
            // ServerBootstrap 类,是启动NIO服务器的辅助启动类
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup,WorkerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG,1024)
                    .childHandler(new ChildChannelHandler());

            // 绑定端口,同步等待成功
            ChannelFuture f= b.bind(port).sync();

            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        }finally {
            // 释放线程池资源
            bossGroup.shutdownGracefully();
            WorkerGroup.shutdownGracefully();
        }
    }

    private class ChildChannelHandler extends ChannelInitializer<SocketChannel>{
        @Override
        protected  void initChannel(SocketChannel arg0)throws Exception{
            arg0.pipeline().addLast(new TimeServerHandler());
        }
    }

    public static void main(String[]args)throws Exception{
        int port = 8080;
        if(args!=null && args.length>0){
            try {
                port = Integer.valueOf(args[0]);
            }
            catch (NumberFormatException ex){}
        }
        new TimeServer().bind(port);
    }
}
复制代码

 

TimeServerHandler
复制代码
package Netty.Server;

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

import java.util.Date;

/**
 * Created by Idea 14 whih "netty"
 *
 * @Auhor: karl.zhao
 * @Email: karl.zhao@qq.com
 * @Date: 2015-11-28
 * @Time: 16:13
 */
public class TimeServerHandler extends ChannelHandlerAdapter {
    // 用于网络的读写操作
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("the time server order : " + body);

        String currentTime = "QUERY TIME ORDER".equalsIgnoreCase(body) ? new Date(
                System.currentTimeMillis()).toString() : "BAD ORDER";
        ByteBuf resp = Unpooled.copiedBuffer(currentTime.getBytes());
        ctx.write(resp);
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();   // 它的作用是把消息发送队列中的消息写入SocketChannel中发送给对方
        // 为了防止频繁的唤醒Selector进行消息发送,Netty的write方法,并不直接将消息写入SocketChannel中
        // 调用write方法只是把待发送的消息发到缓冲区中,再调用flush,将发送缓冲区中的消息
        // 全部写到SocketChannel中。
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        ctx.close();
    }
}
复制代码

 

1.2 Netty开发客户端代码的流程:

复制代码
1.创建EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();
2.初始化Bootstrap
Bootstrap bootstrap = new Bootstrap();
3.配置Bootstrap
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000);
bootstrap.handler()
4.连接服务器
String host = "localhost";
int port = 8080;
ChannelFuture future = bootstrap.connect(host, port).addListener((ChannelFutureListener) future -> {
if (future.isSuccess()) {
System.out.println("Connected to the server successfully.");
} else {
System.err.println("Failed to connect to the server.");
future.cause().printStackTrace();
}
});
// 等待连接成功或失败
future.syncUninterruptibly();
5.关闭连接和EventLoopGroup
Channel channel = future.channel();
...
channel.closeFuture().sync();
group.shutdownGracefully();
复制代码

1.2.1 客户端完整代码

TimeClient
复制代码
package Netty.Client;

import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;

/**
 * Created by Idea 14 whih "netty"
 *
 * @Auhor: karl.zhao
 * @Email: karl.zhao@qq.com
 * @Date: 2015-11-28
 * @Time: 16:56
 */
public class TimeClient {
    public void connect(String host, int port) throws Exception {
        // 配置服务端的NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();

        try {
            // Bootstrap 类,是启动NIO服务器的辅助启动类
            Bootstrap b = new Bootstrap();
            b.group(group).channel(NioSocketChannel.class)
                    .option(ChannelOption.TCP_NODELAY, true)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast(new TimeClientHandler());
                        }
                    });

            // 发起异步连接操作
            ChannelFuture f = b.connect(host, port).sync();

            // 等待客服端链路关闭
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 8080;
        if (args != null && args.length > 0) {
            try {
                port = Integer.valueOf(args[0]);
            } catch (NumberFormatException ex) {
            }
        }
        new TimeClient().connect("127.0.0.1", port);
    }
}
复制代码

 

TimeClientHandler
复制代码
package Netty.Client;

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

import java.util.logging.Logger;

/**
 * Created by Idea 14 whih "netty"
 *
 * @Auhor: karl.zhao
 * @Email: karl.zhao@qq.com
 * @Date: 2015-11-28
 * @Time: 16:58
 */
public class TimeClientHandler extends ChannelHandlerAdapter {

    // 写日志
    private static final Logger logger =
            Logger.getLogger(TimeClientHandler.class.getName());

    private final ByteBuf firstMessage;

    public TimeClientHandler() {
        byte[] req = "QUERY TIME ORDER".getBytes();
        firstMessage = Unpooled.buffer(req.length);
        firstMessage.writeBytes(req);
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        ByteBuf buf = (ByteBuf) msg;
        byte[] req = new byte[buf.readableBytes()];
        buf.readBytes(req);
        String body = new String(req, "UTF-8");
        System.out.println("Now is : " + body);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        // 当客户端和服务端建立tcp成功之后,Netty的NIO线程会调用channelActive
        // 发送查询时间的指令给服务端。
        // 调用ChannelHandlerContext的writeAndFlush方法,将请求消息发送给服务端
        // 当服务端应答时,channelRead方法被调用
        ctx.writeAndFlush(firstMessage);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        logger.warning("message from:" + cause.getMessage());
        ctx.close();
    }
}
复制代码

 

1.3 运行结果

启动TimeServer,再启动TimeClient ,运行结果如下:
 
服务端:

 客户端:

 

摘自: 李林峰《netty权威指南》
 

本文作者:MuXinu

本文链接:https://www.cnblogs.com/MuXinu/p/18091573

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MuXinu  阅读(93)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起