木心

毕竟几人真得鹿,不知终日梦为鱼

导航

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)客户端发送消息给服务器,服务器返回消息给客户端

  ---

posted on 2020-05-04 03:55  wenbin_ouyang  阅读(1432)  评论(0编辑  收藏  举报