websocket协议开发

     首先是服务端启动类:

@Slf4j
public class WebSocketServer {

    private static final int port = 8888;

    public void run() {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(boss, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            /**
                             *将请求和应答消息编码或者解码为Http消息
                             */
                            pipeline.addLast("http-codec", new HttpServerCodec());
                            pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
                            /**
                             *向客户端发送HTML5文件,主要用于支持浏览器和服务端进行WebSocket通信
                             */
                            pipeline.addLast("http-chunked", new ChunkedWriteHandler());
                            pipeline.addLast("handler", new WebSocketServerHandler());
                        }
                    });
            Channel channel = bootstrap.bind(port).sync().channel();
            log.info("web socket server started at port:{}", port);
            channel.closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }

    }

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

     处理器

@Slf4j
public class WebSocketServerHandler extends SimpleChannelInboundHandler<Object> {

    private WebSocketServerHandshaker handshaker;
    private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpRequest) {
            /**
             * 处理webSocket握手请求
             */
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) {
            /**
             * 处理webSocket请求
             */
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }

    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) {
        /**
         * 先判断解码是或否成功,然后判断是不是请求建立WebSocket连接
         */
        if (!request.decoderResult().isSuccess()
                || (!"websocket".equals(request.headers().get("Upgrade")))) {
            sendHttpResponse(ctx, request, new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.BAD_REQUEST));
        }
        /**
         * 创建握手处理类WebSocketServerHandshaker来构造握手响应返回客户端
         */
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://localhost:8888/websocket", null, false);
        handshaker = wsFactory.newHandshaker(request);
        if (null == handshaker) {
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            handshaker.handshake(ctx.channel(), request);
        }
    }

    /**
     * 接收的消息是已经解码的WebSocketFrame消息
     *
     * @param ctx
     * @param frame
     */
    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        /**
         *先对控制帧判断
         *判断是否为关闭链路指令
         */
        if (frame instanceof CloseWebSocketFrame) {
            handshaker.close(ctx.channel(), ((CloseWebSocketFrame) frame).retain());
            return;
        }

        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }

        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException(String.format("%s frame type not supported", frame.getClass().getName()));
        }

        String request = ((TextWebSocketFrame) frame).text();
        log.info("receive msg:{}", request);

        ctx.channel().write(new TextWebSocketFrame(request + ",欢迎使用Netty WebSocket服务" + dateTimeFormatter.format(LocalDateTime.now())));
    }

    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest request, FullHttpResponse response) {
        if (response.status().code() != HttpStatus.SC_OK) {
            ByteBuf buf = Unpooled.copiedBuffer(response.status().toString(), CharsetUtil.UTF_8);
            response.content().writeBytes(buf);
            buf.release();
            setContentLength(response, response.content().readByte());
        }
        ChannelFuture future = ctx.channel().writeAndFlush(response);
        if (!isKeepAlive(response) || response.status().code() != HttpStatus.SC_OK) {
            future.addListener(ChannelFutureListener.CLOSE);
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

  客户端页面需要通过js发起建立WebSocket的连接,然后进行通信,页面代码如下:

<!DOCTYPE html>
<html>
<head>
    <title>Netty WebSocket时间服务器</title>
    <meta name="content-type" content="text/html; charset=UTF-8">
</head>
<br>
<body>
<br>
<script type="text/javascript">
        var socket;
        if(!window.WebSocket){
            window.WebSocket=window.MozWebSocket;
        }
        if(window.WebSocket){
            socket=new WebSocket("ws://localhost:8888/webSocket");
            socket.onmessage=function(event){
                var ta=document.getElementById('responseText');
                ta.value="";
                ta.value=event.data;
            };
            socket.onopen=function(event){
                var ta=document.getElementById('responseText');
                ta.value='打开WebSocket服务器正常,浏览器支持WebSocket!';
            };
            socket.onclose=function(event){
                var ta=document.getElementById('responseText');
                ta.value='';
                ta.value="WebSocket 关闭!";
            };
        }else{
            alert("抱歉,您的浏览器不支持WebSocket协议!");
        }
        function send(message){
            if(!window.WebSocket){
                return;
            }
            if(socket!=null){
                socket.send(message);
            }else{
                alert("WebSocket连接没有建立成功,请刷新页面!");
            }
        }
    </script>
<form onsubmit="return false;">
    <input type="text" name="message" value="Netty最佳实践"/>
    <br><br>
    <input type="button" value="发送WebSocket请求消息" onclick="send(this.form.message.value)"/>
    <hr color="blue"/>
    <h3>服务端返回的应答消息</h3>
    <textarea id="responseText" style="width:500px;height:300px;"></textarea>
</form>
</body>
</html>

 

posted on 2022-04-16 19:24  溪水静幽  阅读(95)  评论(0)    收藏  举报