一丶前言
本文講述 基于springboot + netty 實(shí)現(xiàn)websocket服務(wù)端搭建。
不過(guò)這種方式不推薦使用允华, 不過(guò)可以參考看一下露久。暫時(shí)我還無(wú)法找到通過(guò)url的方式來(lái)進(jìn)行路由區(qū)分的撩荣,需要每個(gè)業(yè)務(wù)場(chǎng)景定義一個(gè)端口蚪燕。
需要了解netty的 請(qǐng)點(diǎn)擊網(wǎng)址查看我總結(jié)的一些netty的介紹
netty介紹地址:還沒(méi)完善后邊不上
netty-websocket-spring-boot-starter實(shí)現(xiàn)方式地址: https://blog.csdn.net/qq825478739/article/details/126894239
二丶快速開(kāi)始
1. pom 坐標(biāo)
引入 netty pom坐標(biāo)
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.45.Final</version>
</dependency>
2. 創(chuàng)建 Handler 處理器
這里創(chuàng)建 Netty Handler 處理器 這就不用我說(shuō)了把
@Slf4j
public class MessageNettyHandler extends SimpleChannelInboundHandler {
private RedisService<String> redisService;
private String userId;
public MessageNettyHandler(RedisService<String> redisService) {
this.redisService = redisService;
}
/**
* 讀取消息
*
* @param channelHandlerContext
* @param json
* @throws Exception
*/
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object json) throws Exception {
//緩存每個(gè)連接
if (json instanceof TextWebSocketFrame) {//正常的數(shù)據(jù)
try {
TextWebSocketFrame textWebSocketFrame = (TextWebSocketFrame) json;
log.info("MessageNettyHandler 客戶(hù)端消息:{}", textWebSocketFrame.text());
textWebSocketFrame = new TextWebSocketFrame("success");
//發(fā)送消息
channelHandlerContext.writeAndFlush(textWebSocketFrame);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 客戶(hù)端連接成功
*
* @param ctx
* @throws Exception
*/
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
log.info("MessageNettyHandler 客戶(hù)端連接成功!");
TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame("success");
//發(fā)送消息
ctx.writeAndFlush(textWebSocketFrame);
}
/**
* 客戶(hù)端斷開(kāi)連接
*
* @param ctx
* @throws Exception
*/
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
log.info("MessageNettyHandler 客戶(hù)端斷開(kāi)連接锦募!");
// 獲取過(guò)程中如果channelGroup長(zhǎng)度都為0 會(huì)刪除緩存
ctx.channel().close();
}
}
3. 啟動(dòng)springboot 創(chuàng)建Netty 鏈接
我這里使用 ApplicationRunner springboot 啟動(dòng)成功后在創(chuàng)建Netty websocket 服務(wù)端摆屯。
/**
* websocker 監(jiān)聽(tīng)服務(wù)
*
* @Author su
* @Date 2022/5/15 13:15
*/
@Slf4j
@Configuration
public class WebSocketRunner implements ApplicationRunner {
@Autowired
private RedisService redisService;
@Override
public void run(ApplicationArguments args) throws Exception {
NioEventLoopGroup bossGroup = new NioEventLoopGroup(2);
NioEventLoopGroup workGroup = new NioEventLoopGroup(4);
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
//端口
.localAddress(new InetSocketAddress(8081))
.option(ChannelOption.SO_BACKLOG, 1024)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ChannelPipeline pipeline = socketChannel.pipeline();
//解碼
pipeline.addLast(new HttpServerCodec());
// 添加一個(gè)聚合器,這個(gè)聚合器主要是將HttpMessage聚合成FullHttpRequest/Response
pipeline.addLast(new HttpObjectAggregator(65536));
pipeline.addLast(new WebSocketServerProtocolHandler("/ws"));
//自定義Hander處理器
pipeline.addLast(new MessageNettyHandler(redisService));
}
});
serverBootstrap.bind().sync().channel();
log.info("netty server startStructRecord state:success, port:{}", 8081);
}
}
4. html 連接demo
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<div style="width:400px;margin:20px auto;border:1px solid lightgray;padding:20px;text-align:center;">
當(dāng)前價(jià)格為:¥<span style="color:#ff4719" id="price">10000</span>
</div>
</body>
<script type="text/javascript">
var websocket=null;
//判斷瀏覽器是否支持websocket
if('WebSocket' in window){
websocket=new WebSocket("ws://127.0.0.1:8081/ws");
websocket.onopen=function(){
websocket.send("測(cè)試客戶(hù)端連接成功");
}
websocket.onerror=function(){
websocket.send("測(cè)試客戶(hù)端連接失敗");
}
websocket.onclose=function(){
websocket.send("測(cè)試客戶(hù)端連接關(guān)閉");
}
websocket.onmessage=function(e){
console.log("onmessage-----------------",e)
send(e.data);
}
//監(jiān)聽(tīng)窗口關(guān)閉事件糠亩,當(dāng)窗口關(guān)閉時(shí)虐骑,主動(dòng)去關(guān)閉websocket連接,防止連接還沒(méi)斷開(kāi)就關(guān)閉窗口赎线,server端會(huì)拋異常廷没。
window.onbeforeunload = function () {
closeWebSocket();
}
}
else {
alert('當(dāng)前瀏覽器 Not support websocket')
}
//將消息顯示在網(wǎng)頁(yè)上
function send(e) {
document.getElementById('price').innerHTML =e;
}
//關(guān)閉WebSocket連接
function closeWebSocket() {
websocket.close();
}
</script>
</html>
5. 啟動(dòng)驗(yàn)證
鏈接成功
三丶總結(jié)
使用Netty手動(dòng)創(chuàng)建 websocket 服務(wù)端 這種方式還是有點(diǎn)生硬的。不推薦這種 垂寥。
我個(gè)人哈颠黎,搗鼓半天沒(méi)有實(shí)現(xiàn) websocket 那種通過(guò) url 路徑來(lái) 作為路由轉(zhuǎn)發(fā)的那種方式。有知道方式的可以評(píng)論下, 我也想知道怎么搞滞项。
最后發(fā)現(xiàn)可以可以使用netty-websocket-spring-boot-starter 來(lái)實(shí)現(xiàn)與 spring-boot-starter-websocket 的方式websocket 開(kāi)發(fā)方式盏缤。
netty-websocket-spring-boot-starter 實(shí)現(xiàn)websocket 地址:https://blog.csdn.net/qq825478739/article/details/126894239