netty 詳解(四)netty 開發 WebSocket 長連接程序


 

 

  WSServer

package com.oy.ws;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
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.stream.ChunkedWriteHandler;

public class WSServer {
    private int port;
    public WSServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) {
        new WSServer(8002).run();
    }

    public void run() {
        EventLoopGroup boss = new NioEventLoopGroup(1);
        EventLoopGroup work = new NioEventLoopGroup();

        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap
                    .group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            // 因為基於 HTTP 協議,使用 http 的編碼解碼器
                            pipeline.addLast(new HttpServerCodec());
                            // 以塊的方式寫
                            pipeline.addLast(new ChunkedWriteHandler());
                            // 因為 http 數據在傳輸過程中時分段的,HttpObjectAggregator 就可以將多個段聚合
                            pipeline.addLast(new HttpObjectAggregator(8192));
                            // websocket 數據是以幀(frame)的形式傳遞
                            // webSocketFrame 下面有六個子類
                            // WebSocketServerProtocolHandler:將 http 協議升級為 ws 協議,保持長連接
                            pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
                            pipeline.addLast(new WSServerHandler());
                        }
                    });

            // 綁定端口,啟動服務
            ChannelFuture future = bootstrap.bind(port).sync();
            System.out.println("server started and listen " + port);
            // 監聽關閉
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }
}

  

  WSServerHandler

package com.oy.ws;

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

import java.time.LocalDateTime;

/**
 * WebSocketFrame: 表示一個文本幀
 */
public class WSServerHandler 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 {
        // longText 是唯一的
        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();
    }
}

 

  hello.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <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>
<script>
    var socket;
    // 判斷當前瀏覽器是否支持 websocket
    if(window.WebSocket){
        socket = new WebSocket("ws://localhost:8002/hello");
        // 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) {
        // 先判斷 socket 是否創建好
        if (!window.socket) {
            return;
        }
        if (socket.readyState == WebSocket.OPEN) {
            // 通過 socket 發送消息
            socket.send(message);
        } else {
            alert("連接失敗");
        }
    }

</script>
</html>

 

 

測試:

  1)啟動服務端 WSServer;

  2)啟動客戶端

 

  3)客戶端發送消息給服務器,服務器返回消息給客戶端

  ---


免責聲明!

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



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