一、说在前面的话
本文是上一篇文章的补充,所以很多背景以及过程就不再赘述。
上一篇是把错误信息通过onMessage事件,在和客户端对接的时候发现,每次都要判断响应报文,对现有的逻辑改动较大。
其实,我们本次的需求是在创建ws长连接的时候,把错误提示信息给客户端。
所以本文是介绍,如何把错误信息给到onError事件里。
二、先看效果
返回的是CloseWebSocketFrame,状态码和原因给到172.27.28.90客户端。
- 这里的抓包,过滤了ping和pong包。
- 从上面的Info可以看出,它是一个Connection Close 包,注意不是Text
三、源码实现
synchronized (this) {
status = this.resolveUserInfo(auth, deviceId, ctx);
}
if (status.equals(ResolveStatus.FAIL)) {
return;
}
// 先建立握手
this.handleHttpRequest(ctx, (FullHttpRequest) msg);
if (ResolveStatus.TEMP.equals(status)) {
this.sendRepeatConnectMessage(ctx);
return;
}
- 这里和上一个版本的不同在于,不是抛自定义的运行时异常,而是直接返回报文。
- 注意,还是要先建立握手,再发送提示信息–不能跳过握手。因为它非常重要,所以再次贴下代码。
WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(getWebSocketLocation(req), null
, false, 65536 * commonConfig.getMaxWebsocketLengthTimes());
handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
} else {
//把握手消息返回给客户端
handshaker.handshake(ctx.channel(), req);
}
下面贴出发送错误提示的代码
private void sendRepeatConnectMessage(ChannelHandlerContext ctx) {
if (ctx.channel().isActive()) {
ctx.channel().writeAndFlush(new CloseWebSocketFrame(
4001, "repeat connect channel"))
.addListener(ChannelFutureListener.CLOSE);
}
}
- 这里的状态码定义为4001,请见下图。
顺便讲一句,上一篇文章中就返回的是1006状态码。
-
4000~4999 是可以为我们应用的自定义状态码范围
-
这里我们使用的是CloseWebSocketFrame,还有其他的见下
byte opcode;
if (msg instanceof TextWebSocketFrame) {
opcode = OPCODE_TEXT;
} else if (msg instanceof PingWebSocketFrame) {
opcode = OPCODE_PING;
} else if (msg instanceof PongWebSocketFrame) {
opcode = OPCODE_PONG;
} else if (msg instanceof CloseWebSocketFrame) {
opcode = OPCODE_CLOSE;
} else if (msg instanceof BinaryWebSocketFrame) {
opcode = OPCODE_BINARY;
} else if (msg instanceof ContinuationWebSocketFrame) {
opcode = OPCODE_CONT;
} else {
throw new UnsupportedOperationException("Cannot encode frame of type: " + msg.getClass().getName());
}
- ChannelFutureListener的源码可以看出,在发送消息的同时,会关闭连接。
public interface ChannelFutureListener extends GenericFutureListener<ChannelFuture> {
/**
* A {@link ChannelFutureListener} that closes the {@link Channel} which is
* associated with the specified {@link ChannelFuture}.
*/
ChannelFutureListener CLOSE = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
future.channel().close();
}
};
/**
* A {@link ChannelFutureListener} that closes the {@link Channel} when the
* operation ended up with a failure or cancellation rather than a success.
*/
ChannelFutureListener CLOSE_ON_FAILURE = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
future.channel().close();
}
}
};
/**
* A {@link ChannelFutureListener} that forwards the {@link Throwable} of the {@link ChannelFuture} into the
* {@link ChannelPipeline}. This mimics the old behavior of Netty 3.
*/
ChannelFutureListener FIRE_EXCEPTION_ON_FAILURE = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) {
if (!future.isSuccess()) {
future.channel().pipeline().fireExceptionCaught(future.cause());
}
}
};
// Just a type alias
}