Netty網(wǎng)絡(luò)編程實(shí)戰(zhàn)之搭建Netty服務(wù)器
一、Netty是什么
Netty是JBOSS開(kāi)源的一款NIO網(wǎng)絡(luò)編程框架,可用于快速開(kāi)發(fā)網(wǎng)絡(luò)的應(yīng)用。Netty是一個(gè)異步的、基于事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用于快速開(kāi)發(fā)高性能的服務(wù)端和客戶(hù)端??梢詷O大的簡(jiǎn)化基于TCP、UDP等協(xié)議的網(wǎng)絡(luò)服務(wù)。并且Netty對(duì)于各種傳輸類(lèi)型(阻塞或非阻塞式的socket)、通信方式(HTTP或websocket)都提供了統(tǒng)一的API接口,提供了靈活的可擴(kuò)展性,高度可自定義的線程模型(多線程、線程池等),支持使用無(wú)連接的數(shù)據(jù)報(bào)UDP進(jìn)行通信,具有高吞吐量、低延遲、資源消耗低、最低限度的內(nèi)存復(fù)制等特性。除了優(yōu)越的性能外,Netty還支持SSL/TLS和StartTLS等加密傳輸協(xié)議,保證了數(shù)據(jù)傳輸?shù)陌踩浴?/p>
在實(shí)際使用時(shí),Netty可以作為Socket編程的中間件;也可以和Protobuf技術(shù)結(jié)合使用,實(shí)現(xiàn)一個(gè)RPC框架,實(shí)現(xiàn)遠(yuǎn)程過(guò)程調(diào)用;也可以作為一個(gè)websocket的長(zhǎng)鏈接服務(wù)器,實(shí)現(xiàn)客戶(hù)端和服務(wù)端的長(zhǎng)連接通信。
二、Hello Netty
使用Netty作為一個(gè)Web服務(wù)器,用于接收用戶(hù)請(qǐng)求并給出響應(yīng)。
Netty程序一般都是按套路來(lái)寫(xiě),依次編寫(xiě)主程序類(lèi)、自定義初始化器、自定義處理器。
1、主程序類(lèi)MyNettyServerTest
通過(guò)ServerBootstrap注冊(cè)serverGroup和clientGroup兩個(gè)事件循環(huán)組,其中serverGroup用于獲取客戶(hù)端連接,clientGroup用于處理客戶(hù)端連接,類(lèi)似于常見(jiàn)的Master-Slave結(jié)構(gòu)。
2、初始化器MyNettyServerInitializer
繼承Netty提供的初始化器ChannelInitializer。
Netty封裝了各種各樣的內(nèi)置處理器,用于實(shí)現(xiàn)各種功能。并且ChannelInitializer的initChannel()方法會(huì)在某一個(gè)連接注冊(cè)到Channel后立即被觸發(fā)調(diào)用。因此,可以根據(jù)業(yè)務(wù)需求,在initChannel()中添加若干個(gè)Netty內(nèi)置處理器,利用Netty強(qiáng)大的類(lèi)庫(kù)直接處理大部分業(yè)務(wù)。最后再在initChannel()中添加一個(gè)自定義處理器,用于實(shí)現(xiàn)特定業(yè)務(wù)的具體功能。
3、自定義處理器MyNettyServerHandler
繼承SimpleChannelInboundHandler,該父類(lèi)的channelRead0()方法可以接收客戶(hù)端的所有請(qǐng)求,并作出響應(yīng),輸出“Hello Netty”。
簡(jiǎn)單講,Netty程序就是通過(guò)主程序類(lèi)關(guān)聯(lián)自定義初始化器,然后在初始化器中加入Netty內(nèi)置處理器和自定義處理器,最后在自定義處理器中編寫(xiě)處理特定需求的業(yè)務(wù)代碼。
三、代碼實(shí)例
1、maven中加入netty-all
<!-- https://mvnrepository.com/artifact/io.netty/netty-all --> <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.68.Final</version> </dependency>
2、主程序類(lèi)MyNettyServerTest
package com.guor.demo.netty; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; /** * 主程序類(lèi) */ public class MyNettyServerTest { public static void main(String[] args) throws InterruptedException { /** * EventLoopGroup:事件循環(huán)組,是一個(gè)線程池,也是一個(gè)死循環(huán),用于不斷地接收用戶(hù)請(qǐng)求; * serverGroup:用戶(hù)監(jiān)聽(tīng)及建立連接,并把每一個(gè)連接抽象為一個(gè)channel,最后再將連接交給clientGroup處理; * clientGroup:真正的處理連接 */ EventLoopGroup serverGroup = new NioEventLoopGroup(); EventLoopGroup clientGroup = new NioEventLoopGroup(); try { // 服務(wù)端啟動(dòng)時(shí)的初始化操作 ServerBootstrap serverBootstrap = new ServerBootstrap(); // 1、將serverGroup和clientGroup注冊(cè)到服務(wù)端的Channel上; // 2、注冊(cè)一個(gè)服務(wù)端的初始化器MyNettyServerInitializer; // 3、該初始化器中的initChannel()方法會(huì)在連接被注冊(cè)到Channel后立刻執(zhí)行; // 5、最后將端口號(hào)綁定到8080; ChannelFuture channelFuture = serverBootstrap.group(serverGroup, clientGroup) .channel(NioServerSocketChannel.class) .childHandler(new MyNettyServerInitializer()).bind(8080).sync(); channelFuture.channel().closeFuture().sync(); }catch (Exception e){ System.out.println(e); }finally { serverGroup.shutdownGracefully(); clientGroup.shutdownGracefully(); } } }
3、初始化器MyNettyServerInitializer
package com.guor.demo.netty; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.http.HttpServerCodec; /** * 初始化器 */ public class MyNettyServerInitializer extends ChannelInitializer<SocketChannel> { // 連接被注冊(cè)到Channel后,立刻執(zhí)行此方法 @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); // 加入netty提供的處理器 pipeline.addLast("HttpServerCodec",new HttpServerCodec()); // 增加自定義處理器MyNettyServerHandler,用于實(shí)際處理請(qǐng)求,并給出響應(yīng) pipeline.addLast("MyNettyServerHandler",new MyNettyServerHandler()); } }
4、自定義處理器MyNettyServerHandler
package com.guor.demo.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; /** * 自定義處理器 * Inbound代表"進(jìn)入"的請(qǐng)求 */ public class MyNettyServerHandler extends SimpleChannelInboundHandler<HttpObject> { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception { // 定義響應(yīng)的內(nèi)容 ByteBuf byteBuf = Unpooled.copiedBuffer("Hello Netty", CharsetUtil.UTF_8); DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf); defaultFullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain"); byteBuf.readableBytes(); // 等響應(yīng)返回給客戶(hù)端 channelHandlerContext.writeAndFlush(defaultFullHttpResponse); } }
5、通過(guò)curl http://localhost:8080訪問(wèn)Netty服務(wù)
此時(shí),可能會(huì)提示curl不是內(nèi)部或外部命令,如何解決?
四、curl不是內(nèi)部或外部命令
1、下載curl for 64-bit
2、將zip解壓到文件夾
3、在環(huán)境變量中配置
五、重寫(xiě)SimpleChannelInboundHandler中一些重要的回調(diào)方法
1、重寫(xiě)回調(diào)方法
package com.guor.demo.netty; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.*; import io.netty.util.CharsetUtil; /** * 自定義處理器 * Inbound代表"進(jìn)入"的請(qǐng)求 */ public class MyNettyServerHandler extends SimpleChannelInboundHandler<HttpObject> { @Override protected void channelRead0(ChannelHandlerContext channelHandlerContext, HttpObject httpObject) throws Exception { // 定義響應(yīng)的內(nèi)容 ByteBuf byteBuf = Unpooled.copiedBuffer("Hello Netty", CharsetUtil.UTF_8); DefaultFullHttpResponse defaultFullHttpResponse = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, byteBuf); defaultFullHttpResponse.headers().set(HttpHeaderNames.CONTENT_TYPE,"text/plain"); byteBuf.readableBytes(); // 等響應(yīng)返回給客戶(hù)端 channelHandlerContext.writeAndFlush(defaultFullHttpResponse); } /** * 增加新處理器時(shí),觸發(fā)此方法 */ @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception{ System.out.println("1、handlerAdded、增加了新的處理器"); super.handlerAdded(ctx); } /** * 當(dāng)通道被注冊(cè)到一個(gè)事件循環(huán)組EventLoop上時(shí),觸發(fā)此方法 */ @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception{ System.out.println("2、channelRegistered、通道被注冊(cè)"); super.channelRegistered(ctx); } /** * 當(dāng)遠(yuǎn)端處于活躍狀態(tài)時(shí)(連接到了某個(gè)遠(yuǎn)端,可以收發(fā)數(shù)據(jù)),執(zhí)行此方法 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception{ System.out.println("3、channelActive、通道連接到了遠(yuǎn)端,處于活躍狀態(tài)"); super.channelActive(ctx); } /** * 當(dāng)通道處于非活躍狀態(tài)時(shí)(與遠(yuǎn)端斷開(kāi)了連接),執(zhí)行此方法 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception{ System.out.println("4、channelInactive、通道遠(yuǎn)端斷開(kāi)了連接,處于非活躍狀態(tài)"); super.channelInactive(ctx); } /** * 當(dāng)通道被取消了注冊(cè)時(shí),執(zhí)行此方法 */ @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception{ System.out.println("5、channelUnregistered、通道被取消了注冊(cè)"); super.channelUnregistered(ctx); } /** * 當(dāng)程序發(fā)生異常,執(zhí)行此方法 */ @Override public void exceptionCaught(ChannelHandlerContext ctx,Throwable e) throws Exception{ System.out.println("6、exceptionCaught、程序發(fā)生了異常"); e.printStackTrace(); ctx.close(); } }
2、通過(guò)curl http://localhost:8080訪問(wèn)Netty服務(wù)
3、控制臺(tái)輸出
4、ctrl + c停止訪問(wèn)
以上就是Netty網(wǎng)絡(luò)編程實(shí)戰(zhàn)之搭建Netty服務(wù)器的詳細(xì)內(nèi)容,更多關(guān)于搭建Netty服務(wù)器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
解決創(chuàng)建springboot后啟動(dòng)報(bào)錯(cuò):Failed?to?bind?properties?under‘spri
在Spring?Boot項(xiàng)目中,application.properties和application.yml是用于配置參數(shù)的兩種文件格式,properties格式簡(jiǎn)潔但不支持層次結(jié)構(gòu),而yml格式支持層次性,可讀性更好,在yml文件中,要注意細(xì)節(jié),比如冒號(hào)后面需要空格2024-10-10用Java設(shè)計(jì)模式中的觀察者模式開(kāi)發(fā)微信公眾號(hào)的例子
這篇文章主要介紹了用Java設(shè)計(jì)模式中的觀察者模式開(kāi)發(fā)微信公眾號(hào)的例子,這里Java的微信SDK等部分便不再詳述,只注重關(guān)鍵部分和開(kāi)發(fā)過(guò)程中觀察者模式優(yōu)點(diǎn)的體現(xiàn),需要的朋友可以參考下2016-02-02java基于jdbc實(shí)現(xiàn)簡(jiǎn)單學(xué)生管理系統(tǒng)
本文主要主要介紹了java連接mysql數(shù)據(jù)庫(kù)的一個(gè)簡(jiǎn)單學(xué)生系統(tǒng),通過(guò)jdbc連接數(shù)據(jù)庫(kù)。文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10MybatisX-Generator自動(dòng)代碼生成插件教程
這篇文章主要介紹了MybatisX-Generator自動(dòng)代碼生成插件教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04Idea如何使用Fast Request接口調(diào)試
這篇文章主要介紹了Idea如何使用Fast Request接口調(diào)試問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Java8 LocalDateTime極簡(jiǎn)時(shí)間日期操作小結(jié)
這篇文章主要介紹了Java8-LocalDateTime極簡(jiǎn)時(shí)間日期操作整理,通過(guò)實(shí)例代碼給大家介紹了java8 LocalDateTime 格式化問(wèn)題,需要的朋友可以參考下2020-04-04Java基本數(shù)據(jù)類(lèi)型與封裝類(lèi)型詳解(int和Integer區(qū)別)
這篇文章主要介紹了Java基本數(shù)據(jù)類(lèi)型與封裝類(lèi)型詳解(int和Integer區(qū)別) ,需要的朋友可以參考下2017-02-02Java 中的 BufferedWriter 介紹_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
BufferedWriter 是緩沖字符輸出流。它繼承于Writer。接下來(lái)通過(guò)本文給大家分享Java 中的 BufferedWriter知識(shí),需要的朋友參考下吧2017-05-05java實(shí)現(xiàn)新浪微博Oauth接口發(fā)送圖片和文字的方法
這篇文章主要介紹了java實(shí)現(xiàn)新浪微博Oauth接口發(fā)送圖片和文字的方法,涉及java調(diào)用新浪微博Oauth接口的使用技巧,具有一定參考接借鑒價(jià)值,需要的朋友可以參考下2015-07-07