admin管理员组

文章数量:1597470

Netty 入门学习教程之简易聊天室

  • 0x01 Netty 入门学习教程之简易聊天室
    • 1.1 Netty 简介
    • 1.1 功能
      • 1.1.1 设计
      • 1.1.2 使用方便
      • 1.1.3 性能
      • 1.1.4 安全性
      • 1.1.5 社区
  • 0x02 Netty 文档
    • 2.1 Netty 讨论社区
    • 2.2 最新稳定版本
    • 2.3 第三方文章
    • 2.4 示例
      • 2.4.1 基础
      • 2.4.2 文本协议
      • 2.4.3 二进制协议
      • 2.4.4 Http
      • 2.4.5 高级篇
      • 2.4.6 UDT
  • 0x03 Netty 4.x 用户指南
    • 3.1 问题
    • 3.2 解决方案
    • 3.3 入门
      • 3.3.1 入门准备
      • 3.3.2 编写Discord服务器
        • 3.3.2.1 pom.xml 中添加依赖
        • 3.3.2.2 创建 DiscardServerHandler
        • 3.3.2.3 编写main方法

0x01 Netty 入门学习教程之简易聊天室

1.1 Netty 简介

Netty是异步事件驱动的网络应用程序框架
用于快速开发可维护的高性能协议服务器和客户端。

Netty是一个NIO客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。 它极大地简化和简化了网络编程,例如TCP和UDP套接字服务器。

“快速简便”并不意味着最终的应用程序将遭受可维护性或性能问题的困扰。 Netty经过精心设计,结合了许多协议(例如FTP,SMTP,HTTP以及各种基于二进制和文本的旧式协议)的实施经验。 结果,Netty成功地找到了一种无需妥协即可轻松实现开发,性能,稳定性和灵活性的方法。

1.1 功能

1.1.1 设计

  • 适用于各种传输类型的统一API-阻塞和非阻塞套接字
  • 基于灵活且可扩展的事件模型,可将关注点明确分离
  • 高度可定制的线程模型-单线程,一个或多个线程池,例如SEDA
  • 真正的无连接数据报套接字支持(从3.1开始)

1.1.2 使用方便

  • 记录良好的Javadoc,用户指南和示例
  • 没有其他依赖关系,JDK 5(Netty 3.x)或6(Netty 4.x)就足够了
    注意:某些组件(例如HTTP / 2)可能有更多要求。

1.1.3 性能

  • 更高的吞吐量,更低的延迟
  • 减少资源消耗
  • 减少不必要的内存复制

1.1.4 安全性

  • 安全
  • 完整的SSL / TLS和StartTLS支持

1.1.5 社区

  • 提前发布,经常发布
  • 自2003年以来,作者一直在编写类似的框架,但他仍然发现您的反馈很宝贵!

0x02 Netty 文档

2.1 Netty 讨论社区

开始使用用户指南和API参考, Netty 社区

2.2 最新稳定版本

当前最新稳定版本是4.1

  • 用户指南
  • API 参考
  • 源代码
  • 新的和值得注意的

2.3 第三方文章

我们经常发现世界上有一些人比我们更擅长技术写作,并且为社区写了很多好文章。

https://netty.io/wiki/related-articles.html

2.4 示例

有几个示例可以帮助您更好地使用Netty。 建议从第一个开始到最后一个结束。

2.4.1 基础

Echo

Discard

Uptime

2.4.2 文本协议

Telnet

Quoto of the Moment

SecureChat

2.4.3 二进制协议

ObjectEcho

Factorial

WorldClock

2.4.4 Http

Snoop

FileServer

WebSockets

SPDY

CORS Demo

2.4.5 高级篇

ProxyServer

Port unfication

2.4.6 UDT

  • Byte streams ‐ use UDT in TCP-like byte streaming mode
  • Message flow ‐ use UDT in UDP-like message delivery mode
  • Byte streams in symmetric peer-to-peer rendezvous connect mode
  • Message flow in symmetric peer-to-peer rendezvous connect mode

