zoukankan      html  css  js  c++  java
  • 【Netty】第一个Netty应用

    一、前言

      前面已经学习完了Java NIO的内容,接着来学习Netty,本篇将通过一个简单的应用来了解Netty的使用。

    二、Netty应用

      2.1 服务端客户端框架图

      下图展示了Netty中服务端与客户端在之间的关系,客户端连接至服务器,然后两者之间互相通信,服务器可连接多个客户端。

      

      2.2 服务端

      服务端主要包含两部分内容,分为引导和实现服务器处理器。引导用于设置端口号等信息,处理器主要是用于处理用户自定义逻辑。

      1. 引导服务端

      引导服务端类名为EchoServer,其代码如下 

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    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 java.net.InetSocketAddress;
    
    public class EchoServer {
        private int port;
    
        public EchoServer(int port) {
            this.port = port;
        }
    
        public void start() throws Exception {
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();
                b.group(group)
                        .channel(NioServerSocketChannel.class)
                        .localAddress(new InetSocketAddress(port))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new EchoServerHandler());
                            }
                        });
                // 绑定端口,开始接收连接
                ChannelFuture f = b.bind().sync();
                System.out.println("Server start listen at " + port);
                // 等待服务器socket关闭
                f.channel().closeFuture().sync();
            } finally {
                group.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            int port;
            if (args.length > 0) {
                port = Integer.parseInt(args[0]);
            } else {
                port = 8080;
            }
            new EchoServer(port).start();
        }
    }

      说明:其流程大致如下

      ① 创建NioEventLoopGroup实例来处理事件,如接受连接,读写数据等。

      ② 创建ServerBootstrap实例。

      ③ 指定服务端绑定的端口。

      ④ 设置childHandler来处理每一次连接。

      ⑤ 使用ServerBootstrap的bind方法进行绑定并同步直至其完成绑定。

      2. 实现服务端逻辑

      从代码来看引导服务器只是完成了服务端的创建,如指定端口和处理器等,并未涉及到服务端的具体逻辑,其具体业务逻辑可以在处理器中完成,处理器需要继承ChannelInboundHandlerAdapter,本实例中处理器为EchoServerHandler,其代码如下 

    package com.hust.grid.leesf.chapter2;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.ByteBufUtil;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelFutureListener;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    
    public class EchoServerHandler extends ChannelInboundHandlerAdapter {
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            ByteBuf bb = (ByteBuf) msg;
            bb.markReaderIndex();
            System.out.println("Server received: " + ByteBufUtil
                    .hexDump(bb.readBytes(bb.readableBytes())));
            bb.resetReaderIndex();
            ctx.write(msg);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) {
            ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
                    .addListener(ChannelFutureListener.CLOSE);
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }

      说明:当服务器接受到消息后,channelRead方法会被调用,具体消息为msg,用户可以对该消息进行处理,本例中首先将接收的消息进行转化后打印,然后将消息写入ctx中,其中值得注意的是需要标记读索引,然后恢复,否则写入的数据为空。channelReadComplete将之前写入客户端的消息刷新,待操作完成后关闭。exceptionCaught方法则会捕捉处理中的异常。

      2.3 客户端

      客户端部分的逻辑同服务器类似,也包含引导客户端和实现客户端处理器两部分,客户端连接服务端,并且接收服务端的消息,关闭连接等。

      1. 引导客户端

      引导客户端类名为EchoClient,其代码如下  

    package com.hust.grid.leesf.chapter2;
    
    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 java.net.InetSocketAddress;
    
    public final class EchoClient {
    
        private String host;
        private int port;
    
        public EchoClient(String host, int port) {
            this.host = host;
            this.port = port;
        }
    
        public void start() throws Exception {
            EventLoopGroup group = new NioEventLoopGroup();
            try {
                Bootstrap b = new Bootstrap();
                b.group(group)
                        .channel(NioSocketChannel.class)
                        .remoteAddress(new InetSocketAddress(host, port))
                        .handler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            public void initChannel(SocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new EchoClientHandler());
                            }
                        });
    
                // 启动客户端
                ChannelFuture f = b.connect().sync();
                // 直到连接关闭
                f.channel().closeFuture().sync();
            } finally {
                group.shutdownGracefully();
            }
        }
    
        public static void main(String[] args) throws Exception {
            String host = "127.0.0.1";
            int port = 8080;
            if (args.length == 2) {
                host = args[0];
                port = Integer.parseInt(args[1]);
            }
    
            new EchoClient(host, port).start();
        }
    }

      说明:其引导部分与服务端非常类似,流程非常类似,其给出了服务端的地址和端口号,Bootstrap的connect函数将会根据指定的地址和端口号连接服务器。

      2. 实现客户端逻辑

      本部分完成用户实际的业务逻辑,本例中的EchoClientHandler继承SimpleChannelInboundHandler,需要重写如下三个函数

        · channelActive函数,在建立了与服务端的连接后该函数被调用。

        · channelRead0函数,当接收到服务端发送来的消息后被调用。

        · exceptionCaught函数,当处理发生异常时被调用。

      EchoClientHandler的代码如下  

    package com.hust.grid.leesf.chapter2;
    
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.ByteBufUtil;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.SimpleChannelInboundHandler;
    import io.netty.util.CharsetUtil;
    
    public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) {
            ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
        }
    
        @Override
        public void channelRead0(ChannelHandlerContext ctx, ByteBuf in) {
            System.out.println("Client received: " + ByteBufUtil
                    .hexDump(in.readBytes(in.readableBytes())));
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
            cause.printStackTrace();
            ctx.close();
        }
    }

      说明:当同服务器的连接建立后,客户端会发送消息至服务端,然后当接收到服务端发送来的消息时,打印该消息。

      2.4 运行

      1. pom.xml文件

      由于本应用依赖的jar文件使用maven构建,其pom.xml文件如下。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>NettyInAction</groupId>
        <artifactId>com.hust.grid.leesf</artifactId>
        <version>1.0-SNAPSHOT</version>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <version.jackson.core>2.6.3</version.jackson.core>
        </properties>
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.2</version>
                    <configuration>
                        <optimize>true</optimize>
                        <source>1.7</source>
                        <target>1.7</target>
                    </configuration>
                </plugin>
    
            </plugins>
        </build>
        <dependencies>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>io.netty</groupId>
                <artifactId>netty-all</artifactId>
                <version>4.0.32.Final</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>${version.jackson.core}</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>${version.jackson.core}</version>
            </dependency>
        </dependencies>
    
    
    </project>

      2. 运行服务端

      启动EchoServer,等待客户端连接。

      3. 运行客户端

      启动EchoClient,连接服务端并发送消息。

      其中服务端的运行结果如下。  

    Server start listen at 8080
    Server received: 4e6574747920726f636b7321

      客户端的运行结果如下。 

    Client received: 4e6574747920726f636b7321

    三、总结

      本篇博文讲解了Netty的简单应用,通过简单应用对Netty有所了解,具体的细节将会在之后的博文中进行讲解,本文的代码已经上传至github,也谢谢各位园友的观看~ 

  • 相关阅读:
    Git上手:四种常见的Git协同工作方式
    Git上手:Git扫盲区
    理解web缓存
    浅谈对技术债的理解
    保护个人隐私,从我做起
    cookie注意事项
    了解JavaScript核心精髓(二)
    简单实现nodejs爬虫工具
    浅谈我所见的CSS组织风格
    利用正则表达式清除多余的空行
  • 原文地址:https://www.cnblogs.com/leesf456/p/6824345.html
Copyright © 2011-2022 走看看