日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Netty高级进阶之基于Netty的Websocket开发网页聊天室

發(fā)布時間:2023/12/18 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Netty高级进阶之基于Netty的Websocket开发网页聊天室 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

本通過實戰(zhàn)演練,學(xué)習(xí)了如何基于Netty的websocket開發(fā)一個網(wǎng)頁聊天室。

Netty高級進(jìn)階之基于Netty的Websocket開發(fā)網(wǎng)頁聊天室

Webdocket簡介

Websockt是一種在單個TCP連接上進(jìn)行全雙工通信的協(xié)議。

Websocket使客戶端和服務(wù)端的數(shù)據(jù)交互變得簡單,允許服務(wù)器主動向客戶端推送數(shù)據(jù)

在Websocket API中,客戶端只需要與服務(wù)器完成一次握手,兩者之間就可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。

他的應(yīng)用場景如下:

  • 社交訂閱
  • 協(xié)同編輯/編程
  • 股票基金報價
  • 體育實況更新
  • 多媒體聊天
  • 在線教育

Websocket和HTTP的區(qū)別

HTTP協(xié)議是應(yīng)用層的協(xié)議,是基于TCP協(xié)議的。

HTTP協(xié)議必須經(jīng)過三次握手才能發(fā)送消息。

HTTP連接分為短連接和長鏈接。短連接是每次都要經(jīng)過三次握手才能發(fā)送消息。就是說每一個request對應(yīng)一個response。長連接在一定期限內(nèi)保持TCP連接不斷開。

客戶端與服務(wù)器通信,必須由客戶端先發(fā)起,然后服務(wù)端返回結(jié)果。客戶端是主動的,服務(wù)端是被動的。

客戶端想要實時獲取服務(wù)端的消息,就要不斷發(fā)送長連接到服務(wù)端。

Websocket實現(xiàn)了多路復(fù)用,它是全雙工通信。在Websocket協(xié)議下,服務(wù)端和客戶端可以同時發(fā)送消息。

建立了Websocket連接之后,服務(wù)端可以主動給客戶端發(fā)送消息。信息中不必帶有header的部分信息,與HTTP長連接通信對比,這種方式降低了服務(wù)器的壓力,信息當(dāng)中也減少了多余的信息

