1. 程式人生 > >Java網路程式設計--Netty入門

Java網路程式設計--Netty入門

Netty簡介

Netty是一個高效能,高可擴充套件性的非同步事件驅動的網路應用程式框架,它極大的簡化了TCP和UDP客戶端和伺服器端網路開發。它是一個NIO框架,對Java NIO進行了良好的封裝。作為一個非同步NIO框架,Netty的所有IO操作都是非同步非阻塞的,通過Future-Listener機制,使用者可以方便的主動獲取或者通過通知機制獲得IO操作結果。

Netty的特性

  • 統一的API,適用於不同的協議
  • 基於靈活、可擴充套件的事件驅動模型
  • 高度可定製的執行緒模型
  • 更好的吞吐量,低延遲
  • 更省資源,儘量減少不必要的記憶體拷貝
  • 完整的SSL/TLS和STARTTLS的支援
  • 能在Applet與Android的限制環境執行良好
  • 不再因過快、過慢或超負載連線導致OutOfMemoryError
  • 不再有在高速網路環境下NIO讀寫頻率不一致的問題

Netty核心內容

Netty中最核心的內容主要有以下四個方面:

  • Reactor執行緒模型:一種高效能的多執行緒程式設計思路
  • Netty中自己定義的Channel概念:增強版的通道概念
  • ChannelPipeline職責鏈設計模式:事件處理機制
  • 記憶體管理:增強的ByteBuf緩衝區

Netty整體結構圖

img

Netty核心元件

EventLoop:EventLoop維護了一個執行緒和任務佇列,支援非同步提交執行任務。EventLoop自身實現了Executor介面,當呼叫executor方法提交任務時,則判斷是否啟動,未啟動則呼叫內建的executor建立新執行緒來觸發run方法執行,其大致流程參考Netty原始碼SingleThreadEventExecutor如下:

img

EventLoopGroup: EventLoopGroup主要是管理eventLoop的生命週期,可以將其看作是一個執行緒池,其內部維護了一組EventLoop,每個eventLoop對應處理多個Channel,而一個Channel只能對應一個EventLoop

img

Bootstrap:BootStrap 是客戶端的引導類,主要用於客戶端連線遠端主機,有1個EventLoopGroup。Bootstrap 在呼叫 bind()(連線UDP)和 connect()(連線TCP)方法時,會新建立一個單獨的、沒有父 Channel 的 Channel 來實現所有的網路交換。

ServerBootstrap

: ServerBootstrap 是服務端的引導類,主要使用者服務端繫結本地埠,有2個EventLoopGroup。ServerBootstarp 在呼叫 bind() 方法時會建立一個 ServerChannel 來接受來自客戶端的連線,並且該 ServerChannel 管理了多個子 Channel 用於同客戶端之間的通訊。

Channel:Netty中的Channel是一個抽象的概念,可以理解為對Java NIO Channel的增強和擴充套件,增加了許多新的屬性和方法,如bing方法等。

ChannelFuture:ChannelFuture能夠註冊一個或者多個ChannelFutureListener 例項,當操作完成時,不管成功還是失敗,均會被通知。ChannelFuture儲存了之後執行的操作的結果並且無法預測操作何時被執行,提交至Channel的操作按照被喚醒的順序被執行。

ChannelHandler:ChannelHandler用來處理業務邏輯,分別有入站和出站的實現。

ChannelPipeline: ChannelPipeline 提供了 ChannelHandler鏈的容器,並定義了用於在該鏈上傳播入站和出站事件流的API。

Netty執行緒模型

Netty的執行緒模型是基於Reactor模式的執行緒實現。關於Reactor模式可以參考 [Reactor模式](2.1.3 Reactor模式.md) ,Netty中依據使用者的配置可以支援單執行緒的Reactor模型,多執行緒的Reactor模型以及主從多Reactor的模型。在Netty中其大致流程如下如下:

img

Netty入門程式碼示例

服務端程式碼示例:

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.nio.charset.Charset;

public class EchoServer {

  public static void main(String[] args) {
    // accept執行緒組,用來接受連線
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    // I/O執行緒組, 用於處理業務邏輯
    EventLoopGroup workerGroup = new NioEventLoopGroup(1);

    try {
      // 服務端啟動引導
      ServerBootstrap b = new ServerBootstrap();
      b.group(bossGroup, workerGroup) // 繫結兩個執行緒組
          .channel(NioServerSocketChannel.class) // 指定通道型別
          .option(ChannelOption.SO_BACKLOG, 100) // 設定TCP連線的緩衝區
          .handler(new LoggingHandler(LogLevel.INFO)) // 設定日誌級別
          .childHandler(
              new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel socketChannel) throws Exception {
                  ChannelPipeline pipeline = socketChannel.pipeline(); // 獲取處理器鏈
                  pipeline.addLast(new EchoServerHandler()); // 新增新的件處理器
                }
              });

