Netty实现对Websocket的支持
阅读原文时间:2023年07月09日阅读:2

一、WebSocket的简介及优势

WebSocket 是一种网络通信协议。RFC6455 定义了它的通信标准。WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

首先可以看下HTTP协议的有哪些不好的地方:HTTP 协议是一种无状态的、无连接的、单向的应用层协议。它采用了请求/响应模型。通信请求只能由客户端发起,服务端对请求做出应答处理。这种通信模型有一个弊端:HTTP 协议无法实现服务器主动向客户端发起消息。

为了解决这些痛点,WebSocket就应运而生了。WebSocket 连接允许客户端和服务器之间进行全双工通信,以便任一方都可以通过建立的连接将数据推送到另一端。WebSocket 只需要建立一次连接,就可以一直保持连接状态。

二、WebSocket客户端API

2.1 WebSocket 构造函数

   var Socket=new WebSocket("ws://localhost:20000/web");

2.2 WebSocket 属性

以下是 WebSocket 对象的属性。假定我们使用了以上代码创建了 Socket 对象:

属性

描述

Socket.readyState

只读属性 readyState 表示连接状态,可以是以下值:0 - 表示连接尚未建立。1 - 表示连接已建立,可以进行通信。2 - 表示连接正在进行关闭。3 - 表示连接已经关闭或者连接不能打开。

Socket.bufferedAmount

只读属性 bufferedAmount 已被 send() 放入正在队列中等待传输,但是还没有发出的 UTF-8 文本字节数。

2.3 WebSocket 事件

以下是 WebSocket 对象的相关事件。假定我们使用了以上代码创建了 Socket 对象:

事件

事件处理程序

描述

open

Socket.onopen

连接建立时触发

message

Socket.onmessage

客户端接收服务端数据时触发

error

Socket.onerror

通信发生错误时触发

close

Socket.onclose

连接关闭时触发

2.4 WebSocket 方法

以下是 WebSocket 对象的相关方法。假定我们使用了以上代码创建了 Socket 对象:

方法

描述

Socket.send()

使用连接发送数据

Socket.close()

关闭连接

三、WebSocket客户端代码



Netty WebSocket




应答消息


四、netty服务端代码

4.1 netty启动类

public class NettyServer {

public static void main(String\[\] args) throws Exception{  
    //服务器启动  
    new NettyServer().start(20000);  
}

public void start(int port) throws Exception{  
    //用于监听连接的线程组  
    EventLoopGroup bossGroup=new NioEventLoopGroup();  
    //用于发送接收消息的线程组  
    EventLoopGroup workGroup=new NioEventLoopGroup();

    try{  
        //启动类引导程序  
        ServerBootstrap b=new ServerBootstrap();  
        //绑定两个线程组  
        b.group(bossGroup,workGroup);  
        //设置非阻塞,用它来建立新accept的连接,用于构造serverSocketChannel的工厂类  
        b.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<SocketChannel>() {  
            @Override  
            protected void initChannel(SocketChannel channel){  
                ChannelPipeline channelPipeline=channel.pipeline();  
                // HttpServerCodec:将请求和应答消息解码为HTTP消息  
                channelPipeline.addLast(new HttpServerCodec());  
                // HttpObjectAggregator:将HTTP消息的多个部分合成一条完整的HTTP消息  
                channelPipeline.addLast(new HttpObjectAggregator(65536));  
                // ChunkedWriteHandler:向客户端发送HTML5文件  
                channelPipeline.addLast(new ChunkedWriteHandler());  
                //在管道中添加自己实现的Handler处理类  
                channelPipeline.addLast(new WebsocketServerHandler());  
            }  
        });  
        Channel channel=b.bind(port).sync().channel();  
        System.out.println("服务器启动端口:"+port);  
        channel.closeFuture().sync();  
    }finally {  
        workGroup.shutdownGracefully();  
        bossGroup.shutdownGracefully();  
    }

}  

}

4.2 netty的业务处理的Handler类

public class WebsocketServerHandler extends SimpleChannelInboundHandler {

private WebSocketServerHandshaker handshaker;

@Override  
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object o) throws Exception {  
    //传统的http接入  
    if(o instanceof FullHttpRequest){  
        handleHttpRequest(channelHandlerContext,(FullHttpRequest) o);  
    }  
    //webSocket接入  
    else if(o instanceof WebSocketFrame){  
        handleWebsocketFrame(channelHandlerContext,(WebSocketFrame) o);  
    }

}

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

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

private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req){  
    //构造握手响应返回  
    WebSocketServerHandshakerFactory webSocketServerHandshakerFactory=new WebSocketServerHandshakerFactory("ws://localhost:20000/web",null,false);  
    handshaker=webSocketServerHandshakerFactory.newHandshaker(req);  
    handshaker.handshake(ctx.channel(),req);

}

private void handleWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame){  
    //判断是否是链路关闭消息  
    if(frame instanceof CloseWebSocketFrame){  
        handshaker.close(ctx.channel(),(CloseWebSocketFrame) frame.retain());  
        return;  
    }  
    //判断是否是ping消息  
    if(frame instanceof PingWebSocketFrame){  
        ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));  
        return;  
    }  
    //文本消息处理  
    String request=((TextWebSocketFrame)frame).text();  
    System.out.println("接受的信息是:"+request);  
    String date=new Date().toString();  
    //将接收消息写回给客户端  
    ctx.channel().write(new TextWebSocketFrame("现在时刻:"+date+"发送了:"+request));  
}  

}

五、运行成功截图

参考资料:

https://www.cnblogs.com/jingmoxukong/p/7755643.html

http://www.ruanyifeng.com/blog/2017/05/websocket.html