【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
    繁體地址,請注明出處:http://hk.pswp.cn/pingmian/84551.shtml
    英文地址,請注明出處:http://en.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幀 -…