2021-11-11
历经万般红尘劫,犹如凉风轻拂面。——林清玄
今天用了这个netty-websocket-spring-boot-starter
那是相当的香啊
1 | package com.ruben.xchat.controller; |
使用注解进行配置,netty
的各种配置例如端口、主机、都可以在yml
中配置,文档就是gitee
中的md
,用来做即时通讯简直不要太香
netty-websocket-spring-boot-starter
简介
本项目帮助你在spring-boot中使用Netty来开发WebSocket服务器,并像spring-websocket的注解开发一样简单
要求
- jdk版本为1.8或1.8+
快速开始
- 添加依赖:
1
2
3
4
5 <dependency>
<groupId>org.yeauty</groupId>
<artifactId>netty-websocket-spring-boot-starter</artifactId>
<version>0.12.0</version>
</dependency>
- 在端点类上加上
@ServerEndpoint
注解,并在相应的方法上加上@BeforeHandshake
、@OnOpen
、@OnClose
、@OnError
、@OnMessage
、@OnBinary
、@OnEvent
注解,样例如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 @ServerEndpoint(path = "/ws/{arg}")
public class MyWebSocket {
@BeforeHandshake
public void handshake(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String arg, @PathVariable Map pathMap){
session.setSubprotocols("stomp");
if (!"ok".equals(req)){
System.out.println("Authentication failed!");
session.close();
}
}
@OnOpen
public void onOpen(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap reqMap, @PathVariable String arg, @PathVariable Map pathMap){
System.out.println("new connection");
System.out.println(req);
}
@OnClose
public void onClose(Session session) throws IOException {
System.out.println("one connection closed");
}
@OnError
public void onError(Session session, Throwable throwable) {
throwable.printStackTrace();
}
@OnMessage
public void onMessage(Session session, String message) {
System.out.println(message);
session.sendText("Hello Netty!");
}
@OnBinary
public void onBinary(Session session, byte[] bytes) {
for (byte b : bytes) {
System.out.println(b);
}
session.sendBinary(bytes);
}
@OnEvent
public void onEvent(Session session, Object evt) {
if (evt instanceof IdleStateEvent) {
IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
switch (idleStateEvent.state()) {
case READER_IDLE:
System.out.println("read idle");
break;
case WRITER_IDLE:
System.out.println("write idle");
break;
case ALL_IDLE:
System.out.println("all idle");
break;
default:
break;
}
}
}
}
- 打开WebSocket客户端,连接到
ws://127.0.0.1:80/ws/xxx
注解
@ServerEndpoint
当ServerEndpointExporter类通过Spring配置进行声明并被使用,它将会去扫描带有@ServerEndpoint注解的类 被注解的类将被注册成为一个WebSocket端点 所有的配置项都在这个注解的属性中 ( 如:
@ServerEndpoint("/ws")
)@BeforeHandshake
当有新的连接进入时,对该方法进行回调 注入参数的类型:Session、HttpHeaders…
@OnOpen
当有新的WebSocket连接完成时,对该方法进行回调 注入参数的类型:Session、HttpHeaders…
@OnClose
当有WebSocket连接关闭时,对该方法进行回调 注入参数的类型:Session
@OnError
当有WebSocket抛出异常时,对该方法进行回调 注入参数的类型:Session、Throwable
@OnMessage
当接收到字符串消息时,对该方法进行回调 注入参数的类型:Session、String
@OnBinary
当接收到二进制消息时,对该方法进行回调 注入参数的类型:Session、byte[]
@OnEvent
当接收到Netty的事件时,对该方法进行回调 注入参数的类型:Session、Object
配置
所有的配置项都在这个注解的属性中
属性 默认值 说明 path “/” WebSocket的path,也可以用 value
来设置host “0.0.0.0” WebSocket的host, "0.0.0.0"
即是所有本地地址port 80 WebSocket绑定端口号。如果为0,则使用随机端口(端口获取可见 多端点服务) bossLoopGroupThreads 0 bossEventLoopGroup的线程数 workerLoopGroupThreads 0 workerEventLoopGroup的线程数 useCompressionHandler false 是否添加WebSocketServerCompressionHandler到pipeline optionConnectTimeoutMillis 30000 与Netty的 ChannelOption.CONNECT_TIMEOUT_MILLIS
一致optionSoBacklog 128 与Netty的 ChannelOption.SO_BACKLOG
一致childOptionWriteSpinCount 16 与Netty的 ChannelOption.WRITE_SPIN_COUNT
一致childOptionWriteBufferHighWaterMark 64*1024 与Netty的 ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK
一致,但实际上是使用ChannelOption.WRITE_BUFFER_WATER_MARK
childOptionWriteBufferLowWaterMark 32*1024 与Netty的 ChannelOption.WRITE_BUFFER_LOW_WATER_MARK
一致,但实际上是使用ChannelOption.WRITE_BUFFER_WATER_MARK
childOptionSoRcvbuf -1(即未设置) 与Netty的 ChannelOption.SO_RCVBUF
一致childOptionSoSndbuf -1(即未设置) 与Netty的 ChannelOption.SO_SNDBUF
一致childOptionTcpNodelay true 与Netty的 ChannelOption.TCP_NODELAY
一致childOptionSoKeepalive false 与Netty的 ChannelOption.SO_KEEPALIVE
一致childOptionSoLinger -1 与Netty的 ChannelOption.SO_LINGER
一致childOptionAllowHalfClosure false 与Netty的 ChannelOption.ALLOW_HALF_CLOSURE
一致readerIdleTimeSeconds 0 与 IdleStateHandler
中的readerIdleTimeSeconds
一致,并且当它不为0时,将在pipeline
中添加IdleStateHandler
writerIdleTimeSeconds 0 与 IdleStateHandler
中的writerIdleTimeSeconds
一致,并且当它不为0时,将在pipeline
中添加IdleStateHandler
allIdleTimeSeconds 0 与 IdleStateHandler
中的allIdleTimeSeconds
一致,并且当它不为0时,将在pipeline
中添加IdleStateHandler
maxFramePayloadLength 65536 最大允许帧载荷长度 useEventExecutorGroup true 是否使用另一个线程池来执行耗时的同步业务逻辑 eventExecutorGroupThreads 16 eventExecutorGroup的线程数 sslKeyPassword “”(即未设置) 与spring-boot的 server.ssl.key-password
一致sslKeyStore “”(即未设置) 与spring-boot的 server.ssl.key-store
一致sslKeyStorePassword “”(即未设置) 与spring-boot的 server.ssl.key-store-password
一致sslKeyStoreType “”(即未设置) 与spring-boot的 server.ssl.key-store-type
一致sslTrustStore “”(即未设置) 与spring-boot的 server.ssl.trust-store
一致sslTrustStorePassword “”(即未设置) 与spring-boot的 server.ssl.trust-store-password
一致sslTrustStoreType “”(即未设置) 与spring-boot的 server.ssl.trust-store-type
一致corsOrigins {}(即未设置) 与spring-boot的 @CrossOrigin#origins
一致corsAllowCredentials “”(即未设置) 与spring-boot的 @CrossOrigin#allowCredentials
一致通过application.properties进行配置
所有参数皆可使用
${...}
占位符获取application.properties
中的配置。如下:
- 首先在
@ServerEndpoint
注解的属性中使用${...}
占位符
1
2
3
4 @ServerEndpoint(host = "${ws.host}",port = "${ws.port}")
public class MyWebSocket {
...
}
- 接下来即可在
application.properties
中配置
1
2 ws.host=0.0.0.0
ws.port=80自定义Favicon
配置favicon的方式与spring-boot中完全一致。只需将
favicon.ico
文件放到classpath的根目录下即可。如下:
1
2
3
4
5
6 src/
+- main/
+- java/
| + <source code>
+- resources/
+- favicon.ico自定义错误页面
配置自定义错误页面的方式与spring-boot中完全一致。你可以添加一个
/public/error
目录,错误页面将会是该目录下的静态页面,错误页面的文件名必须是准确的错误状态或者是一串掩码,如下:
1
2
3
4
5
6
7
8
9
10 src/
+- main/
+- java/
| + <source code>
+- resources/
+- public/
+- error/
| +- 404.html
| +- 5xx.html
+- <other public assets>多端点服务
- 在快速启动的基础上,在多个需要成为端点的类上使用
@ServerEndpoint
、@Component
注解即可- 可通过
ServerEndpointExporter.getInetSocketAddressSet()
获取所有端点的地址- 当地址不同时(即host不同或port不同),使用不同的
ServerBootstrap
实例- 当地址相同,路径(path)不同时,使用同一个
ServerBootstrap
实例- 当多个端点服务的port为0时,将使用同一个随机的端口号
- 当多个端点的port和path相同时,host不能设为
"0.0.0.0"
,因为"0.0.0.0"
意味着绑定所有的host