Netty简介及简单客户端/服务端示例代码

news2025/1/11 0:54:20

什么是Netty?

        Netty是一个NIO客户机-服务器框架,它支持快速而容易地开发网络应用程序,如协议服务器和客户机。它大大简化和简化了网络编程,如TCP和UDP套接字服务器。

        “快速简单”并不意味着生成的应用程序将遭受可维护性或性能问题的困扰。Netty经过了精心的设计,其经验来自于FTP、SMTP、HTTP以及各种基于二进制和文本的遗留协议的实现。因此,Netty成功地找到了一种不妥协地实现易开发性、性能、稳定性和灵活性的方法。

Netty 的应用场景

互联网行业

1)互联网行业: 在分布式系统中, 各个节点之间需要远程服务调用, 高性能的 RPC 框架必不可少, Netty 作为异步高性能的通信框架, 往往作为基础通信组件被这些 RPC 框架使用。

2)典型的应用有: 阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信, Dubbo 协议默认使用 Netty 作为基础通信组件, 用于实现各进程节点之间的内部通信。

游戏行业

1)无论是手游服务端还是大型的网络游戏, Java 语言得到了越来越广泛的应用。

2)Netty 作为高性能的基础通信组件, 提供了 TCP/UDP 和 HTTP 协议栈, 方便定制和开发私有协议栈, 账号登录服务器。

3)地图服务器之间可以方便的通过 Netty 进行高性能的通信。

大数据领域

1) 经典的 Hadoop 高性能通信和序列化组件 Avro 的 RPC 框架, 默认采用 Netty 进行跨界点通信。

2) 它的 Netty Service 基于 Netty 框架二次封装实现。

代码示例

maven依赖

<dependencies>
    <!--netty依赖-->
    <dependency>
        <groupId>io.netty</groupId>
        <artifactId>netty-all</artifactId>
        <version>4.1.90.Final</version>
    </dependency>
</dependencies>

服务端

Constant类

public class Constant {
    // 常量,Server需要的一些参数信息,需要根据实际情况进行自定义修改
    static final int PORT = 8888;   // 可以自定义端口号
    static final String CLIENT_PREFIX = "Server received:";   // Server显示接收到client发送的信息的前缀
    static final String SERVER_PREFIX = "Server send:";   // Server将接收到client发送的信息返回给client的前缀
}

NettyServer类

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;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/*
 * Netty的Server
 * 功能:接收client发送的消息并在消息的前缀加上一些内容后返回给客户端
 * */
public class NettyServer {
    private final int port;

    public static void main(String[] args) {
        int port = Constant.PORT;
        new NettyServer(port).start();
    }

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

    public void start() {
        // 创建两个事件循环组
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            // 创建服务器启动对象
            ServerBootstrap bootstrap = new ServerBootstrap();

            // 设置两个处理器,用于接收和返回信息
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) // 使用NioServerSocketChannel作为服务器的通道实现
                    .option(ChannelOption.SO_BACKLOG, 128) // 设置线程队列等待连接的个数
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 创建通道初始化对象,设置处理器
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            // 给处理器添加对应的ChannelHandler
                            ch.pipeline().addLast(new StringDecoder()); // 添加字符串解码器
                            ch.pipeline().addLast(new StringEncoder()); // 添加字符串编码器
                            ch.pipeline().addLast(new ServerHandler()); // 添加服务处理器
                        }
                    });

            System.out.println("服务端启动,等待客户端连接...");
            // 绑定端口并启动服务
            ChannelFuture future = bootstrap.bind(8888).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭两个事件循环组,释放资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }

    private class ServerHandler extends ChannelInboundHandlerAdapter {
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) {
            System.out.println(Constant.CLIENT_PREFIX + msg); // 打印收到的客户端信息
            ctx.writeAndFlush(Constant.SERVER_PREFIX + msg); // 将收到的信息加上前缀返回给客户端
        }
    }

}

客户端

Constant类

public class Constant {
    // 常量,Client需要的一些参数信息,需要根据实际情况进行自定义修改
    static final String HOST = "127.0.0.1";   // 要连接的服务器地址
    static final int PORT = 8888;   // 要连接的服务器端口号
    static final String CLIENT_PREFIX = "Client received:";   // Client显示接收到Server发送的信息的前缀
}

NettyClient类

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 io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

import java.util.Scanner;

/*
 * Netty的Client
 * 功能:向server发送控制台输入的消息,并接收server发回的消息并显示
 * */
public class NettyClient {
    private final String host;
    private final int port;
    private Channel channel;

    public static void main(String[] args) throws Exception {
        String host = Constant.HOST;
        int port = Constant.PORT;

        new NettyClient(host, port).start();
    }

