前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >netty-websocket

netty-websocket

作者头像
阿超
发布2022-08-17 21:34:45
7850
发布2022-08-17 21:34:45
举报
文章被收录于专栏:快乐阿超快乐阿超

历经万般红尘劫,犹如凉风轻拂面。——林清玄

今天用了这个netty-websocket-spring-boot-starter

那是相当的香啊

代码语言:javascript
复制
package com.ruben.xchat.controller;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.alibaba.fastjson.JSON;
import com.ruben.xchat.pojo.to.ChatTransferObject;
import com.ruben.xchat.service.WebSocketService;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.yeauty.annotation.*;
import org.yeauty.pojo.Session;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * WebSocket控制层
 * https://gitee.com/Yeauty/netty-websocket-spring-boot-starter
 *
 * @author <achao1441470436@gmail.com>
 * @since 2021/11/11 9:14
 */
@Slf4j
@Component
@ServerEndpoint(path = "${ws.path}", port = "${ws.port}")
public class WebSocketController {

    @Autowired
    private WebSocketService webSocketService;

    /**
     * 建立会话之前
     *
     * @param session 会话
     * @param headers 请求头
     * @param req     请求参数
     * @param reqMap  请求参数
     * @param arg     路径上的参数
     * @param pathMap 路径上的参数
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:18
     */
    @BeforeHandshake
    public void handshake(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap<String, List<Object>> reqMap, @PathVariable String arg, @PathVariable Map<String, Object> pathMap) {
        session.setSubprotocols("stomp");
        // 进行认证
        try {
            webSocketService.verifyLogin(headers);
        } catch (Exception e) {
            session.close();
            ExceptionUtil.wrapAndThrow(e);
        }
    }


    /**
     * 当有新的WebSocket连接完成时
     *
     * @param session 会话
     * @param headers 请求头
     * @param req     请求参数
     * @param reqMap  请求参数
     * @param arg     路径上的参数
     * @param pathMap 路径上的参数
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:21
     */
    @OnOpen
    public void onOpen(Session session, HttpHeaders headers, @RequestParam String req, @RequestParam MultiValueMap<String, List<Object>> reqMap, @PathVariable String arg, @PathVariable Map<String, Object> pathMap) {
        log.info("new connection");
        webSocketService.registerSession(headers, session);
    }

    /**
     * 连接关闭
     *
     * @param session 会话
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:22
     */
    @OnClose
    public void onClose(Session session) throws IOException {
        log.info("one connection closed");
        webSocketService.removeSession(session);
    }

    /**
     * 连接发生异常
     *
     * @param session   会话
     * @param throwable 异常
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:22
     */
    @OnError
    public void onError(Session session, Throwable throwable) {
        log.error("Connection error happened.", throwable);
        webSocketService.removeSession(session);
    }

    /**
     * 收到消息
     *
     * @param session 会话
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:22
     */
    @OnMessage
    public void onMessage(Session session, String message) {
        System.out.println(message);
        ChatTransferObject chat = JSON.parseObject(message, ChatTransferObject.class);
        switch (chat.getChatToType()) {
            case CHAT_ONE:
                log.info("发送到个人:{}", chat);
                webSocketService.sendSomeone(session, chat);
                break;
            case CHAT_GROUP:
                log.error("发送到群聊");
                break;
            default:
                log.error("未识别的消息类型");
                break;
        }
    }

    /**
     * 收到二进制消息
     *
     * @param session 会话
     * @param bytes   消息
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:22
     */
    @OnBinary
    public void onBinary(Session session, byte[] bytes) {
        for (byte b : bytes) {
            System.out.println(b);
        }
        session.sendBinary(bytes);
    }

    /**
     * 收到Netty事件
     *
     * @param session 会话
     * @param evt     事件
     * @author <achao1441470436@gmail.com>
     * @since 2021/11/11 9:22
     */
    @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;
            }
        }
    }
}

使用注解进行配置,netty的各种配置例如端口、主机、都可以在yml中配置,文档就是gitee中的md,用来做即时通讯简直不要太香

netty-websocket-spring-boot-starter

English Docs 简介 本项目帮助你在spring-boot中使用Netty来开发WebSocket服务器,并像spring-websocket的注解开发一样简单 要求

  • jdk版本为1.8或1.8+

快速开始

  • 添加依赖:

代码语言:javascript
复制
<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注解,样例如下:

代码语言:javascript
复制
@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 配置 所有的配置项都在这个注解的属性中

所有参数皆可使用${...}占位符获取application.properties中的配置。如下:

  • 首先在@ServerEndpoint注解的属性中使用${...}占位符

代码语言:javascript
复制
@ServerEndpoint(host = "${ws.host}",port = "${ws.port}")
public class MyWebSocket {
    ...
}

  • 接下来即可在application.properties中配置

代码语言:javascript
复制
ws.host=0.0.0.0
ws.port=80

自定义Favicon

配置favicon的方式与spring-boot中完全一致。只需将favicon.ico文件放到classpath的根目录下即可。如下:

代码语言:javascript
复制
src/
  +- main/
    +- java/
    |   + <source code>
    +- resources/
        +- favicon.ico

自定义错误页面

配置自定义错误页面的方式与spring-boot中完全一致。你可以添加一个 /public/error 目录,错误页面将会是该目录下的静态页面,错误页面的文件名必须是准确的错误状态或者是一串掩码,如下:

代码语言:javascript
复制
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
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-11-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 自定义Favicon
  • 自定义错误页面
相关产品与服务
即时通信 IM
即时通信 IM(Instant Messaging)基于腾讯二十余年的 IM 技术积累,支持Android、iOS、Mac、Windows、Web、H5、小程序平台且跨终端互通,低代码 UI 组件助您30分钟集成单聊、群聊、关系链、消息漫游、群组管理、资料管理、直播弹幕和内容审核等能力。适用于直播互动、电商带货、客服咨询、社交沟通、在线课程、企业办公、互动游戏、医疗健康等场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档