0x03 Netty 4.x 用户指南

3.1 问题

Nowadays we use general purpose applications or libraries to communicate with each other. For example, we often use an HTTP client library to retrieve information from a web server and to invoke a remote procedure call via web services. However, a general purpose protocol or its implementation sometimes does not scale very well. It is like how we don’t use a general purpose HTTP server to exchange huge files, e-mail messages, and near-realtime messages such as financial information and multiplayer game data. What’s required is a highly optimized protocol implementation that is dedicated to a special purpose. For example, you might want to implement an HTTP server that is optimized for AJAX-based chat application, media streaming, or large file transfer. You could even want to design and implement a whole new protocol that is precisely tailored to your need. Another inevitable case is when you have to deal with a legacy proprietary protocol to ensure the interoperability with an old system. What matters in this case is how quickly we can implement that protocol while not sacrificing the stability and performance of the resulting application.

如今,我们使用通用应用程序或库相互通信。例如,我们经常使用HTTP Client库从Web服务器检索信息,并通过Web服务调用远程过程调用(RPC,Remote Procedure call)。但是,通用协议或其实现有时不能很好地扩展。就像我们不使用通用HTTP服务器来交换大文件,电子邮件和近乎实时的消息(例如财务信息和多人游戏数据)一样。所需要的是专门用于特殊目的的高度优化的协议实现。

例如,您可能想实现针对基于AJAX的聊天应用程序,媒体流或大文件传输进行了优化的HTTP服务器。您甚至可能想要设计和实现完全适合您的需求的全新协议。

另一个不可避免的情况是,您必须处理旧的专有协议以确保与旧系统的互操作性。在这种情况下,重要的是我们能够在不牺牲最终应用程序的稳定性和性能的情况下,以多快的速度实现该协议。

3.2 解决方案

Netty项目致力于提供一个异步事件驱动的网络应用程序框架和工具,以快速开发可维护的高性能和高可扩展性协议服务器和客户端。

换句话说,Netty是一个NIO客户端服务器框架,可以快速轻松地开发网络应用程序,例如协议服务器和客户端。它极大地简化和简化了网络编程,例如TCP和UDP套接字服务器的开发。

“快速简便”并不意味着最终的应用程序将遭受可维护性或性能问题的困扰。 Netty经过精心设计,结合了从许多协议(例如FTP,SMTP,HTTP以及各种基于二进制和文本的旧式协议)的实施中获得的经验。结果,Netty成功地找到了一种无需妥协即可轻松实现开发,性能,稳定性和灵活性的方法。

一些用户可能已经发现其他声称具有相同优势的网络应用程序框架,您可能想问一下Netty与他们有何不同。答案是它所基于的哲学。 Netty旨在从第一天开始就API和实施方面为您提供最舒适的体验。这不是有形的东西,但是您会认识到,当您阅读本指南并与Netty一起玩时,这种哲学将使您的生活更加轻松。

3.3 入门

本章将通过简单的示例介绍Netty的核心构造,以使您快速入门。 在本章结束时,您将可以立即在Netty之上编写客户端和服务器。

如果您喜欢自上而下的学习方法,则可能要从第2章,体系结构概述开始,然后回到此处。

3.3.1 入门准备

运行本章中的示例的最低要求只有两个; Netty和JDK 1.6或更高版本的最新版本。 Netty的最新版本可在项目下载页面中找到。 要下载正确版本的JDK,请访问您首选的JDK供应商的网站。

在阅读时,您可能对本章介绍的类有更多疑问。 如果您想进一步了解它们,请参考API参考。 为了方便起见,本文档中的所有类名都链接到在线API参考。 另外,请不要犹豫,与Netty项目社区联系,并让我们知道是否有任何不正确的信息,语法或拼写错误,以及您是否有任何好的想法来帮助改进文档。

