1. 程式人生 > >Netty進階篇之簡單版websocket發訊息(7)

Netty進階篇之簡單版websocket發訊息(7)

序言:簡單開發前端傳送訊息,後端接收訊息並返回。

1、故事牽引

今天通過一個故事來講解netty,主要講client和server端和下面的主要實現類。

客戶要去ktv唱歌,進入ktv之前,門口會有招待服務生,然後招待服務生看到客戶之後,會安排你們給ktv內部服務生給你們安排房間,讓後帶你們進入ktv房間唱歌,準備開始唱歌就要開啟音樂,選好歌曲,開啟燈光等因素,之後這個期間你們可能會讓唱歌的服務生來助唱,讓會跳舞的服務生給你們跳舞,甚至讓服務生帶來酒或者吃的,直到你們唱歌結束付完錢離開為止,整個過程就算結束。

專有名詞解釋:

client端:客戶

server端:ktv

mainGroup或者boosGroup:主執行緒組,相當於招待服務生

subGroup或者workerGroup:子執行緒組,相當於內部服務生

ServerBootstrap :客戶進入ktv之前到離開ktv之後的整個流程

HelloServerInitializer:進入ktv房間準備點歌,初始化,準備點歌的環境以及東西

CustomHandler :分別代表唱歌服務生、端茶送水服務生、跳舞服務生等等

1、NIO圖

2、server端

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

/**
 * @Description: 實現客戶端傳送一個請求,伺服器會返回 hello netty
 */
public class HelloServer {

   public static void main(String[] args) throws Exception {

      // 定義一對執行緒組
      // 主執行緒組, 用於接受客戶端的連線,但是不做任何處理,跟老闆一樣,不做事
      EventLoopGroup bossGroup = new NioEventLoopGroup();
      // 從執行緒組, 老闆執行緒組會把任務丟給他,讓手下執行緒組去做任務
      EventLoopGroup workerGroup = new NioEventLoopGroup();
      
      try {
         // netty伺服器的建立, ServerBootstrap 是一個啟動類
         ServerBootstrap serverBootstrap = new ServerBootstrap();
         serverBootstrap.group(bossGroup, workerGroup)        // 設定主從執行緒組
                     .channel(NioServerSocketChannel.class) // 設定nio的雙向通道
                     .childHandler(new HelloServerInitializer()); // 子處理器,用於處理workerGroup
         
         // 啟動server,並且設定8088為啟動的埠號,同時啟動方式為同步
         ChannelFuture channelFuture = serverBootstrap.bind(8088).sync();
         
         // 監聽關閉的channel,設定位同步方式
         channelFuture.channel().closeFuture().sync();
      } finally {
         bossGroup.shutdownGracefully();
         workerGroup.shutdownGracefully();
      }
   }
}

3、建立處理器

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpServerCodec;
/**
 * @Description: 初始化器,channel註冊後,會執行裡面的相應的初始化方法
 */
public class HelloServerInitializer extends ChannelInitializer<SocketChannel> {

   @Override
   protected void initChannel(SocketChannel channel) throws Exception {
      // 通過SocketChannel去獲得對應的管道
      ChannelPipeline pipeline = channel.pipeline();
      
      // 通過管道,新增handler
      // HttpServerCodec是由netty自己提供的助手類,可以理解為攔截器
      // 當請求到服務端,我們需要做解碼,響應到客戶端做編碼
      pipeline.addLast("HttpServerCodec", new HttpServerCodec());
      
      // 新增自定義的助手類,返回 "hello netty~"
      pipeline.addLast("customHandler", new CustomHandler());
   }
}

4、建立助手類

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpObject;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.CharsetUtil;

/**
 * @Description: 建立自定義助手類
 */
// SimpleChannelInboundHandler: 對於請求來講,其實相當於[入站,入境]
public class CustomHandler extends SimpleChannelInboundHandler<HttpObject> {

