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

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

一、故事牽引

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

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

專有名詞解釋:瀏覽器

client端:客戶服務器

server端:ktvsocket

mainGroup或者boosGroup:主線程組,至關於ktv門口外的招待服務生ide

subGroup或者workerGroup:子線程組,至關於ktv內部服務生,負責安排房間的,由招待服務生通知內部服務生安排給他們的函數

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

HelloServerInitializer:進入ktv房間以後的整個流程oop

CustomHandler :分別表明唱歌服務生、端茶送水服務生、跳舞服務生各個實現的功能等等

一、NIO圖

二、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();
      }
   }
}

三、建立處理器

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());
   }
}

四、建立助手類

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);
   }
}

五、pom文件

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

六、啓動main函數

打開瀏覽器:localhost:8088,出現下面錯誤狀況,若是用postman工具的話,就會發現不報錯,由於瀏覽器會自動關閉。

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

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

相關文章
相關標籤/搜索