Netty通過WebSocket實現長連接,服務器和瀏覽器可以雙向通信


1.服務端

package netty.mydemo;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.sctp.nio.NioSctpServerChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
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.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;

/**
 *Netty通過WebSocket實現長連接,服務器和瀏覽器可以雙向通信*/
public class NettyServer {
    public static void main(String[] args)throws  Exception{
        //創建2個線程組
        EventLoopGroup boss=new NioEventLoopGroup(1);
        EventLoopGroup work=new NioEventLoopGroup();//NettyRuntime.availableProcessors() * 2) 默認創建的線程個數
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        try {
            serverBootstrap.group(boss, work);
            serverBootstrap.channel(NioServerSocketChannel.class);
            serverBootstrap.handler(new LoggingHandler(LogLevel.INFO)); //Netty提供的日志handler
            serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    //基於http協議,使用http的編碼和解碼器
                    pipeline.addLast(new HttpServerCodec());
                    //以塊方式寫,添加ChunkedWriteHandler處理器
                    pipeline.addLast(new ChunkedWriteHandler());
                    /*
                    1. http數據在傳輸過程中是分段, HttpObjectAggregator ,就是可以將多個段聚合
                    2. 這就就是為什么,當瀏覽器發送大量數據時,就會發出多次http請求
                     */
                    pipeline.addLast(new HttpObjectAggregator(7168));
                    /*
                    1. 對應websocket ,它的數據是以 幀(frame) 形式傳遞
                    2. 可以看到WebSocketFrame 下面有六個子類
                    3. 瀏覽器請求時 ws://localhost:9000/demo 表示請求的uri
                    4. WebSocketServerProtocolHandler 核心功能是將 http協議升級為 ws協議 , 保持長連接
                    5. 是通過一個 狀態碼 101
                     */
                    pipeline.addLast(new WebSocketServerProtocolHandler("/demo"));

                    //自定義的handler ,處理業務邏輯
                    pipeline.addLast(new MyTextWebSocketFrameHandler());
                }
            });

            //啟動服務器
            ChannelFuture channelFuture = serverBootstrap.bind(9000).sync();
            channelFuture.channel().closeFuture().sync();
        }finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }


}
package netty.mydemo;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;

import java.time.LocalDate;
import java.time.LocalDateTime;

public class MyTextWebSocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("服務端收到消息:"+msg.text());
        //回復消息
        ctx.channel().writeAndFlush(new TextWebSocketFrame(("服務器當前時間:"+ LocalDateTime.now()+"  "+msg.text())));
    }

    //當web客戶端連接后, 觸發方法
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded 被調用 id" + ctx.channel().id());
        //id 表示唯一的值,LongText 是唯一的 ShortText 不是唯一
        System.out.println("handlerAdded 被調用" + ctx.channel().id().asLongText());
        System.out.println("handlerAdded 被調用" + ctx.channel().id().asShortText());
    }


    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {

        System.out.println("handlerRemoved 被調用" + ctx.channel().id().asLongText());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("異常發生 " + cause.getMessage());
        ctx.close(); //關閉連接
    }
}

 

2.瀏覽器端

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<script>
    var socket;
    //判斷當前瀏覽器是否支持websocket
    if(window.WebSocket) {
        //go on
        socket = new WebSocket("ws://localhost:9000/demo");
        //相當於channelReado, ev 收到服務器端回送的消息
        socket.onmessage = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + ev.data;
        }

        //相當於連接開啟(感知到連接開啟)
        socket.onopen = function (ev) {
            var rt = document.getElementById("responseText");
            rt.value = "連接開啟了.."
        }

        //相當於連接關閉(感知到連接關閉)
        socket.onclose = function (ev) {

            var rt = document.getElementById("responseText");
            rt.value = rt.value + "\n" + "連接關閉了.."
        }
    } else {
        alert("當前瀏覽器不支持websocket")
    }

    //發送消息到服務器
    function send(message) {
        if(!window.socket) { //先判斷socket是否創建好
            return;
        }
        if(socket.readyState == WebSocket.OPEN) {
            //通過socket 發送消息
            socket.send(message)
        } else {
            alert("連接沒有開啟");
        }
    }
</script>
    <form onsubmit="return false">
        <textarea name="message" style="height: 300px; width: 300px"></textarea>
        <input type="button" value="發生消息" onclick="send(this.form.message.value)">
        <textarea id="responseText" style="height: 300px; width: 300px"></textarea>
        <input type="button" value="清空內容" onclick="document.getElementById('responseText').value=''">
    </form>
</body>
</html>

瀏覽器端

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM