Netty 应用:WebSocket服务

考虑一个场景:如何实现网页版的聊天程序? 不同于Socket的聊天程序

在以往的http1.0,http1.1当中,是没法实现长连接的。 没出现Websocket之前,采用的是一种轮询的方式,如Comet技术

术语实时Web:利用技术和实践,使用户在信息的作者发布信息之后就能够立即收到信息,而不需要他们或者他们的软件周期性的检查信息源以获取更新。

  • Websocket是重新设计的协议,目的是为Web上的双向数据传输问题提供一个可行的解决方案,使得客户端和服务器能在任意时刻传输消息,因此要求他们异步的处理消息回调(websocket作为html5的一部分,大部分浏览器已经支持)。

下面的例子实现的功能:基于websocket协议,客户端能向服务端发送消息,服务端返回服务器的当前时间。

 

服务端实现

/**
 * Created by fubin on 2019/7/14.
 */
public class WebsocketServer {
    public static void main(String[] args) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try{
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    //增加日志handler
                    .handler(new LoggingHandler(LogLevel.INFO))
                    .childHandler(new WebsocketInitializer());

            ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8899)).sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
class WebsocketInitializer extends ChannelInitializer<SocketChannel>{
    @Override
    protected void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();

        /**
         * 处理http请求
         */
        //http编解码
        pipeline.addLast("httpCodec",new HttpServerCodec());
        //以块的方式写的处理器
        pipeline.addLast(new ChunkedWriteHandler());
        //http消息聚合 -> FullHttpRequest或FullHttoResponse
        pipeline.addLast(new HttpObjectAggregator(8192));

        /**
         * 处理websocket
         */

        //websocket握手,控制帧Frames的处理
        // ws://server:port/content_path(hellowebsocket)
        pipeline.addLast(new WebSocketServerProtocolHandler("/hellowebsocket"));
        pipeline.addLast(new TextWebsocketFrameHandler());
    }
}
/**
 * 范型使用TextWebSocketFrame,Websocket传输使用Frame来传递的
 */
class TextWebsocketFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame>{
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("收到的消息:" + msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("server发送websocket frame ,服务器时间:" + LocalDateTime.now()));
    }
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded: channel的全局id - " + ctx.channel().id().asLongText());
    }
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved: channel的全局id - " + ctx.channel().id().asLongText());
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("异常发生");
        ctx.close();
    }
}

Netty服务端提供的websocket六种frame类型

客户端实现

扫描二维码关注公众号,回复: 7991992 查看本文章

websocket需要浏览器支持

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>websocket html</title>
</head>
<body>

<script type="text/javascript">
    var socket;

    if(window.WebSocket){
        socket = new WebSocket("ws://localhost:8899/hellowebsocket");
        //客户端回调,收到服务器端发送的消息
        socket.onmessage = function (event) {
            var serverData = event.data;
            var responseTextVal = document.getElementById("responseText");
            responseTextVal.value = responseTextVal.value + serverData;
        }
        //连接打开
        socket.onopen = function (event) {
            var responseTextVal = document.getElementById("responseText");
            responseTextVal.value = "连接打开...";
        }
        socket.onclose = function (ev) {
            var responseTextVal = document.getElementById("responseText");
            responseTextVal.value = "连接断开...";
        }
    }else{
        alert('浏览器不支持websocket!');
    }
    function send(message) {
        if(!window.WebSocket){
            return;
        }
        if(socket.readyState == WebSocket.OPEN){
            socket.send(message);
        }else {
            alert("连接尚未开启!");
        }
    }
</script>

<form onsubmit="return false">
    <textarea name="message" style="width: 400px; height: 200px"></textarea>
    <input type="button" value="发送数据" onclick="send(this.form.message.value)">
    <h1>服务端输出:</h1>
    <textarea id="responseText" style="width: 400px;height: 200px;"></textarea>
    <input type="button" onclick="javascript: document.getElementById('responseText').value = ''" value="清空内容">

</form>

</body>
</html>

websocket请求头

浏览器查看websocket帧

Netty提供的处理WebSocket服务的Handler WebSocketServerProtocolHandler的API解释

这个处理程序为您运行websocket服务器做了所有繁重的工作。 它负责websocket握手以及控制框架的处理(Close,Ping,Pong)。 文本和二进制数据帧将传递给管道中的下一个处理程序(由您实现)进行处理。 有关用法,请参阅io.netty.example.http.websocketx.html5.WebSocketServer。 此处理程序的实现假定您只想运行websocket服务器而不处理其他类型的HTTP请求(如GET和POST)。 如果您希望在一台服务器中同时支持HTTP请求和websockets,请参阅io.netty.example.http.websocketx.server.WebSocketServer示例。 要知道握手完成后,您可以拦截ChannelInboundHandler.userEventTriggered(ChannelHandlerContext,Object)并检查该事件是否是WebSocketServerProtocolHandler.HandshakeComplete的实例,该事件将包含有关握手的额外信息,例如请求和所选子协议。

 

 

猜你喜欢

转载自www.cnblogs.com/fubinhnust/p/11940681.html