   @Override
   protected void channelRead0(ChannelHandlerContext ctx, HttpObject msg) 
         throws Exception {
      // 獲取channel
      Channel channel = ctx.channel();
      
      if (msg instanceof HttpRequest) {
         // 顯示客戶端的遠端地址
         System.out.println(channel.remoteAddress());
         
         // 定義傳送的資料訊息
         ByteBuf content = Unpooled.copiedBuffer("Hello netty~", CharsetUtil.UTF_8);
         
         // 構建一個http response
         FullHttpResponse response = 
               new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, 
                     HttpResponseStatus.OK, 
                     content);
         // 為響應增加資料型別和長度
         response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
         response.headers().set(HttpHeaderNames.CONTENT_LENGTH, content.readableBytes());
         
         // 把響應刷到客戶端
         ctx.writeAndFlush(response);
      }
      
   }

   @Override
   public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
      System.out.println("channel。。。註冊");
      super.channelRegistered(ctx);
   }

   @Override
   public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
      System.out.println("channel。。。移除");
      super.channelUnregistered(ctx);
   }

   @Override
   public void channelActive(ChannelHandlerContext ctx) throws Exception {
      System.out.println("channel。。。活躍");
      super.channelActive(ctx);
   }

   @Override
   public void channelInactive(ChannelHandlerContext ctx) throws Exception {
      System.out.println("channel。。。不活躍");
      super.channelInactive(ctx);
   }

   @Override
   public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
      System.out.println("channeld讀取完畢。。。");
      super.channelReadComplete(ctx);
   }

   @Override
   public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
      System.out.println("使用者事件觸發。。。");
      super.userEventTriggered(ctx, evt);
   }

   @Override
   public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
      System.out.println("channel可寫更改");
      super.channelWritabilityChanged(ctx);
   }

   @Override
   public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
      System.out.println("補貨到異常");
      super.exceptionCaught(ctx, cause);
   }

   @Override
   public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
      System.out.println("助手類新增");
      super.handlerAdded(ctx);
   }

   @Override
   public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
      System.out.println("助手類移除");
      super.handlerRemoved(ctx);
   }
}

5、pom檔案

<dependency>
    <groupId>io.netty</groupId>
    <artifactId>netty-all</artifactId>
    <version>4.1.25.Final</version>
</dependency>

6、啟動main函式

開啟瀏覽器:localhost:8088,出現下面錯誤情況,如果用postman工具的話,就會發現不報錯,因為瀏覽器會自動關閉。

解決方案:將super.exceptionCauht(ctx,case);註釋掉就好了

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    //super.exceptionCaught(ctx, cause);
}

相關推薦

Netty簡單websocket訊息7

序言:簡單開發前端傳送訊息,後端接收訊息並返回。 1、故事牽引 今天通過一個故事來講解netty,主要講client和ser

Nettywebsocket傳送訊息8

序言:Netty進階篇之簡單版websocket發訊息(7) 大概和下面的程式碼就成相似,如果不懂,可以看一下這篇部落格

【VMCloud雲平臺】應用數據層面優化

vmcloud在上一篇中我們講到準備SQL基礎環境改造,這一篇將繼續講述如何為應用提供高可用的底層數據層,以下是本次要進行實驗的拓撲(紅色為已完成搭建,藍色是本次文章涉及的區域):1、 上一篇我們搭建好了SQL底層,這一篇來講述如何創建一個AlwayOn可用組對外提供服務,設置兩個SQL節點的AlwayOn高

Java十五 ----- JDK1.8的Lambda、Stream和日期的使用詳解(很詳細)

前言 本篇主要講述是Java中JDK1.8的一些新語法特性使用,主要是Lambda、Stream和LocalDate日期的一些使用講解。 Lambda Lambda介紹 Lambda 表示式(lambda expression)是一個匿名函式,Lambda表示式基於數學中的λ演算得名,直接對應於

十二vue.js元件——元件通訊3

(1)概述 所謂元件間的通訊,實際上就是指在各個元件間,進行引數或者資訊的相互傳遞。比如我們前面學的通過props給子元件傳參,實際上這就是父元件向子元件進行單向的通訊。 (2)元件間通訊的幾種方式 1.父到子的通訊 父到子的通訊使用我們前面使用的props即可

Android引導頁系列ViewPager實現Animation動畫引導頁

小夥伴們,時隔很久,我又開始來BB了,勿噴,,今天繼續上次所說的引導頁之旅。 其實想實現一個靜態的引導頁還是很容易的,就是一個ViewPager,但是想對每一頁實現動畫效果,比如第一頁有一幾朵雲在飄啊飄!,想實現這種效果對只要瞭解過Animation動畫的人來說也不難實現。

Jenkins外掛開發擴充套件外掛功能

之前寫過一篇文章是關於如何開發jenkins外掛,主要講述了開發jenkins外掛時需要準備的環境,如何新建一個jenkins外掛工程,以及對工程專案目錄結構的解析。本文是jenkins外掛開發的進階篇,主要講述如何擴充套件jenkins外掛的功能。如下圖所示:(1)Job任

Three.js2

動畫原理 在這裡,我們將動態畫面簡稱為動畫(animation)。正如動畫片的原理一樣,動畫的本質是利用了人眼的視覺暫留特性,快速地變換畫面,從而產生物體在運動的假象。而對於Three.js程式而言,動畫的實現也是通過在每秒中多次重繪畫面實現的。 為了衡量畫面切換速度

