四、Netty实现webSocket,实现服务器与浏览器(HTML)在线聊天功能

版权声明:如需转载,请标明转载出处! https://blog.csdn.net/Z0157/article/details/82829190

        由于http协议有一个缺陷:通信只能由客户端发起。如果服务器有连续的状态变化,客户端要获知只能使用"轮询":每隔一段时候,就发出一个询问,了解服务器有没有新的信息。轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开)。
而websocket就这样应用而生。

        浏览器与服务器之间的长连接,是websocket的支持。WebSocket是一种在单个TCP连接上进行全双工通信的协议。而netty他就对websocket通信做了支持。

websocket除了上面的支持,还有一些其他的特点:
(1)建立在 TCP 协议之上。
(2)与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议。
(3)数据格式比较轻量,性能开销小,通信高效。
(4)可以发送文本,二进制数据。
(5)客户端可以与任意服务器通信。
(6)协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。

下面我用netty对websocket的支持来实现一个完整了实现一个在线聊天功能:

服务器端的代码实现:

package com.zhurong.netty.test5;

import com.zhurong.netty.test4.NettyServerInitializer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;

import java.net.InetSocketAddress;

/**
 * Description:
 * User: zhurong
 * Date: 2018-09-24  23:17
 */
public class NettyWebSocketServer {

    public static void main(String[] args) {
        //接收连接
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //连接发送给work
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            System.out.println("服务器启动成功!");
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).
                    handler(new LoggingHandler(LogLevel.INFO)).
                    childHandler(new WebSocketChannelInitalizer());
            ChannelFuture channelFuture = serverBootstrap.bind(new InetSocketAddress(8000)).sync();
            channelFuture.channel().closeFuture().sync();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }

    }

}
package com.zhurong.netty.test5;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
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;

/**
 * Description:
 * User: zhurong
 * Date: 2018-09-24  11:05
 */
public class WebSocketChannelInitalizer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();

        pipeline.addLast(new HttpServerCodec());
        pipeline.addLast(new ChunkedWriteHandler());
        pipeline.addLast(new HttpObjectAggregator(8192));
        pipeline.addLast(new WebSocketServerProtocolHandler("/test"));

        pipeline.addLast(new WebSockethandler());
    }
}
package com.zhurong.netty.test5;

import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.EventExecutorGroup;

import java.util.Date;

/**
 * Description:
 * User: zhurong
 * Date: 2018-09-24  11:15
 */
public class WebSockethandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        System.out.println("收到浏览器的消息:"+ msg.text());
        ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器返回消息:" + new Date()));
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerAdded");
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        System.out.println("handlerRemoved");

    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        System.out.println("exceptionCaught");
    }
}

webapp端的代码:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>webSocketTest客户端</title>
</head>
<script type="text/javascript">
    var webSocket;
    if(window.WebSocket){
        webSocket = new WebSocket("ws://localhost:8000/test");
        //客户端收到服务器的方法,这个方法就会被回调
        webSocket.onmessage = function (ev) {
            var contents = document.getElementById("responseText");
            contents.value = contents.value +"\n"+ ev.data;
        }

        webSocket.onopen = function (ev) {
            var contents = document.getElementById("responseText");
            contents.value = "与服务器端的websocket连接建立";
        }
        webSocket.onclose = function (ev) {
            var contents = document.getElementById("responseText");
            contents.value =  contents.value +"\n"+ "与服务器端的websocket连接断开";
        }
    }else{
        alert("该环境不支持websocket")
    }

    function sendMessage() {
        if(window.webSocket){
            if(webSocket.readyState == WebSocket.OPEN){
                var contents = document.getElementById("message").value;
                webSocket.send(contents);
            }else{
                alert("与服务器连接尚未建立")
            }
        }
    }
</script>
<body>
    <form onsubmit="return false;">
        <h4>客户端输入:</h4>
        <textarea id = "message" name="message" style="width: 200px;height: 100px"></textarea>
        <br/>
        <input type="button" value="发送到服务器" onclick="sendMessage()">

        <h4>服务器返回消息:</h4>
        <textarea id = "responseText" name="message" style="width: 200px;height: 100px"></textarea>
        <br/>
        <input type="button" onclick="javascript:document.getElementById('responseText').value=''" value="clear data">
    </form>
</body>
</html>

服务器输出:

这就是一个完整的websocket功能,当然我们其实客户端直接使用浏览器http就可以了,这样更方便。

猜你喜欢

转载自blog.csdn.net/Z0157/article/details/82829190