導(dǎo)入基礎(chǔ)環(huán)境

  • 新建netty-springboot項目

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zt5trjYz-1651644006392)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220430225632849.png)]

  • 導(dǎo)入依賴模塊

    <dependencies><!-- 模板引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- web模塊 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency> </dependencies>
  • 導(dǎo)入靜態(tài)資源

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-uiAqciq1-1651644006394)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220430230249496.png)]

  • 配置yaml

    server:port: 8080 resources:static-locations:- classpath:/static/ spring:thymeleaf:cache: falsechecktemplatelocation: trueenabled: trueencoding: UTF-8mode: HTMLprefix: classpath:/templates/suffix: .html
  • 關(guān)于Springboot整合thymeleaf的404問題,參考:

    /post/404-problem-with-springboot-configuration-thymeleaf.html

    代碼實現(xiàn)

    服務(wù)端開發(fā)

  • 添加Netty相關(guān)依賴

    <!--引入netty依賴 --> <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId> </dependency>
  • Netty相關(guān)配置

    netty:port: 8081ip: 127.0.0.1path: /chat
  • Netty配置類

    /*** Netty配置類** @name: NettyConfig* @author: terwer* @date: 2022-05-01 00:04**/ @Component @Data @ConfigurationProperties(prefix = "netty") public class NettyConfig {// netty監(jiān)聽端口private int port;// webdocket訪問路徑private String path; }
  • Netty的WebsocketServer開發(fā)

    /*** Netty的Websocket服務(wù)器** @name: NettyWebsocketServer* @author: terwer* @date: 2022-05-01 00:11**/ @Component public class NettyWebsocketServer implements Runnable {@Autowiredprivate NettyConfig nettyConfig;@Autowiredprivate WebsocketChannelInit websocketChannelInit;private NioEventLoopGroup bossGroup = new NioEventLoopGroup(1);private NioEventLoopGroup workerGroup = new NioEventLoopGroup();@Overridepublic void run() {try {// 1.創(chuàng)建服務(wù)端啟動助手ServerBootstrap serverBootstrap = new ServerBootstrap();// 2.設(shè)置線程組serverBootstrap.group(bossGroup, workerGroup);// 3.設(shè)置參數(shù)serverBootstrap.channel(NioServerSocketChannel.class).handler(new LoggingHandler(LogLevel.DEBUG)).childHandler(websocketChannelInit);// 4.啟動服務(wù)端ChannelFuture channelFuture = serverBootstrap.bind(nettyConfig.getPort()).sync();System.out.println("------Netty服務(wù)端啟動成功------");channelFuture.channel().closeFuture().sync();} catch (InterruptedException e) {e.printStackTrace();bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();throw new RuntimeException(e);} finally {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();}}/*** 關(guān)閉資源-容器銷毀時候關(guān)閉*/@PreDestroypublic void close() {bossGroup.shutdownGracefully();workerGroup.shutdownGracefully();} }
  • 通道初始化對象

    /*** 通道初始化對象** @name: WebsocketChannelInit* @author: terwer* @date: 2022-05-01 23:11**/ @Component public class WebsocketChannelInit extends ChannelInitializer {@Autowiredprivate NettyConfig nettyConfig;@Autowiredprivate WebsocketHandler websocketHandler;@Overrideprotected void initChannel(Channel channel) throws Exception {ChannelPipeline pipeline = channel.pipeline();// 對HTTP協(xié)議的支持pipeline.addLast(new HttpServerCodec());// 對大數(shù)據(jù)流的支持pipeline.addLast(new ChunkedWriteHandler());// post請求分為三個部分:request line/request header/message body// 對POST請求的支持,將多個信息轉(zhuǎn)化成單一的request/response對象pipeline.addLast(new HttpObjectAggregator(8000));// 對WebSocket協(xié)議的支持// 將http協(xié)議升級為ws協(xié)議pipeline.addLast(new WebSocketServerProtocolHandler(nettyConfig.getPath()));// 自定義處理handlerpipeline.addLast(websocketHandler);} }
  • 處理對象

    /*** 自定義Websocket處理類* Websocket數(shù)據(jù)以幀的形式進(jìn)行處理* 需要設(shè)置通道共享** @name: WebsocketHandler* @author: terwer* @date: 2022-05-01 23:21**/ @Component @ChannelHandler.Sharable public class WebsocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {public static List<Channel> channelList = new ArrayList<>();/*** 通道就緒事件** @param ctx* @throws Exception*/@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception {Channel channel = ctx.channel();// 有客戶端連接時,將通道放入集合channelList.add(channel);System.out.println("有新的鏈接");}/*** 通道未就緒** @param ctx* @throws Exception*/@Overridepublic void channelInactive(ChannelHandlerContext ctx) throws Exception {// channel下線Channel channel = ctx.channel();// 客戶端連接端口,移除連接channelList.remove(channel);System.out.println("連接斷開");}/*** 通道讀取事件** @param ctx* @param textWebSocketFrame* @throws Exception*/@Overrideprotected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) throws Exception {String msg = textWebSocketFrame.text();System.out.println("接收到消息:" + msg);// 當(dāng)前發(fā)送消息的通道Channel channel = ctx.channel();for (Channel channel1 : channelList) {// 排除自身通道if (channel != channel1) {channel1.writeAndFlush(new TextWebSocketFrame(msg));}}}/*** 異常處理事件** @param ctx* @param cause* @throws Exception*/@Overridepublic void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {cause.printStackTrace();Channel channel = ctx.channel();System.out.println("消息發(fā)送異常。");// 移除channelList.remove(channel);} }

    注意:處理類需要設(shè)置成共享的

  • 啟動類

    @SpringBootApplication public class NettySpringbootApplication implements CommandLineRunner {@Autowiredprivate NettyWebsocketServer nettyWebsocketServer;public static void main(String[] args) {SpringApplication.run(NettySpringbootApplication.class, args);}@Overridepublic void run(String... args) throws Exception {new Thread(nettyWebsocketServer).start();} }
  • 前端js開發(fā)

    • 建立連接

      var ws = new WebSocket("ws://localhost:8081/chat"); ws.onopen = function () {console.log("連接成功") }
    • 發(fā)送消息

      function sendMsg() {var message = $("#my_test").val();$("#msg_list").append(`<li class="active"}><div class="main self"><div class="text">` + message + `</div></div></li>`);$("#my_test").val('');//發(fā)送消息message = username + ":" + message;ws.send(message);// 置底setBottom(); }
    • 接收消息

      ws.onmessage = function (evt) {showMessage(evt.data); }function showMessage(message) {// 張三:你好var str = message.split(":");$("#msg_list").append(`<li class="active"}><div class="main"><img class="avatar" width="30" height="30" src="/img/user.png"><div><div class="user_name">${str[0]}</div><div class="text">${str[1]}</div></div> </div></li>`);// 置底setBottom(); }
    • 關(guān)閉與錯誤處理

      ws.onclose = function (){console.log("連接關(guān)閉") }ws.onerror = function (){console.log("連接異常") }

    運行效果

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4KaNmXdr-1651644006395)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220502001145851.png)]

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-oZW1k4P5-1651644006395)(…/…/…/…/…/…/…/Library/Application Support/typora-user-images/image-20220502001159603.png)]

    [外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-d5woRo7z-1651644006396)(https://cdn.jsdelivr.net/gh/terwer/upload/img/image-20220502001220081.png)]

    總結(jié)

    以上是生活随笔為你收集整理的Netty高级进阶之基于Netty的Websocket开发网页聊天室的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。