FragmentFragment生命週期和回退棧

前言 上一篇blog(處女男學Android(八)---Fragment初體驗之實現Tab導航)記錄了fragment的基本概念和基本的使用方法,本篇將逐步深入記錄關於fragment的幾個重要知識點,包括:fragment的生命週期、fragm

Three.js7

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <style> body {margin: 0;} body, html {overflow: hidden

Android引導頁系列Splash閃屏Log

最近比較閒,所以會陸續出很多基礎型Demo和高階點的Demo,,小夥伴們肯定又要罵了,孫子又來裝B了 在android應用中,其實閃屏是一個很重要的前期 先上效果圖:就是一個頁面,沒有Logo和公司名幾乎就是一張背景,又要捱罵了 很重要?,這不廢話嗎,不重要為什麼每個應用

反射應用自定義反射工具類在springmvc中的應用

本篇使用自定義工具類進行批量處理物件 ---將批量源物件的屬性值注入到實際需要的目標類物件(屬性名相同,型別不同)中 專案使用maven構建war工程:  spring+spring MVC+Mybatis 回顧知識點: 事務:--->為什麼在使用AOP時需要使

AndroidListView、GridView以及ScrollView實現按鈕控制滾動

這次還是一樣,因為專案需要,需要對滾動檢視需要手動控制,也就是點選上下左右按鈕,能讓滾動控制元件觸發對應的操作 在android中,滾動View最基本的有三種,分別是:ListView、GridView和ScrollView 先看效果圖:1、ListView的效果圖,右下角

Data binding 入坑筆記二雙向繫結

上一篇介紹了Data binding的基礎用法,你可能會想這也太基礎了,只支援前置資料的繫結,一旦資料變化了UI都監聽不到。不要著急,這一篇就來講到databinding的雙向繫結用法。 0.舉個例子 我們用一個輸入控制元件EditText和一個

《手把手教你》系列1-python+ selenium自動化測試 - python基礎掃盲詳細教程

1. 簡介   如果你從一開始就跟著巨集哥看部落格文章到這裡,基礎篇和練習篇的文章。如果你認真看過,並且手動去敲過每一篇的指令碼程式碼,那邊恭喜你,至少說你算真正會利用Python+Selenium編寫自動化指令碼了,你基本掌握了Selenium中webdriver的大部分常用的方法函式。有大佬曾經說過,自動

《手把手教你》系列2-python+ selenium自動化測試 - python基礎掃盲詳細教程

1. 簡介   這篇文章主要是分享講解一下,如何封裝自己用到的方法和類。以便方便自己和別人的呼叫,這樣就可以避免重複地再造輪子。   封裝(Encapsulation)是面向物件的三大特徵之一(另外兩個是繼承和多型),它指的是將物件的狀態資訊隱藏在物件內部,不允許外部程式直接訪問物件內部資訊,而是通過該類所提

《手把手教你》系列3-python+ selenium自動化測試 - python幾種騷操作你都知道嗎?詳細教程

1. 簡介    這篇文章主要是給小夥伴或者童鞋們介紹和分享 python幾種騷操:讀取配置檔案、獲取根目錄的相對路徑、獲取系統時間和格式化時間顯示、字串切割等等操作。為後邊的自動化框架打下一個結實的基礎。 2. Python讀取配置檔案內容   本文來介紹下Python中如何讀取配置檔案

Spring CloudEureka原理分析

前言 之前寫了幾篇Spring Cloud的小白教程,相信看過的朋友對Spring Cloud中的一些應用有了簡單的瞭解,寫小白篇的目的就是為初學者建立一個基本概念,讓初學者在學習的道路上建立一定的基礎。 從今天開始,我會持續更新幾篇Spring Cloud的進階教程。 Eureka簡介 Eu

《手把手教你》系列4-python+ selenium自動化測試 - python幾種超神操作你都知道嗎?詳細教程

1. 簡介    今天分享和講解的超神操作,對於菜鳥來說是超神的操作,對於大佬來說也就是幾個簡單方法的封裝和呼叫。這裡講解和分享這部分主要是為了培養小夥伴們和童鞋們的面向物件的開發思維,對比這樣做的好處讓你自己身臨其境的感受一番。 2. 自定義封裝一個簡單的Log類   本文介紹如何寫一個

UVM序列二:sequence和item

技術 一點 目標 idt 需要 開始 掛載 ron 前行 無論是自駕item,穿過sequencer交通站,通往終點driver,還是坐上sequence的大巴,一路沿途觀光,最終跟隨導遊停靠到風景點driver,在介紹如何駕駛item和sequence,遵守什麽交規,最終