Netty對WebSocket的支持(五)


Netty對WebSocket的支持(五)

一.WebSocket簡介

  在Http1.0和Http1.1協議中,我們要實現服務端主動的發送消息到網頁或者APP上,是比較困難的,尤其是現在IM(即時通信)幾乎是很多APP都需要實現的功能,我們往往采用一種輪詢的方式讓終端去請求服務器獲取對應的數據,相信很多做過IM通信的朋友應該深有感觸,其實大多數的輪詢都是無效的(即沒有獲得到任何的數據);另外一個方面,每一次輪詢都是一個完整的Http請求,而根據Http協議,每一次請求都要在Header中攜帶大量的參數,這無疑對帶寬也是一種極大的消耗。

  html5的誕生為我們帶來的WebSocket,這是一個振奮人心的事情,WebSocket是基於Http協議的一種長連接協議,有了這種協議,我們就可以實現服務端主動往客戶端發送消息的功能。有關WebSocket協議的相關信息請讀者查詢相關的文檔,在筆者的博文中不再作過多的贅述。因為我們講的是Netty, 所以今天我們就來說說Netty對WebSocket的支持。

二.Netty實現WebSocket

2.1 服務端啟動程序

public class WebsocketServer {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        
        
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                           .handler(new LoggingHandler(LogLevel.INFO))
                           .childHandler(new WebSocketChannelInitializer());
            
            ChannelFuture channelFuture = serverBootstrap.bind(8989).sync();
            channelFuture.channel().closeFuture().sync();
        }finally{
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2.2 服務端通道初始化

public class WebSocketChannelInitializer extends ChannelInitializer<SocketChannel>{

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        //HttpServerCodec: 針對http協議進行編解碼
        pipeline.addLast("httpServerCodec", new HttpServerCodec());
        //ChunkedWriteHandler分塊寫處理,文件過大會將內存撐爆
        pipeline.addLast("chunkedWriteHandler", new ChunkedWriteHandler());
        /**
         * 作用是將一個Http的消息組裝成一個完成的HttpRequest或者HttpResponse,那么具體的是什么
         * 取決於是請求還是響應, 該Handler必須放在HttpServerCodec后的后面
         */
        pipeline.addLast("httpObjectAggregator", new HttpObjectAggregator(8192));
        
        //用於處理websocket, /ws為訪問websocket時的uri
        pipeline.addLast("webSocketServerProtocolHandler", new WebSocketServerProtocolHandler("/ws"));
        
        pipeline.addLast("myWebSocketHandler", new MyWebSocketHandler());
    }
}

2.3 服務端Handler

public class WebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        Channel channel = ctx.channel();
        System.out.println(channel.remoteAddress() + ": " + msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("來自服務端: " + LocalDateTime.now()));
    }
    
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("ChannelId" + ctx.channel().id().asLongText());
    }
    
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("用戶下線: " + ctx.channel().id().asLongText());
    }
    
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.channel().close();
    }
}

2.4 網頁端的實現

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Socket</title>
        <script type="text/javascript">
            var websocket;
            
            //如果瀏覽器支持WebSocket
            if(window.WebSocket){  
                websocket = new WebSocket("ws://localhost:8989/ws");  //獲得WebSocket對象
                
                //當有消息過來的時候觸發
                websocket.onmessage = function(event){ 
                    var respMessage = document.getElementById("respMessage");
                    respMessage.value = respMessage.value + "\n" + event.data;
                }
                
                //連接關閉的時候觸發
                websocket.onclose = function(event){
                    var respMessage = document.getElementById("respMessage");
                    respMessage.value = respMessage.value + "\n斷開連接";
                }
                
                //連接打開的時候觸發
                websocket.onopen = function(event){
                    var respMessage = document.getElementById("respMessage");
                    respMessage.value = "建立連接";
                }
            }else{
                alert("瀏覽器不支持WebSocket");
            }
            
            function sendMsg(msg) { //發送消息 
                if(window.WebSocket){
                    if(websocket.readyState == WebSocket.OPEN) { //如果WebSocket是打開狀態
                        websocket.send(msg); //send()發送消息
                    }
                }else{
                    return;
                }
            }
        </script>
    </head>
<body>
    <form onsubmit="return false">
        <textarea style="width: 300px; height: 200px;" name="message"></textarea>
        <input type="button" onclick="sendMsg(this.form.message.value)" value="發送"><br>
        <h3>信息</h3>
        <textarea style="width: 300px; height: 200px;" id="respMessage"></textarea>
        <input type="button" value="清空" onclick="javascript:document.getElementById('respMessage').value = ''">
    </form>
</body>
</html>

2.5 測試

首先運行服務端啟動程序,然后打開網頁,發送“Hello world!”,如下圖:

 


免責聲明!

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



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