【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,也謝謝各位園友的觀看~ 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM