【Java开发日记】简单说一说使用 Netty 进行 Socket 编程

目录

什么是 Netty

对比Netty和传统的Socket

传统Socket编程服务端

传统Socket编程客户端

Netty环境搭建

先创建出来一个项目

Netty服务端程序

Netty客户端程序

Channel

Channel分类

为什么选择Netty


什么是 Netty

Netty是由JBOSS提供的一个java开源框架,现为Github上的独立项目。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

也就是说,Netty 是一个基于NIO的客户、服务器端的编程框架,使用Netty 可以确保快速和简单的开发出一个网络应用,例如实现了某种协议的客户、服务端应用。Netty相当于简化和流线化了网络应用的编程开发过程,例如:基于TCP和UDP的socket服务开发。
上面是来自于百度百科给出的解释,能清晰的看到,Netty是一个基于NIO的模型,使用Netty的地方很多就是socket服务开发,而关于NIO,相信大家肯定不陌生。 

对比Netty和传统的Socket

既然要说Netty,那么肯定要对Netty还有Socket不同的代码进行一个分析,分析的透彻了,才能真的选择使用Netty,而不再进行Socket的开发了,相信到时候,大家肯定会做出最正确的选择。 

传统Socket编程服务端
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/*** @ClassName SocketDemo* @Date 2021/4/19 10:33* @Description SocketDemo*/
public class SocketServerDemo {public static void main(String[] args) {ServerSocket server=null;try {server=new ServerSocket(18080);System.out.println("时间服务已经启动--端口号为:18080...");while (true){Socket client = server.accept();//每次接收到一个新的客户端连接,启动一个新的线程来处理new Thread(new TimeServerHandler(client)).start();}} catch (IOException e) {e.printStackTrace();}finally {try {server.close();} catch (IOException e) {e.printStackTrace();}}}
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Calendar;
/*** @ClassName TimeServerHandler* @Date 2021/4/19 10:35* @Description TimeServerHandler*/
public class TimeServerHandler implements Runnable {private Socket clientProxxy;public TimeServerHandler(Socket clientProxxy) {this.clientProxxy = clientProxxy;}@Overridepublic void run() {BufferedReader reader = null;PrintWriter writer = null;try {reader = new BufferedReader(new InputStreamReader(clientProxxy.getInputStream()));writer =new PrintWriter(clientProxxy.getOutputStream()) ;while (true) {//因为一个client可以发送多次请求,这里的每一次循环,相当于接收处理一次请求String request = reader.readLine();if (!"GET CURRENT TIME".equals(request)) {writer.println("BAD_REQUEST");} else {writer.println(Calendar.getInstance().getTime().toLocaleString());}writer.flush();}} catch (Exception e) {throw new RuntimeException(e);} finally {try {writer.close();reader.close();clientProxxy.close();} catch (IOException e) {e.printStackTrace();}}}
}

传统Socket编程客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/*** @ClassName SocketClientDemo* @Date 2021/4/19 10:42* @Description SocketClientDemo*/
public class SocketClientDemo {public static void main(String[] args)  {BufferedReader reader = null;PrintWriter writer = null;Socket client=null;try {client=new Socket("127.0.0.1",18080);writer = new PrintWriter(client.getOutputStream());reader = new BufferedReader(new InputStreamReader(client.getInputStream()));while (true){//每隔5秒发送一次请求writer.println("GET CURRENT TIME");writer.flush();String response = reader.readLine();System.out.println("Current Time:"+response);Thread.sleep(5000);}} catch (Exception e) {e.printStackTrace();} finally {try {writer.close();reader.close();client.close();} catch (IOException e) {e.printStackTrace();}}}
}

来执行一下才能知道效果,
首先运行服务端:

TimeServer Started on 18080...

接着启动客户端

Current Time:2021-4-19 10:48:21
Current Time:2021-4-19 10:48:26
Current Time:2021-4-19 10:48:31
Current Time:2021-4-19 10:48:36
Current Time:2021-4-19 10:48:41
Current Time:2021-4-19 10:48:46
Current Time:2021-4-19 10:48:51

大家看一下,这是不是就是相当于一个Socket的客户端和服务端之间进行通信的过程,在client端可以发送请求指令”GET CURRENT TIME”给server端,每隔5秒钟发送一次,每次server端都返回当前时间。
而这也是传统的BIO的做法,每一个client都需要去对应一个线程去进行处理,client越多,那么要开启的线程也就会越多,也就是说,如果采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,当接收到客户端的连接请求后,会为每一个客户端请求创建新的线程进行请求的处理,处理完成后通过输出流返回信息给客户端,响应完成后销毁线程。
模型图如下


这时候就有大佬说,不会用线程池么?使用线程池的话,它实际上并没有解决任何实际性的问题,他实际上就是对BIO做了一个优化,属于伪异步IO通信。
伪异步IO通信模型图


异步IO通信确实能缓解一部分的压力,但是这种模型也是有缺陷的,当有大量客户端请求的时候,随着并发访问量的增长,伪异步IO就会造成线程池阻塞。
这时候就取决于是想选择,系统发生线程堆栈溢出、创建新线程失败等问题呢,还是选择大量客户端请求,造成线程池阻塞。
都说,技术是为了解决问题而出现的,那么接下来就有了解决这个问题的技术出现了,Netty,来看看Netty吧。 

Netty环境搭建

在这里使用的依旧是Springboot来整合Netty的环境,然后在后续过程中,使用Netty实现服务端程序和客户端程序,虽然Netty并没有实现传说中的AIO,但是已经算是吧这个NIO的模型,实现到了极致了。 

先创建出来一个项目

 加入Netty的pom的依赖

<!--Netty-->
<dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.31.Final</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.16.22</version>
</dependency>
<!-- logger -->
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.25</version>
</dependency>
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-core</artifactId><version>1.2.3</version>
</dependency>
<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.2.3</version>
</dependency>

Netty服务端程序
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 io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/*** @ClassName NettyServerDemo* @Date 2021/4/19 11:11* @Description NettyServerDemo*/
public class NettyServerDemo {private int port=18081;public void run() throws Exception {EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup();try {ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LineBasedFrameDecoder(1024));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new TimeServerHandler());}});ChannelFuture f = b.bind(port).sync(); System.out.println("TimeServer Started on 18081...");f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();bossGroup.shutdownGracefully();}}public static void main(String[] args) throws Exception {new NettyServerDemo().run();}
}
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import java.util.Date;
/*** @ClassName TimeServerHandler* @Date 2021/4/19 11:19* @Description TimeServerHandler*/
public class TimeServerHandler extends ChannelInboundHandlerAdapter {@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String request = (String) msg;String response = null;if ("QUERY TIME ORDER".equals(request)) {response = new Date(System.currentTimeMillis()).toString();} else {response = "BAD REQUEST";}response = response + System.getProperty("line.separator");ByteBuf resp = Unpooled.copiedBuffer(response.getBytes());ctx.writeAndFlush(resp);}
}

Netty客户端程序
import io.netty.bootstrap.Bootstrap;
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.NioSocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
/*** @ClassName NettyClientDemo* @Date 2021/4/19 11:21* @Description NettyClientDemo*/
public class NettyClientDemo {public static void main(String[] args) throws Exception {String host = "localhost";int port = 18081;EventLoopGroup workerGroup = new NioEventLoopGroup();try {Bootstrap b = new Bootstrap();b.group(workerGroup);b.channel(NioSocketChannel.class);b.handler(new ChannelInitializer<SocketChannel>() {@Overridepublic void initChannel(SocketChannel ch) throws Exception {ch.pipeline().addLast(new LineBasedFrameDecoder(1024));ch.pipeline().addLast(new StringDecoder());ch.pipeline().addLast(new TimeClientHandler());}});// 开启客户端.ChannelFuture f = b.connect(host, port).sync();// 等到连接关闭.f.channel().closeFuture().sync();} finally {workerGroup.shutdownGracefully();}}
}
    import io.netty.buffer.ByteBuf;
    import io.netty.buffer.Unpooled;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    /*** @ClassName TimeClientHandler* @Date 2021/4/19 11:22* @Description TimeClientHandler*/
    public class TimeClientHandler extends ChannelInboundHandlerAdapter {private byte[] req=("QUERY TIME ORDER" + System.getProperty("line.separator")).getBytes();@Overridepublic void channelActive(ChannelHandlerContext ctx) {//1ByteBuf message = Unpooled.buffer(req.length);message.writeBytes(req);ctx.writeAndFlush(message);}@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {String body = (String) msg;System.out.println("Now is:" + body);}
    }