      // 通過bind啟動服務
      ChannelFuture f = b.bind(8080).sync();
      // 阻塞主執行緒,知道網路服務被關閉
      f.channel().closeFuture().sync();

    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      workerGroup.shutdownGracefully();
      bossGroup.shutdownGracefully();
    }
  }
}

class EchoServerHandler extends ChannelInboundHandlerAdapter {

  // 每當從客戶端收到新的資料時,這個方法會在收到訊息時被呼叫
  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    System.out.println("收到資料:" + ((ByteBuf) msg).toString(Charset.defaultCharset()));
    ctx.write(Unpooled.wrappedBuffer("Server message".getBytes()));
    ctx.fireChannelRead(msg);
  }

  // 資料讀取完後被呼叫
  @Override
  public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    ctx.flush();
  }

  // 當Netty由於IO錯誤或者處理器在處理事件時丟擲的異常時被呼叫
  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
    ctx.close();
  }
}

客戶端程式碼示例:

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.nio.charset.Charset;

public class EchoClient {

  public static void main(String[] args) {
    EventLoopGroup group = new NioEventLoopGroup();
    try {
      Bootstrap b = new Bootstrap();
      b.group(group)
          .channel(NioSocketChannel.class)
          .option(ChannelOption.TCP_NODELAY, true)
          .handler(
              new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                  ChannelPipeline p = ch.pipeline();
                  p.addLast(new EchoClientHandler());
                }
              });

      ChannelFuture f = b.connect("127.0.0.1", 8080).sync();
      f.channel().closeFuture().sync();

    } catch (Exception e) {
      e.printStackTrace();
    } finally {
      group.shutdownGracefully();
    }
  }
}

class EchoClientHandler extends ChannelInboundHandlerAdapter {

  private final ByteBuf firstMessage;

  public EchoClientHandler() {
    firstMessage = Unpooled.buffer(256);
    for (int i = 0; i < firstMessage.capacity(); i++) {
      firstMessage.writeByte((byte) i);
    }
  }

  @Override
  public void channelActive(ChannelHandlerContext ctx) {
    ctx.writeAndFlush(firstMessage);
  }

  @Override
  public void channelRead(ChannelHandlerContext ctx, Object msg) {
    System.out.println("收到資料:" + ((ByteBuf) msg).toString(Charset.defaultCharset()));
    ctx.write(Unpooled.wrappedBuffer("Client message".getBytes()));
  }

  @Override
  public void channelReadComplete(ChannelHandlerContext ctx) {
    ctx.flush();
  }

  @Override
  public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    cause.printStackTrace();
    ctx.close();
  }
}

相關推薦

Java網路程式設計--Netty入門

Netty簡介 Netty是一個高效能,高可擴充套件性的非同步事件驅動的網路應用程式框架,它極大的簡化了TCP和UDP客戶端和伺服

Java網路程式設計入門到精通(27):關閉服務端連線