    public NettyClient(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public void start() throws Exception { // 定义一个名为start的方法,这个方法抛出Exception异常
        EventLoopGroup group = new NioEventLoopGroup(); // 创建一个NioEventLoopGroup对象,它负责处理I/O操作的多线程事件循环
        try { // 开始try-catch块,用于捕获可能的异常
            Bootstrap bootstrap = new Bootstrap(); // 创建一个Bootstrap对象,它是Netty应用程序的入口点
            bootstrap.group(group) // 设置EventLoopGroup,用于处理I/O操作
                    .channel(NioSocketChannel.class) // 指定用于通信的Channel类型
                    .handler(new ChannelInitializer<SocketChannel>() { // 添加一个ChannelInitializer,用于初始化新连接的Channel
                        @Override // 覆盖ChannelInitializer中的初始化方法
                        protected void initChannel(SocketChannel ch) throws Exception { // 初始化Channel
                            ch.pipeline().addLast(new StringDecoder(CharsetUtil.UTF_8)); // 添加一个StringDecoder,用于将字节流解码为字符串,使用UTF-8编码
                            ch.pipeline().addLast(new StringEncoder(CharsetUtil.UTF_8)); // 添加一个StringEncoder,用于将字符串编码为字节流,使用UTF-8编码
                            ch.pipeline().addLast(new ClientHandler()); // 添加一个ClientHandler,用于处理业务逻辑
                        }
                    });

            channel = bootstrap.connect(host, port).sync().channel(); // 使用Bootstrap连接服务器,同步连接并获取到Channel

            Scanner scanner = new Scanner(System.in); // 创建一个Scanner对象,用于从控制台接收用户输入
            while (true) { // 无限循环,直到用户输入exit命令
                System.out.print("请输入信息(exit退出):"); // 向控制台输出提示信息
                String message = scanner.nextLine(); // 从控制台读取用户输入的行,并存储在message变量中
                channel.writeAndFlush(message + "\n"); // 将用户输入的信息通过Channel发送到服务器,并在信息末尾添加换行符以保证服务器能正确接收信息
                if (message.equals("exit")) { // 如果用户输入的信息是exit,则退出循环
                    break;
                }
                Thread.sleep(1000); // 等待1秒,等待客户端接收并打印服务器发送的消息
            }
            scanner.close(); // 关闭Scanner对象,释放资源
            channel.close(); // 关闭Channel对象,释放资源
        } finally { // finally块用于无论try块中的代码是否发生异常都会执行的操作
            group.shutdownGracefully(); // 优雅地关闭EventLoopGroup,释放资源
        }
    }

    private class ClientHandler extends SimpleChannelInboundHandler<String> {
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(Constant.CLIENT_PREFIX + msg);  // 将服务器的响应打印到控制台
        }
    }
}

运行截图:

先启动服务端,再启动客户端,在客户端控制台输入信息后点击回车,可以在服务器端看到服务器接收到客户端发送的信息;可以在客户端看到服务器返回的信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035709.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pycharm 让控制台里的链接可以点击

