前言:
tcpSocket 与 websocket 的区别:
本片文章目的:
使用netty同端口监听tcpsocket和websocket消息传输。
实现思想:
在netty编程中,对于不同的消息肯定需要不同的编解码来处理,所以我们需要利用netty具有动态增删处理器handle的功能。
而从上文中我们知道websocket第一次是采用http握手的,因此debug握手请求,知道他的请求头部是GET方式。
所以我们需要根据这点来判断接收的消息是websocket还是tcpsocket。
1.判断handle如下:
/**
* 协议初始化解码器.
*
* 用来判定实际使用什么协议.</b>
*
*/
public class SocketChooseHandle extends ByteToMessageDecoder {
/** 默认暗号长度为23 */
private static final int MAX_LENGTH = 23;
/** WebSocket握手的协议前缀 */
private static final String WEBSOCKET_PREFIX = "GET /";
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
String protocol = getBufStart(in);
if (protocol.startsWith(WEBSOCKET_PREFIX)) {
SpringApplicationContextHolder.getSpringBeanForClass(PipelineAdd.class).websocketAdd(ctx);
ctx.pipeline().remove(LengthFieldBasedFrameDecoder.class);
ctx.pipeline().remove(LengthFieldPrepender.class);
ctx.pipeline().remove(BytebufToByteHandle.class);
}
in.resetReaderIndex();
ctx.pipeline().remove(this.getClass());
}
private String getBufStart(ByteBuf in){
int length = in.readableBytes();
if (length > MAX_LENGTH) {
length = MAX_LENGTH;
}
// 标记读位置
in.markReaderIndex();
byte[] content = new byte[length];
in.readBytes(content);
return new String(content);
}
}
说明:判断是否是GET /协议开始的消息,如果是,则是websocket,那么移除后面的socket编解码器,添加websocket编解码器。然后删除自己(将自身删除后,此连接之后的消息将不再进入这个handle,直接走安排好的对应handle串行处理器。而不是所有连接)。
2.接下来我们将上述handle判断器加入到消息来的编解码器前方。
实现代码如下:
// 1.socket方式服务
// 设置N秒没有读到数据,则触发一个READER_IDLE事件。
pipeline.addLast(new IdleStateHandler(readerIdleTime,0,0, TimeUnit.SECONDS));
pipeline.addLast("active",new ChannelActiveHandle());
pipeline.addLast("socketChoose",new SocketChooseHandle());
//tcpsocket编码解码handle,如果是websocket链接,会将其删除
pipeline.addLast("lengthEncode",new LengthFieldPrepender(4, false));
pipeline.addLast("lengthDecoder",new LengthFieldBasedFrameDecoder(2000, 0, 4,0, 4));
pipeline.addLast(bytebufToByteHandle);
//因为接收类型的泛型不对,所以在websocket握手的时候不会进入该handle
//此handle为最后的socket消息分解,web和tcp通用
pipeline.addLast("byteToBuf",byteToByteBufHandle);
pipeline.addLast("protocolResolve",protocolResolveHandle);
说明:
IdleStateHandler 心跳处理器
active 活跃连接处理器
socketChoose 前文所述的判断消息协议处理器
lengthEncode tcp连接的拆包粘包编码处理器(用来在消息前面附加4个字节的长度信息)
lengthDecoder tcp连接的拆包粘包解码处理器(长度判断)
bytebufToByteHandle 自定义处理器,用来将bytebuf转成我们需要的byte[]。
byteToBuf 自定义处理器,用来将要发出的消息从byte[]装成bytebuf。
protocolResolve 自定义处理器,处理消息,找到路由handle以及处理逻辑
3.websocket需要的编解码器
代码如下
public void websocketAdd(ChannelHandlerContext ctx){
// HttpServerCodec:将请求和应答消息解码为HTTP消息
ctx.pipeline().addBefore("byteToBuf","http-codec",new HttpServerCodec());
// HttpObjectAggregator:将HTTP消息的多个部分合成一条完整的HTTP消息
ctx.pipeline().addBefore("byteToBuf","aggregator",new HttpObjectAggregator(65535));
// ChunkedWriteHandler:向客户端发送HTML5文件
ctx.pipeline().addBefore("byteToBuf","http-chunked",new ChunkedWriteHandler());
ctx.pipeline().addBefore("byteToBuf","WebSocketAggregator",new WebSocketFrameAggregator(65535));
// 在管道中添加我们自己的接收数据实现方法
ctx.pipeline().addBefore("byteToBuf","ws-handShake",wsHandShakeServerHandle);
// 后续直接走消息处理
ctx.pipeline().addBefore("byteToBuf","wsPack",wsPacketHandle);
// 编码。将通用byteBuf编码成binaryWebSocketFrame.通过前面的编码器
ctx.pipeline().addBefore("byteToBuf","bufToFrame",bytebufToBinaryFrameHandle);
}
说明:
wsPacketHandle 自定义处理器,将接收的BinaryWebSocketFrame消息转成byte[]供protocolResolve处理。
bytebufToBinaryFrameHandle 自定义处理器,将需要发送的byte转成BinaryWebSocketFrame供websocket的编码器处理。
详细代码请参考nafos-network: https://gitee.com/huangxinyu/BC-NETTYGO
一个基于netty的轻量级高性能服务端框架。
nafos是一个基于netty的高性能服务器框架,其目的在于易上手,易扩展,让开发人员更致力于业务开发。 在前后端分离的web架构上,或者APP,手游,nafos都是一个很不错的选择。
除此之外,sample中也给出了超简单的扩展方案,使得nafos在分布式扩展上能更胜一筹。