Netty Websocket 案例
MyServer
类
java
public class MyServer {
public static void main(String[] args) {
EventLoopGroup bossGroup = new MultiThreadIoEventLoopGroup(
NioIoHandler.newFactory()
);
EventLoopGroup workerGroup = new MultiThreadIoEventLoopGroup(
NioIoHandler.newFactory()
);
try {
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
// 基于 Http 协议,使用 http 的编码和解码器
pipeline.addLast(new HttpServerCodec());
// 以块方式写
pipeline.addLast(new ChunkedWriteHandler());
// http 数据在传输过程中是分段的,HttpObjectAggregator 可以把多个段聚合
// 浏览器发送大量数据时,会发出多次 http 请求
pipeline.addLast(new HttpObjectAggregator(8192));
// websocket 的数据是以 帧 (frame) 形式传递
// 浏览器请求 ws://localhost:8000/hello 表示请求的 uri
// WebSocketServerProtocolHandler 将 http 协议升级为 ws 协议
pipeline.addLast(new WebSocketServerProtocolHandler("/hello"));
// 自定义的 handler 处理业务逻辑
pipeline.addLast(new MyTextWebSocketFrameHandler());
}
});
ChannelFuture future = bootstrap.bind(8000).sync();
future.channel().closeFuture().sync();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
MyTextWebSocketFrameHandler
Websocket
数据是以帧的形式传输的,这里定义一个文本帧(TextWebSocketFrame
)处理器
- Netty 提供了 6 类 Websocket 帧
java
public class MyTextWebSocketFrameHandler extends
SimpleChannelInboundHandler<TextWebSocketFrame> {
@Override
protected void channelRead0(ChannelHandlerContext ctx,TextWebSocketFrame msg)
throws Exception {
System.out.println("服务器收到消息: " + msg.text());
//回复消息
ctx.channel().writeAndFlush(new TextWebSocketFrame("服务器时间 "
+ LocalDateTime.now() + " 消息: " + msg.text()));
}
//当 web 客户端连接后
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// LongText 是唯一的, ShortText 不是唯一的
System.out.println("新连接建立:" + ctx.channel().id().asLongText());
System.out.println("新连接建立" + ctx.channel().id().asShortText());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
System.out.println("出现异常");
ctx.close();
}
}
前端测试页面
html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Hello World</title>
</head>
<script>
let socket;
//判断浏览器是否支持 websocket
if (window.WebSocket) {
socket = new WebSocket("ws://localhost:8000/hello")
socket.onmessage = function (ev) {
let rt = document.getElementById("responseText");
rt.value = rt.value + "\n" + ev.data
}
socket.onopen = function () {
let rt = document.getElementById("responseText")
rt.value = "连接成功"
}
socket.onclose = function () {
let rt = document.getElementById("responseText")
rt.value = rt.value + "\n" + "连接关闭"
}
}else {
alert("您的浏览器不支持 WebSocket")
}
function send(message) {
if (socket.readyState === WebSocket.OPEN) {
socket.send(message)
}else {
alert("WebSocket 连接失败")
}
}
</script>
<body>
<form onsubmit="return false">
<textarea name="message" style="height: 300px;width: 300px"></textarea>
<input type="button" value="发送消息" onclick="send(this.form.message.value)">
<textarea id="responseText" style="height: 300px;width: 300px"></textarea>
<input type="button" value="清空内容" onclick="document.getElementById('responseText').value=''">
</form>
</body>
</html>
- 验证: 前端发送消息成功,也成功回显后端返回的消息
WebSocketServerProtocolHandler
的核心作用是将http
协议转换为ws
协议