WebSocket 协议介绍
WebSocket 协议是一种在单个 TCP 连接上进行全双工通信的协议,在建立连接完成握手阶段后,服务端也可以主动推送数据给客户端,使得 Web 浏览器和服务器之间的交互性更强大。
目前 WebSocket 协议应用非常广泛,大部分浏览器均已支持 WebSocket,不仅仅在 Web 应用中,其他很多类型应用(例如游戏)也经常用到 WebSocket 协议。
WebSocket 建立连接的过程
WebSocket 分为握手阶段( handshake )和数据传输阶段( data transfer )。
握手阶段( handshake )
在客户端和服务器建立 WebSocket 连接之前,客户端首先要发送一个 HTTP 协议的握手请求:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
其中请求头 Connection: Upgrade
和 Upgrade: websocket
表示客户端想要升级协议为 WebSocket。服务器进行如下响应完成握手:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
完成握手后,接下来就是双向的数据传输的过程。
数据传输阶段( data transfer )
数据传输阶段传输的内容以帧( frame )为单位,其中分为控制帧(Control Frame)和数据帧(Data Frame):
- 控制帧(Control Frame):包括
Close
、Ping
、Pong
帧,Close
用于关闭 WebSocket 连接,Ping
和Pong
用于心跳检测 - 数据帧(Data Frame):包括
Text
和Binary
帧,分别用于传输文本和二进制数据
Netty 实现 WebSocket 服务器
public class WebSocketServer {
public static void main(String[] args) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec()); // HTTP 协议解析,用于握手阶段
pipeline.addLast(new HttpObjectAggregator(65536)); // HTTP 协议解析,用于握手阶段
pipeline.addLast(new WebSocketServerCompressionHandler()); // WebSocket 数据压缩扩展
pipeline.addLast(new WebSocketServerProtocolHandler("/", null, true)); // WebSocket 握手、控制帧处理
pipeline.addLast(new MyWebSocketServerHandler());
}
});
ChannelFuture f = b.bind(8080).sync();
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
class MyWebSocketServerHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception {
if (frame instanceof TextWebSocketFrame) { // 此处仅处理 Text Frame
String request = ((TextWebSocketFrame) frame).text();
ctx.channel().writeAndFlush(new TextWebSocketFrame("收到: " + request));
}
}
}
以上是 Netty 实现的一个简单的 WebSocket 的服务器。启动成功后,可以网上搜索 WebSocket 在线测试工具连接 ws://localhost:8080/ 进行测试。
源代码分析
WebSocketServerProtocolHandler
会帮我们处理握手、Close
、Ping
、Pong
帧等 WebSocket 协议底层,并且将 Text
和 Binary
数据帧传递给 pipeline 中下一个 handler。也就是在下一个 handler 中,我们只需要实现业务逻辑而无需关注 WebSocket 协议本身的细节。
在 WebSocketServerProtocolHandler.java
216 行代码中,会在 pipeline 中添加一个 WebSocketServerProtocolHandshakeHandler
,用于处理握手阶段。具体代码位置:
https://github.com/netty/nett...
WebSocketServerProtocolHandshakeHandler
处理握手请求并响应,同时它会将自身从 pipeline 中移除,因为握手在建立 TCP 连接后仅需要处理一次。具体代码位置:
https://github.com/netty/nett...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。