遊戲開發之-通信框架-Netty+WebSocket(Java版本)

引入Netty的依賴包 

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

WebsocketServer.java

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

public class WebsocketServer {
    //通過主函數運行此方法即可開啓Netty服務
    public static void run(int port,String websocketPath) throws Exception {
        //boos對應,IOServer.java中的接受新連接線程,主要負責創建新連接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //worker對應 IOClient.java中的負責讀取數據的線程,主要用於讀取數據以及業務邏輯處理
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
             //獨立創建服務
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new WebsocketServerInitializer(websocketPath))
                    .option(ChannelOption.SO_BACKLOG, 128)
                    .childOption(ChannelOption.SO_KEEPALIVE, true);
            //綁定端口,等待連接
            ChannelFuture future = bootstrap.bind(port).sync();
            //通道關閉監聽並同步阻塞
            future.channel().closeFuture().sync();
        } finally {
            //優雅的關閉羣組
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
}
WebsocketServerInitializer.java
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

public class WebsocketServerInitializer extends ChannelInitializer<SocketChannel> {
    private String websocketPath;

    public WebsocketServerInitializer(String websocketPath){
        super();
        this.websocketPath = websocketPath;
    }

    //通道初始化
    @Override
    public void initChannel(SocketChannel ch){
        //通信管道
        ChannelPipeline pipeline = ch.pipeline();
       //http服務編解碼器
        pipeline.addLast(new HttpServerCodec());
        //設置最大消息長度
        pipeline.addLast(new HttpObjectAggregator(64*1024));
        //分塊寫入處理器
        pipeline.addLast(new ChunkedWriteHandler());
        //自定義的http服務處理器
        pipeline.addLast(new HttpRequestHandler(websocketPath));
        //websocket的服務協議處理器
        pipeline.addLast(new WebSocketServerProtocolHandler(websocketPath));
        //自定義的websocket的文本框架處理器
        pipeline.addLast(new TextWebSocketFrameHandler());
    }
}

HttpRequestHandler.java

import io.netty.channel.*;
import io.netty.handler.codec.http.*;

public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> { //1
    private String websocketPath;

    public HttpRequestHandler(String websocketPath){
        this.websocketPath = websocketPath;
    }

    //迴應客戶端請求並保持通道
    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
        //如果websocket路徑和請求路徑相同,則保持通道
        if(websocketPath.equalsIgnoreCase(request.getUri())){
            ctx.fireChannelRead(request.retain());
        }else{//否則關閉通道
            ctx.close();
        }
    }

    //當請求發生錯誤時,關閉通道
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        cause.printStackTrace();
        ctx.close();
    }
}
TextWebSocketFrameHandler.java
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;

public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    //默認通道羣組
    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    //當某個通道有消息傳入時
    @Override
    protected void channelRead0(ChannelHandlerContext ctx,TextWebSocketFrame msg) {
        //(自定義操作)對其它客戶端進行消息廣播
        for (Channel channel : channels) {
            if (channel != ctx.channel()){
                channel.writeAndFlush(new TextWebSocketFrame(msg.text()));
            }
        }
    }

    //當某個通道被激活時
    @Override
    public void channelActive(ChannelHandlerContext ctx) {
        channels.add(ctx.channel());
        System.out.println("當前連接的客戶端數量:"+channels.size());
    }

    //當某個通道被銷燬時
    @Override
    public void channelInactive(ChannelHandlerContext ctx) {
        channels.remove(ctx.channel());
        System.out.println("當前連接的客戶端數量:"+channels.size());
    }

    //當某個通道發生錯誤時
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause){
        cause.printStackTrace();
        ctx.close();
    }
}

 最後通過主函數開啓Netty服務

public static void main(String[] args) {
    try {
        WebsocketServer.run(9999,"/ws");
    } catch (Exception e){
        e.printStackTrace();
    }
}

H5前端如何連接服務?

let ws = new WebSocket("ws://localhost:9999/ws");

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章