    首先启动服务端,控制台输出:

    TimeServer Started on 18081...

    接着启动客户端,控制要输出:

    Now is:Mon Apr 19 11:34:21 CST 2021

    既然代码写了,那是不是就得来分析一下这个Netty在中间都干了什么东西,他的类是什么样子的,都有哪些方法。
    大家先从代码的源码上开始看起,因为在代码中分别使用到了好几个类,而这些类的父类,或者是接口定义者追根到底,也就是这个样子的,从IDEA中打开他的类图可以清晰的看到。

    而在源码中,最重要的就是这个Channel,接下来就来分析一波吧。 

    Channel

    All I/O operations are asynchronous.一句话点出核心所有的IO操作都是异步的,这意味着任何I/O调用都将立即返回,但不保证请求的I/O操作已完成。这是在源码的注释上面给出的解释。 

    Channel分类
    • 服务端: NioServerSocketChannel
    • 客户端: NioSocketChannel

    看到这个,大家肯定也都不陌生,因为Channel即可以在JDK的Socket中充当管道出现,同时,也在Netty的服务端和客户端进行IO数据交互,充当一个媒介的存在,那么他的区别在哪?
    Netty对Jdk原生的ServerSocketChannel进行了封装和增强封装成了NioXXXChannel, 相对于原生的JdkChannel,Netty的Channel增加了如下的组件。

    • id 标识唯一身份信息
    • 可能存在的parent Channel
    • 管道 pepiline
    • 用于数据读写的unsafe内部类
    • 关联上相伴终生的NioEventLoop

    在官网可以了解这个这个类的API有更多的信息io.netty.channel
    而关于Channel,其实换成大家容易理解的话的话,那就是由它负责同对端进行网络通信、注册和数据操作等功能

    A Channel can have a parent depending on how it was created. For instance, a SocketChannel, that was accepted by ServerSocketChannel, will return the ServerSocketChannel as its parent on parent().
    The semantics of the hierarchical structure depends on the transport implementation where the Channel belongs to. For example, you could write a new Channel implementation that creates the sub-channels that share one socket connection, as BEEP and SSH do.

    一个Channel可以有一个父Channel,这取决于它是如何创建的。例如,被ServerSocketChannel接受的SocketChannel将返回ServerSocketChannel作为其parent()上的父对象。层次结构的语义取决于通道所属的传输实现。
    Channel的抽象类AbstractChannel中有一个受保护的构造方法,而AbstractChannel内部有一个pipeline属性,Netty在对Channel进行初始化的时候将该属性初始化为DefaultChannelPipeline的实例。 

    为什么选择Netty

    同步阻塞I/O(BIO)伪异步I/O非阻塞I/O (NIO)异步I/O (AIO)
    I/O类型(同步)同步I/O同步I/O同步I/O (I/O多路复用)异步I/O
    API使用难度简单简单非常复杂复杂
    调试难度简单简单复杂复杂
    可靠性非常差
    吞吐量