JDK 1.5 (1.6 for Netty 4+)

3.3.2 编写Discord服务器

世界上最简单的协议不是“ Hello,World!”。 但是DISCARD。 这是一个协议,它丢弃任何收到的数据而没有任何响应。

要实现DISCARD协议,您唯一需要做的就是忽略所有接收到的数据。 让我们直接从处理程序实现开始,该实现处理Netty生成的I / O事件。

3.3.2.1 pom.xml 中添加依赖

pom.xml 添加如下依赖:

        <dependency>
            <groupId>ioty</groupId>
            <artifactId>netty-all</artifactId> <!-- Use 'netty-all' for 4.0 or above -->
            <version>4.1.47.Final</version>
            <scope>compile</scope>
        </dependency>

完整pom.xml 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache/POM/4.0.0"
         xmlns:xsi="http://www.w3/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache/POM/4.0.0 http://maven.apache/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.xingyun</groupId>
    <artifactId>netty-discard-server</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <!-- 设置当前项目源码使用字符编码为UTF-8 -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- 设置当前项目所需要的JDK版本 Open JDK下载地址:https://jdk.java/ -->
        <java.version>1.8</java.version>
        <!-- 设置当前项目编译所需要的JDK版本 Open JDK下载地址:https://jdk.java/ -->
        <mavenpiler.source>${java.version}</mavenpiler.source>
        <mavenpiler.target>${java.version}</mavenpiler.target>
        <!-- 设置maven编译插件版本,可通过下面网址查看最新的版本-->
        <!-- https://mvnrepository/artifact/org.apache.maven.plugins/maven-compiler-plugin -->
        <mavenpiler.plugin.version>3.5.1</mavenpiler.plugin.version>
        <!-- 项目所使用第三方依赖jar包的版本,建议以后都使用这种方式,方便今后维护和升级 -->
        <lombok.version>1.18.10</lombok.version>
        <spring.boot.version>2.1.6.RELEASE</spring.boot.version>
        <netty.version>4.1.47.Final</netty.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
            <version>${spring.boot.version}</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>ioty</groupId>
            <artifactId>netty-all</artifactId> <!-- Use 'netty-all' for 4.0 or above -->
            <version>${netty.version}</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!--该插件限定Maven打包时所使用的版本,避免出现版本不匹配问题-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${mavenpiler.plugin.version}</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
3.3.2.2 创建 DiscardServerHandler
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * 1.DiscardServerHandler扩展ChannelInboundHandlerAdapter,它是ChannelInboundHandler的实现。
 * ChannelInboundHandler提供了可以覆盖的各种事件处理程序方法。 目前,仅扩展ChannelInboundHandlerAdapter即可,而不是自己实现处理程序接口。
 * 继承关系如下所示: DiscardServerHandler extends ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler
 * @author qing-feng.zhao
 */
public class DiscardServerHandler extends ChannelInboundHandlerAdapter {//1
    /**
     * 2.我们在这里重写channelRead()事件处理程序方法。
     * 每当从客户端接收到新数据时,就会使用接收到的消息来调用此方法。 在此示例中,接收到的消息的类型为ByteBuf。
     * @param ctx
     * @param msg
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {//2
        // Discard the received data silently.
        // 3. 为了实现DISCARD协议,处理程序必须忽略收到的消息。
        //  ByteBuf是一个引用计数的对象,必须通过release()方法显式释放它。
        // 请记住,释放任何传递给处理程序的引用计数对象是处理程序的责任。 通常,channelRead()处理程序方法的实现方式如下:
        //@Override
        //public void channelRead(ChannelHandlerContext ctx, Object msg) {
        //    try {
        //        // Do something with msg
        //    } finally {
        //        ReferenceCountUtil.release(msg);
        //    }
        //}
        ((ByteBuf) msg).release(); // (3)
    }

    /**
     * 4. 当Netty因I/O错误而引发异常时,或者由于处理事件时引发异常而由处理程序实现引发异常时,将使用Throwable调用exceptionCaught()事件处理程序方法。
     *    在大多数情况下,应该记录捕获到的异常,并在此处关闭其关联的通道,尽管此方法的实现可能会有所不同,具体取决于您要处理特殊情况时要采取的措施。
     *    例如,您可能想在关闭连接之前发送带有错误代码的响应消息。
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}
3.3.2.3 编写main方法

到目前为止,一切都很好。 我们已经实现了DISCARD服务器的前半部分。 现在剩下的是编写main()方法,该方法使用DiscardServerHandler启动服务器。

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;

/**
 * @author qing-feng.zhao
 */
public class DiscardServer {
    /**
     * 端口
     */
    private int port;

