netty5实现websocket协议
·
一、websocket的基本准备工作和netty的内置类
1、按照maven依赖
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>5.0.0.Alpha2</version>
</dependency>
2、websocket的内置数据帧类
数据帧的基类是WebSocketFrame
2.1 BinaryWebSocketFrame代表二进制数据帧
2.2 TextWebSocketFrame代表文本数据的数据帧
2.3 CloseWebSocketFrame代表一个结束请求,属于控制帧
2.4 ContinuationWebSocketFrame:当发送的数据内容多于一个帧时,将消息拆分为多个WebSocketFrame数据帧进行发送,而此类数据帧专用于发送剩余的内容。
2.5 PingWebSocketFrame和PongWebSocketFrame:是心跳帧,服务器通过PingWebSocketFrame发送到客户端,客户端通过PongWebSocketFrame进行响应。
3、netty内置的handler处理器
3.1 WebSocketServerProtocolHandler:websocket服务的协议处理器,用于进行握手升级操作。在通信过程中先通过握手(http协议),然后在进行升级到websocket协议
3.2 WebSocketFrameEncoder和WebSocketFrameDecoder:websocket协议的编码器和解码器。
二、测试代码实战
1、新建WebSocketParamHandler类,用于处理地址栏参数的
package com.example.netty_project.websocketService;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.util.AttributeKey;
import java.util.List;
import java.util.Map;
@ChannelHandler.Sharable
public class WebSocketParamHandler extends ChannelInboundHandlerAdapter {
// 定义用于在 Channel 属性中存储参数的 Key
public static final AttributeKey<String> USER_ID_KEY = AttributeKey.valueOf("userId");
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
if (msg instanceof FullHttpRequest) {
FullHttpRequest request = (FullHttpRequest) msg;
String uri = request.uri();
// 使用 Netty 的 QueryStringDecoder 解析 URI
QueryStringDecoder queryDecoder = new QueryStringDecoder(uri);
Map<String, List<String>> parameters = queryDecoder.parameters();
// 获取特定参数,例如 token 和 userId
String userId = getFirstValue(parameters, "userId");
if (userId != null) {
ctx.channel().attr(USER_ID_KEY).set(userId);
}
// 重要:将消息传递给下一个 Handler
ctx.fireChannelRead(msg);
// 可选:此 Handler 仅用于握手阶段,可将其从 pipeline 中移除
ctx.pipeline().remove(this);
} else {
ctx.fireChannelRead(msg);
}
}
private String getFirstValue(Map<String, List<String>> parameters, String key) {
List<String> values = parameters.get(key);
if (values != null && !values.isEmpty()) {
return values.get(0);
}
return null;
}
}
2、新建TextWebSocketFrameHandler,文本数据业务处理器(核心部分)
package com.example.netty_project.websocketService;
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.handler.codec.http.websocketx.WebSocketFrame;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import lombok.extern.slf4j.Slf4j;
@ChannelHandler.Sharable
@Slf4j
public class TextWebSocketFrameHandler extends SimpleChannelInboundHandler<WebSocketFrame> {
@Override
protected void messageReceived(ChannelHandlerContext channelHandlerContext, WebSocketFrame webSocketFrame) throws Exception {
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object frame) throws Exception {
if(frame instanceof TextWebSocketFrame webSocketFrame){
String request=webSocketFrame.text();
log.info("收到消息:{}",request);
log.warn("获取地址栏参数");
log.warn(ctx.channel().attr(WebSocketParamHandler.USER_ID_KEY).get());
ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器收到消息:"+request));
}else {
throw new UnsupportedOperationException("不支持的消息类型");
}
}
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
WebSocketServerProtocolHandler.ServerHandshakeStateEvent handshakeComplete = WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE;
if(evt.equals(handshakeComplete)){
//判断是否是握手事件
log.error("握手成功");
}
super.userEventTriggered(ctx, evt);
}
}
3、新建引导类进行启动
package com.example.netty_project.websocketService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
public class WebsocketMain {
public static void main(String[] args) {
ChannelInitializer initializer = new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new HttpServerCodec())
.addLast(new HttpObjectAggregator(65535))
.addLast(new WebSocketParamHandler())
.addLast(new WebSocketServerProtocolHandler("/ws"))
.addLast(new TextWebSocketFrameHandler());
}
};
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(initializer);
Channel ch = serverBootstrap.bind(18899).sync().channel();
ch.closeFuture().sync();
} catch (Exception ex) {
ex.printStackTrace();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
更多推荐


所有评论(0)