    其实在上面的图中,已经能看出来了,不同的I/O模型,效率,使用难度,吞吐量都是非常重要的,所以选择的时候,肯定要慎重选择,而为什么不使用Java原生的呢?
    实际上很简单,1.复杂,2.不好用
    对于Java的NIO的类库和API繁杂使用麻烦,需要熟练掌握Selectol,ServerSocketChannel,SocketChannel,ByteBuffer 等
    JDK NIO的BUG,比如epoll bug,这个BUG会在linux上导致cpu 100%,使得nio server/client不可用,而且在1.7中都没有解决完这个bug,只不过发生频率比较低。
    而Netty是一个高性能、异步事件驱动的NIO框架,它提供了对TCP、UDP和文件传输的支持,作为一个异步NIO框架,Netty的所有IO操作都是异步非阻塞的,通过Future-Listener机制,用户可以方便的主动获取或者通过通知机制获得IO操作结果。

    如果小假的内容对你有帮助,请点赞评论收藏。创作不易,大家的支持就是我坚持下去的动力!

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

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

    相关文章

    目标检测任务的评估指标mAP50和mAP50-95

    mAP50 和 mAP50-95 是目标检测任务中常用的评估指标&#xff0c;用于衡量模型在不同 交并比&#xff08;IoU&#xff09;阈值 下的平均精度&#xff08;Average Precision, AP&#xff09;。它们的区别主要体现在 IoU 阈值范围 上。 ✅ 1. mAP50&#xff08;mean Average Prec…

    COHERENT XPRV23光电接收器控制软件

    COHERENT XPRV23光电接收器控制软件

    执行应用共享内存空间 同步QT进行快速捕获数据流

    引言&#xff1a;本文章针对驱动的应用app&#xff0c;例如sensor data内容的获取&#xff0c;显示到QT的一种办法&#xff0c;共享内存。举例子&#xff0c;这是一个常见需求&#xff0c;比如摄像头采集进程与 GUI 显示进程分离&#xff0c;通过共享内存传输图像&#xff0c;避…

    opencl的简单介绍以及c++实例

    &#x1f9e9; 一、什么是 OpenCL&#xff1f; OpenCL&#xff08;Open Computing Language&#xff09; 是一个用于异构计算的开放标准&#xff0c;由 Khronos Group 提出和维护。它允许你在各种计算设备上&#xff08;如 CPU、GPU、DSP、FPGA&#xff09;并行运行代码&#…

    ThingsCloud事物云平台搭建-微信小程序

    ThingsCloud云平台与微信小程序设计 本文主要是介绍ThingsCloud云平台的搭建及微信小程序与app的使用。 当前文章是作为一个通用案例,介绍如何快速使用 ThingsCloud云平台 以及 利用 ThingsCloud云平台平台的框架快速设计手机APP和微信小程序。 可以快速让硬件接入,实现硬件…

    2024 一带一路暨金砖国家职业技能大赛(金砖国家未来技能和技术挑战赛)

    2024 一带一路暨金砖国家职业技能大赛&#xff08;金砖国家未来技能和技术挑战赛任务书&#xff09; 1 参加比赛的形式&#xff1a;2 项目阶段简介&#xff1a;3 项目阶段和所需时间&#xff1a;4 第一阶段&#xff1a;职业素养与理论技能4.1 项目 1.职业素养4.2 项目 2.法律法…

    2025-06-13【api】阿里百炼api调用方法

    通过调用各种大模型可以完成对文生文&#xff0c;文生图&#xff0c;图片理解&#xff0c;文生视频&#xff0c;音频识别&#xff0c;文转音频等需求。 #方法一 import os from openai import OpenAI# 初始化客户端 client OpenAI(api_keyos.getenv("DASHSCOPE_API_KEY&…

    软件工程的软件生命周期通常分为以下主要阶段

    软件工程的软件生命周期通常分为以下主要阶段&#xff1a; 可行性分析 &#xff1a;评估项目的技术、经济、操作和法律可行性&#xff0c;确定项目是否值得开发。需求分析 &#xff1a;明确用户需求&#xff0c;定义软件功能和非功能需求&#xff0c;形成需求规格说明书。系统…

    Spring依赖注入的四种方式(面)