    /**
     * 构造方法
     * @param port
     */
    public DiscardServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws Exception {
        //默认端口8080
        int port = 8080;
        //如果传入了参数
        if (args.length > 0) {
            //使用传入的参数端口
            port = Integer.parseInt(args[0]);
        }
        //启动服务器
        new DiscardServer(port).run();
    }

    /**
     * @throws Exception
     */
    public void run() throws Exception {
        // 1. NioEventLoopGroup是处理I/O操作的多线程事件循环。
        // Netty为不同类型的传输提供了各种EventLoopGroup实现。
        // 在此示例中,我们正在实现服务器端应用程序,因此将使用两个NioEventLoopGroup。

        //第一个通常称为“boss”,接受传入的连接
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        //第二个通常称为“worker”,一旦boss接受连接并将注册的连接注册给worker,便处理已接受连接的流量。
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        //使用多少个线程以及如何将它们映射到创建的通道取决于EventLoopGroup实现,甚至可以通过构造函数进行配置。
        try {

            //ServerBootstrap是设置服务器的帮助程序类。您可以直接使用Channel设置服务器。
            // 但是,请注意,这是一个繁琐的过程,在大多数情况下您无需这样做。
            ServerBootstrap b = new ServerBootstrap(); // (2)

            b.group(bossGroup, workerGroup)
                    //在这里,我们指定使用NioServerSocketChannel类,该类用于实例化新的Channel来接受传入的连接。
                    .channel(NioServerSocketChannel.class) // (3)
                    // 此处指定的处理程序将始终由新接受的Channel评估。 ChannelInitializer是一个特殊的处理程序,旨在帮助用户配置新的Channel。
                    // 您很可能希望通过添加一些处理程序(例如DiscardServerHandler)来实现新的Channel的ChannelPipeline,以实现您的网络应用程序。
                    //随着应用程序变得复杂,您可能会向管道添加更多处理程序,并最终将此匿名类提取到顶级类中。
                    .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new DiscardServerHandler());
                        }
                    })
                    //您还可以设置特定于Channel实现的参数。我们正在编写一个TCP / IP服务器,因此我们可以设置套接字选项,例如tcpNoDelay和keepAlive。
                    // 请参考ChannelOption的apidocs和特定的ChannelConfig实现,以获取有关受支持的ChannelOptions的概述。
                    .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                    //您是否注意到option()和childOption()
                    //   option()用于接受传入连接的NioServerSocketChannel。
                    //   childOption()用于父级ServerChannel接受的通道,在这种情况下为NioServerSocketChannel。
                    .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // 剩下的就是绑定到端口并启动服务器。
            // 在这里,我们绑定到计算机中所有NIC(网络接口卡)的端口8080。
            // 现在,您可以根据需要多次调用bind()方法(使用不同的绑定地址)。
            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
        //恭喜你!您刚刚在Netty上完成了第一台服务器。
    }
}

使用方法:

  • 运行DiscardServer.main() 方法

  • 另外新开一个窗口输入连接命令:

telnet 127.0.0.1 8080

演示如下所示:

源码放这里了,需要的拿走。

本文标签: 简易入门聊天室教程Netty