前言 如果细心就会发现pychram控制台里一些链接是可以点击的,另外一些不行,那么如果让输出的链接可以点击如何做呢? 解决 输出的i链接会在控制台里可以点击,并且点击会在本地直接打开 如果打印的是网址则可以直接点击 print(file:///{}.format(i))print(https://www.baid…

[架构之路-220]:与机器打交道VS与人打交道,计算机系统VS人体系统,计算机网络VS人类社会:架构、通信、语言、网络、智能、情感、生命

目录 前言&#xff1a; 一、计算机系统架构 VS 人体系统架构 1.1 相似之处 1.2 不同之处 1.3 人的心理与计算机软件 1.4 计算机系统层次 VS 人的层次模型 二、计算机通信 VS 人与人交流 2.1 计算机通信communication 2.2 人与人的交流communication 2.3 智商 VS 情商…

mybatis拦截器执行原理

title: “mybatis拦截器执行过程” createTime: 2021-12-08T12:19:5708:00 updateTime: 2021-12-08T12:19:5708:00 draft: false author: “ggball” tags: [“mybatis”] categories: [“java”] description: “mybatis拦截器执行过程” m[toc] mybatis的拦截器本人平时也很…

【数据结构】链表--单链表

目录 一 概念及结构 二 单链表的实现 1 包含接口(SList.h) 2 打印和创造节点&#xff08;扩容&#xff09;&#xff08;SList.c) 3 尾插&#xff08;SList.c) 4 头插&#xff08;SList.c) 5 尾删&#xff08;SList.c) 6 头删&#xff08;SList.c) 7 在pos前插入x&…

大数据从入门到精通(超详细版)之Hive的案例实战,ETL数据清洗!!!

前言 嗨&#xff0c;各位小伙伴&#xff0c;恭喜大家学习到这里&#xff0c;不知道关于大数据前面的知识遗忘程度怎么样了&#xff0c;又或者是对大数据后面的知识是否感兴趣&#xff0c;本文是《大数据从入门到精通&#xff08;超详细版&#xff09;》的一部分&#xff0c;小…

安全生产知识竞赛活动小程序界面分享

安全生产知识竞赛活动小程序界面分享

SCR截面速度、氨氮比等标准及相对标准偏差计算

SCR截面速度、氨氮比等标准及相对标准偏差计算 # -*- coding: utf-8 -*- """ 联系QQ:3123575367&#xff0c;专业SCR脱硝仿真。 Created on Wed Sep 20 20:40:30 2023 该程序用来处理fluent通过xyplot导出的数据&#xff0c;可计算标准偏差SD、相对标准偏差RSD…

linux 安装 wordpress

文章目录 linux 安装 wordpress1. wordpress 简介2. wordpress功能和特点3. 部署要求4. 环境搭建4.1 部署 nginx4.1.1 新增配置文件 4.2 部署 PHP74.2.1 查看当前版本4.2.2 YUM 安装 PHP74.2.3 查看 PHP 版本4.2.4 启动PHP-FPM4.2.5 修改配置文件4.2.6 重启服务 4.3 部署 mysql…

Spring事务2+银行转账拓展

前言 Transactional()内可以写的属性 一、Dao层的更新 public interface LogDao {//#{}符号取的是接口方法中的形参 now()方法直接调用的内置函数Insert("insert into log (info,createDate) values(#{info},now())")void log(String info); }二、LogService层…

【Element】通知 Notification

ElementUI 弹出通知 created() {const h this.$createElementconst that thisthis.$notify({onClose: function () {that.do()},type: warning,duration: 5000, // 5秒后隐藏offset: 0, // 距离顶部dangerouslyUseHTMLString: false, showClose: false,customClass: notify-…

索引(含B树、B+树)

1、索引&#xff08;index&#xff09; 索引是在数据库表的字段上添加的&#xff0c;是为了提高查询效率存在的一种机制。 一张表的一个字段可以添加一个索引&#xff0c;当然&#xff0c;多个字段联合起来也可以添加索引。 索引相当于一本书的目录&#xff0c;是为了缩小扫描…

Nginx 防止跨站脚本 Cross-Site Scripting (XSS)

1、修改 nginx 配置 在 nginx.conf 配置文件中&#xff0c;增加如下配置内容&#xff1a; add_header X-XSS-Protection "1; modeblock";X-XSS-Protection 的字段有三个可选配置值&#xff0c;说明如下&#xff1a; 0&#xff1a; 表示关闭浏览器的XSS防护机制&…

计算机专业毕业设计项目推荐08-英语在线点读平台(SpringBoot+Vue+MongoDB)

英语在线点读平台&#xff08;SpringBootVueMongoDB&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设…

华为认证HCIP知识点

文章目录 前言考试内容数据通信领域各场景通用核心知识OSPFIS-ISBGPIGMPICMP 数据通信领域路由交换高阶知识大数据技术Hadoop 总结 前言 本博客仅做学习笔记&#xff0c;如有侵权&#xff0c;联系后即刻更改 科普&#xff1a; 考试内容 参考网址 HCIP认证主要定位于中小型网…

Spring面试题7:面试官:Spring是如何进行异常处理的呢?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:Spring是如何进行异常处理的呢? Spring通过异常处理机制来处理应用程序中的异常。它提供了多种方式来处理异常,包括以下几种: 声明式事务管理:…

实战案例:场景测试之ATM机取款业务测试

本期&#xff0c;我们通过经典案例——ATM机的操作&#xff0c;来为大家详细说说如何撰写对应的测试用例。 案例 在我们日常生活中&#xff0c;ATM机是个大家都非常熟悉的事物。银行为例提高工作效率&#xff0c;方便客户随时办理基础的储蓄和提现业务&#xff0c;于是&#…

Qt5开发及实例V2.0-第二十章-Qt.QML动画特效

Qt5开发及实例V2.0-第二十章-Qt.QML动画特效 第20章 QML动画特效20.1 QML动画元素20.1.1 PropertyAnimation元素20.1.2 其他动画元素20.1.3 Animator元素 20.2 动画流UI界面20.2.1 状态和切换 20.3 图像特效20.3.1 3D旋转20.3.2 色彩处理 20.4 Qt 5.5&#xff08;Qt Quick Extr…

web前端float布局、flex布局

1、float布局 <!DOCTYPE html> <html> <head><title>Login Page</title><style>body {font-family: Arial, sans-serif;background-color: #f3f3f3;}.container {max-width: 400px;margin: 0 auto;padding: 40px;background-color: #fff;…

变压器(电抗器) 红外测温作业指导书

1 范围 本标准化作业指导书规定了变压器(电抗器)红外测温(一般检测)工作的准备工作、测温流程图、 现场操作方法、测温周期和标准、测温记录管理等要求。 本标准化作业指导书适用于指导变压器(电抗器)红外测温的一般性检测工作。 2 规范性引用文件 下列文件对于本文件的应用…