    目录 1. 构造器注入 2. 字段注入 3. Setter注入 4. 方法注入 最佳实践建议 1. 构造器注入 Service public class UserService {private final UserRepository userRepository;Autowired // Spring 4.3 可以省略public UserService(UserRepository userRepository) {this.…

    通信网络编程2.0——JAVA

    一、传统阻塞式 I/O 模型 实现简易多人聊天系统&#xff1a;服务端与客户端 服务端 public class ChatServer {int port 6666;// 定义服务器端口号为 6666ServerSocket ss;// 定义一个 ServerSocket 对象用于监听客户端连接//List<Socket> clientSockets new ArrayL…

    (转)什么是DockerCompose?它有什么作用?

    一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用&#xff0c;而无需手动一个个创建和运行容器。 Compose文件是一个文本文件&#xff0c;通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …

    Python打卡第51天

    浙大疏锦行 作业&#xff1a; day43的时候我们安排大家对自己找的数据集用简单cnn训练&#xff0c;现在可以尝试下借助这几天的知识来实现精度的进一步提高 import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader from tor…

    Notepad++ 官方下载

    https://notepad-plus-plus.org/downloads/ 下载官网 1、https://github.com/notepad-plus-plus/notepad-plus-plus/releases 2、https://notepad-plus-plus.org/news/v881-we-are-with-ukraine/

    运维之十个问题--2

    目录 1. 如果有ip恶意刷流量怎么办 2. 标准端口范围 3.内存16G&#xff0c;交换分区多大 4.请简述非对称加密算法&#xff0c;ping命令通过什么协议实现&#xff0c;icmp是什么协议 5.客户访问网站速度慢原因 6. 进程和线程的区别 7.zabbix监控是你搭建的吗&#xff0c;平…

    vue前端面试题——记录一次面试当中遇到的题(1)

    1.v-if和v-show的区别 v-if和v-show都是Vue中用于条件渲染的指令&#xff0c;但它们的实现机制和适用场景有所不同&#xff1a; v-if是真正的条件渲染&#xff0c;在条件切换时会销毁和重建DOM元素&#xff0c;适合运行时条件变化不频繁的场景&#xff1b; v-show只是通过CS…

    【QT面试题】(三)

    文章目录 Qt信号槽的优点及缺点Qt中的文件流和数据流区别&#xff1f;Qt中show和exec区别QT多线程使用的方法 (4种)QString与基本数据类型如何转换&#xff1f;QT保证多线程安全事件与信号的区别connect函数的连接方式&#xff1f;信号与槽的多种用法Qt的事件过滤器有哪些同步和…

    Vscode下Go语言环境配置

    前言 本文介绍了vscode下Go语言开发环境的快速配置&#xff0c;为新手小白快速上手Go语言提供帮助。 1.下载官方Vscode 这步比较基础&#xff0c;已经安装好的同学可以直接快进到第二步 官方安装包地址&#xff1a;https://code.visualstudio.com/ 双击一直点击下一步即可,记…

    HTML 文本省略号

    目录 HTML 文本省略号超行省略号如何实现1. 单行文本溢出显示省略号2. 多行文本溢出显示省略号方法一&#xff1a;使用 -webkit-line-clamp&#xff08;推荐&#xff09;方法二&#xff1a;使用伪元素&#xff08;兼容性好&#xff09;方法三&#xff1a;使用 JavaScript 动态监…

    Spring Boot 实现流式响应(兼容 2.7.x)

    在实际开发中&#xff0c;我们可能会遇到一些流式数据处理的场景&#xff0c;比如接收来自上游接口的 Server-Sent Events&#xff08;SSE&#xff09; 或 流式 JSON 内容&#xff0c;并将其原样中转给前端页面或客户端。这种情况下&#xff0c;传统的 RestTemplate 缓存机制会…

    ffmpeg 新版本转码设置帧率上限

    ffmpeg 新版本转码设置帧率上限 ffmpeg 在老版本比如 4.3的时候&#xff0c;转码设置帧率上限是通过vsync控制 # 设置动态控制最大帧率60 "-vsync 2 -r 60" 新版本这个参数没办法动态判断控制帧率了 替换为使用filter中的fps进行设置 # 设置动态帧率最大60帧 -…