package server;import java.net.*;class Client {     public static void main(String[] args) throws Exception     {         Socket socket = new Socket("127.

Java網路程式設計--Netty中的責任鏈

Netty中的責任鏈 設計模式 - 責任鏈模式 責任鏈模式(Chain of Responsibility Pattern)是一種是行為型設計模式,它為請求建立了一個處理物件的鏈。其鏈中每一個節點都看作是一個物件,每個節點處理的請求均不同,且內部自動維護一個下一節點物件。當一個請求從鏈式的首端發出時,會沿著

Java網路程式設計--Netty中的ByteBuf

ByteBuf的操作 ByteBuf有三個重要的屬性:capacity容量,readerIndex讀取位置,writerIndex寫入位置 提供了readerIndex和weiterIndex兩個變數指標來支援順序讀和寫操作 下圖顯示了一個緩衝區是如何被兩個指標分割成三個區域的: 程式碼示例: impor

Java網路程式設計入門(五)之TCP程式設計——複用Socket連線

如何複用Socket連線? 在前面的示例中,客戶端中建立了一次連線,只發送一次資料就關閉了,這就相當於撥打電話時,電話打通了只對話一次就關閉了,其實更加常用的應該是撥通一次電話以後多次對話,這就是複用客戶端連線。 那 麼如何實現建立一次連線,進行多次資料交換呢?其實很簡單

Java網路程式設計入門(八)之網路協議

 網路協議          對於需要從事網路程式設計的程式設計師來說,網路協議是一個需要深刻理解的概念。那麼什麼是網路協議呢?          網路協議是指對於網路中傳輸的資料格式的規定。對於網路程式設計初學者來說,沒有必要深入瞭解TCP/IP協議簇,所以對於初學者來

JAVA 網路程式設計(6) Netty TCP 示例

maven使用的netty版本如下: <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> &

Java網路程式設計】:Netty實現OIO和NIO

承接上文:https://blog.csdn.net/hxcaifly/article/details/85274664 前言 單純地使用Java JDK來實現網路NIO是一件開發成本非常高的事情。然而,Netty 為它所有的傳輸實現提供了一個通用API,這使得這種

JAVA 網路程式設計(7) Netty 處理Http協議 示例

maven中使用netty的版本為: <dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> &

Java網路程式設計Netty拆包和黏包-yellowcong

Netty中,解決拆包和黏包中,解決方式有三種 1、在每個包尾部,定義分隔符,通過回車符號,或者其他符號來解決 2、通過定義每個包的大小,如果包不夠就空格填充 3、自定義協議的方式,將訊息分為訊息頭和訊息體,在訊息頭中表示出訊息的總長度,

Java 基於UDP的Socket網路程式設計入門及示例

前言:天吶!這兩天上班就像走鋼絲啊,老大家裡出事請假一週,手機關機,底層無人照看,機器又忙著定標,技術盲老闆事事問我這個底層小白。做人好難吶。。。還是祝願老大家裡沒有大礙吧,也保佑自己明天能安然度過。。保佑保佑。 原文出處:http://blog.c

java網路程式設計Netty實戰資料通訊(七)

Netty最佳實戰資料通訊 1 分析       我們需要了解下在真正專案應用中如何去考虛Netty的使用,大體上對於一引數設定都是根據伺服器效能決定的。這個不是最主要的。       我們要考慮的問題是兩臺機器(甚至多臺)使用Netty的怎樣進行通訊,我

【帶你入門java網路程式設計

網路程式設計 網路程式設計對於很多的初學者來說,都是很嚮往的一種程式設計技能,但是很多的初學者卻因為很長一段時間無法進入網路程式設計的大門而放棄了對於該部分技術的學習。 在 學習網路程式設計以前,很多初學者可能覺得網路程式設計是比較複雜的系統工程,需要了

JAVA網路程式設計(TCP篇入門

此次部落格主要以程式碼為主,其中包含了豐富的註釋 簡單的tcp 簡單介紹tcp是如何傳送資料,以及接受資料 程式碼一:客戶端 package com.net.tcp; import java.io.IOException; import java.io.Out

JAVA網路程式設計(UDP入門篇)

網路程式設計:通過計算機語言實現資源的共享 網路程式設計模型資料一層層的封裝打包 應用層:eg:應用程式,qq 表示層: 會話層: 傳輸層: 網路層:資料傳輸 資料鏈路層:資料幀,使用交換機傳遞資料 物理層 : 將資料轉化為1,0資料 應用程式:網路程式設計+IO

java網路程式設計Netty流資料的傳輸處理(五)

Netty流資料的傳輸處理 Socket Buffer的缺陷       對於例如TCP/IP這種基於流的傳輸協議實現,接收到的資料會被儲存在socket的接受緩衝區內。不幸的是,這種基於流的傳輸緩衝區並不是一個包佇列,而是一個位元組佇列。這意味著,即使

java網路程式設計—NIO與Netty(一)

java BIO 流 流是一個連續的寫入\讀取 的資料流。將網路、硬碟、記憶體中的資料寫入程式中稱為InputStream,方向相反輸出則稱為OutputStream 顯然,流具有方向性! InputStream 對於java IO包的

java網路程式設計_IO模型

理解java的BIO、NIO、AIO的原理: 一、UNIX程式設計中的五種IO模型: 1. 阻塞IO 外賣小哥去商家取外賣,到了麻辣燙店,商家還沒做好, 外賣小哥雖然還有其他單子要送,但是不得不焦急地等著商家; 2. 非阻塞IO 外賣小哥去商家取外賣

Java網路程式設計基礎部分

轉載:文章來源 網路通訊協議 網路通訊協議有很多種,目前應用最廣泛的是TCP/IP協議(Transmission Control Protocal/Internet Protoal傳輸控制協議/英特網互聯協議),它是一個包括TCP協議和IP協議,UDP(User Datagram Protoco

20180827-Java網路程式設計

  Java 網路程式設計 網路程式設計是指編寫執行在多個裝置(計算機)的程式,這些裝置都通過網路連線起來。 java.net包中J2SE的API包含有類和介面,它們提供低層次的通訊細節。你可以直接使用這些類和介面,來專注於解決問題,而不用關注通訊細節。 java.net包中提供了兩種常見的