因為接下來的項目要用到netty,所以就了解一下這個程序,奈何網上的教程都是稍微有點基礎的,所以,就寫一篇對於netty零基礎的,順便也記錄一下。
先扔幾個參考學習的網頁:
netty 官方API: http://netty.io/4.1/api/index.html
netty 中文指南:https://waylau.com/netty-4-user-guide/ (來自個人)
關於NIO基礎的知識:https://my.oschina.net/andylucc/blog/614295
http://www.cnblogs.com/dolphin0520/p/3919162.html
http://blog.csdn.net/wuxianglong/article/details/6604817
我這里所使用的:
netty版本:netty-5.0.0.Alpha2 http://files.cnblogs.com/files/applerosa/netty-5.0.0.Alpha2.7z
maven依賴:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>5.0.0.Alpha2</version> </dependency>
好了,我們開始。
一、首先,你要建立一個java工程,至於普通工程還是maven工程,看自己喜好,因為我這里只是做為學習,就建了一個普通工程,然后把下載的jar包扔到lib中,引入即可。
二、接下來我們要搞清楚,netty是什么玩意。
官方那個給出的介紹是:Netty是由JBOSS提供的一個java開源框架。Netty提供異步的、事件驅動的網絡應用程序框架和工具,用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。
然后我們簡單理解一下,這玩意就是個程序,干什么的?netty是封裝java socket noi的。 類似的功能是 apache的mina。
相對於Tomcat這種Web Server(顧名思義主要是提供Web協議相關的服務的),Netty是一個Network Server,是處於Web Server更下層的網絡框架,也就是說你可以使用Netty模仿Tomcat做一個提供HTTP服務的Web容器。
說白了,就是一個好使的處理Socket的東西。要是想了解詳細點,可以去看看官方的介紹。
三、回到正題我們開始寫所謂的“Hello World"
這里插一下,就是我們的的通信是建立在一定的協議之上的,就比如我們常用的Web工程,前台(瀏覽器)發送一個請求,后台做出相應返回相應的結果,這個通信的過程亦是如此。
在netty官方指南里面有講,世上最簡單的協議不是'Hello, World!' 而是 DISCARD(拋棄服務)。這個協議將會拋棄任何收到的數據,而不響應。就是你客戶端發送消息,好,發送過去了,服務器也收到了,但是拋棄了。
說白了,就是你發一條消息給我,我收到了,但是我直接就把消息拋棄了,不理你的。
其次,關於netty ,首先要搞清楚,這是建立在客戶端和服務端之間的。
我們先說服務端,服務端建立相應的規則,然后運行起來,等待客戶端訪問或者發送”消息“。好了,我們先建立服務端代碼:
第一步:先建立相應的規則:
package _01discard; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.util.CharsetUtil; import io.netty.util.ReferenceCountUtil; /** * 服務端處理通道.這里只是打印一下請求的內容,並不對請求進行任何的響應 DiscardServerHandler 繼承自 * ChannelHandlerAdapter, 這個類實現了ChannelHandler接口, ChannelHandler提供了許多事件處理的接口方法, * 然后你可以覆蓋這些方法。 現在僅僅只需要繼承ChannelHandlerAdapter類而不是你自己去實現接口方法。 * */ public class DiscardServerHandler extends ChannelHandlerAdapter { /** * 這里我們覆蓋了chanelRead()事件處理方法。 每當從客戶端收到新的數據時, 這個方法會在收到消息時被調用, * 這個例子中,收到的消息的類型是ByteBuf * * @param ctx * 通道處理的上下文信息 * @param msg * 接收的消息 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { try { ByteBuf in = (ByteBuf) msg; // 打印客戶端輸入,傳輸過來的的字符 System.out.print(in.toString(CharsetUtil.UTF_8)); } finally { /** * ByteBuf是一個引用計數對象,這個對象必須顯示地調用release()方法來釋放。 * 請記住處理器的職責是釋放所有傳遞到處理器的引用計數對象。 */ // 拋棄收到的數據 ReferenceCountUtil.release(msg); } } /*** * 這個方法會在發生異常時觸發 * * @param ctx * @param cause */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { /** * exceptionCaught() 事件處理方法是當出現 Throwable 對象才會被調用,即當 Netty 由於 IO * 錯誤或者處理器在處理事件時拋出的異常時。在大部分情況下,捕獲的異常應該被記錄下來 並且把關聯的 channel * 給關閉掉。然而這個方法的處理方式會在遇到不同異常的情況下有不 同的實現,比如你可能想在關閉連接之前發送一個錯誤碼的響應消息。 */ // 出現異常就關閉 cause.printStackTrace(); ctx.close(); } }
第二步:我們需要應用相應的規則。就是說,我們建立了接收消息的規則,但是光建立規則有什么用,僅僅只是一個規則,我們需要把這個規則”應用“起來,通常就是我們通常的”運行“。
package _01discard; 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; /** * 丟棄任何進入的數據 啟動服務端的DiscardServerHandler */ public class DiscardServer { private int port; public DiscardServer(int port) { super(); this.port = port; } public void run() throws Exception { /*** * NioEventLoopGroup 是用來處理I/O操作的多線程事件循環器, * Netty提供了許多不同的EventLoopGroup的實現用來處理不同傳輸協議。 在這個例子中我們實現了一個服務端的應用, * 因此會有2個NioEventLoopGroup會被使用。 第一個經常被叫做‘boss’,用來接收進來的連接。 * 第二個經常被叫做‘worker’,用來處理已經被接收的連接, 一旦‘boss’接收到連接,就會把連接信息注冊到‘worker’上。 * 如何知道多少個線程已經被使用,如何映射到已經創建的Channels上都需要依賴於EventLoopGroup的實現, * 並且可以通過構造函數來配置他們的關系。 */ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); System.out.println("准備運行端口:" + port); try { /** * ServerBootstrap 是一個啟動NIO服務的輔助啟動類 你可以在這個服務中直接使用Channel */ ServerBootstrap b = new ServerBootstrap(); /** * 這一步是必須的,如果沒有設置group將會報java.lang.IllegalStateException: group not * set異常 */ b = b.group(bossGroup, workerGroup); /*** * ServerSocketChannel以NIO的selector為基礎進行實現的,用來接收新的連接 * 這里告訴Channel如何獲取新的連接. */ b = b.channel(NioServerSocketChannel.class); /*** * 這里的事件處理類經常會被用來處理一個最近的已經接收的Channel。 ChannelInitializer是一個特殊的處理類, * 他的目的是幫助使用者配置一個新的Channel。 * 也許你想通過增加一些處理類比如NettyServerHandler來配置一個新的Channel * 或者其對應的ChannelPipeline來實現你的網絡程序。 當你的程序變的復雜時,可能你會增加更多的處理類到pipline上, * 然后提取這些匿名類到最頂層的類上。 */ b = b.childHandler(new ChannelInitializer<SocketChannel>() { // (4) @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new DiscardServerHandler());// demo1.discard // ch.pipeline().addLast(new // ResponseServerHandler());//demo2.echo // ch.pipeline().addLast(new // TimeServerHandler());//demo3.time } }); /*** * 你可以設置這里指定的通道實現的配置參數。 我們正在寫一個TCP/IP的服務端, * 因此我們被允許設置socket的參數選項比如tcpNoDelay和keepAlive。 * 請參考ChannelOption和詳細的ChannelConfig實現的接口文檔以此可以對ChannelOptions的有一個大概的認識。 */ b = b.option(ChannelOption.SO_BACKLOG, 128); /*** * option()是提供給NioServerSocketChannel用來接收進來的連接。 * childOption()是提供給由父管道ServerChannel接收到的連接, * 在這個例子中也是NioServerSocketChannel。 */ b = b.childOption(ChannelOption.SO_KEEPALIVE, true); /*** * 綁定端口並啟動去接收進來的連接 */ ChannelFuture f = b.bind(port).sync(); /** * 這里會一直等待,直到socket被關閉 */ f.channel().closeFuture().sync(); } finally { /*** * 關閉 */ workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } }
//將規則跑起來 public static void main(String[] args) throws Exception { int port; if (args.length > 0) { port = Integer.parseInt(args[0]); } else { port = 8080; } new DiscardServer(port).run(); System.out.println("server:run()"); } }
第三步:我們現在相應的規則已經建立,並且”運行“規則的代碼也OK,所以運行上面的 public static void main(String[] args) 啟動服務端。
此時服務端已經運行起來了,為等待訪問的狀態。
客戶端
因為這是一個簡單的demo,所以我們使用telnet 來充當client使用。當然,項目中肯定是根據需求來定制的。
首先打開終端,我這里是windows 系統,就以此為例,打開cmd 窗口;
鍵入 telnet 127.0.0.1 8080 回車,進入telnet 終端
這里補充一下,win系統默認是不開啟telnet 客戶端的,需要的朋友去控制面板>>程序>>打開或者關閉windows功能里面 勾選上,如下圖
好了,到了telnet 客戶端這一步,就可以測試消息了
補充,默認的telnet 客戶端輸入是不顯示的,不過反正輸入之后控制台有輸出就表示你這個過程跑通了。如:
你也在終端使用 ctrl+] 來顯示,如:
走到這里,整個過程大概是什么樣子的,心里就有個數了,然后就和學習普通框架一樣,開始的進階之路吧。