基于netty去实现websocket 单聊+群聊 前端使用layui模板引擎

本项目开发周期比较短 大部分都是围绕业务来去实现 很多需要完善地方这里只是出了一个简短的例子
ps:在此之前没有搞过即时通讯,这个项目也算是入门大佬勿喷
所使用的技术:
netty,rabbitMQ,redis,mongoDB,mysql
先上java依赖

		 <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
               <!--引入 redis 依赖 -->
            <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.4.2</version>
        </dependency>

		  <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>
        
        <!-- spring-boot-starter-data-mongodb -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-mongodb</artifactId>
        </dependency>

        <!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.2.3</version>
        </dependency>
	以上基本满足了  还有一些第三方推送和oss储存就不一一放出来了,不会用oss和推送的可以去看看博客
	下面开始详细介绍:
		1.定义前后端通用数据结构
package com.yj.im.project.nettyServer;

import com.yj.im.project.entity.ChatRecord;

import java.io.Serializable;

public class Message implements Serializable {
   

    /**
     * 服务端接受参数
     * type          消息类型     0.连接  1.发送消息  2.接收消息  3.客户端保持心跳  4.群聊  500.异常
     * chatRecord {
     *      * @param {Object} msgType 消息类型  0.文字 1.图片,2.视频,
     *      * @param {Object} userId 发送的id
     *      * @param {Object} recipientId 接收者id
     *      * @param {Object} msg 消息
     *      * @param {Object} msgid 对应的消息id  (读取是需要加入)
     * }
     */

    /**
     * 客户端
     * type   消息类型     0.连接  1.客户端向服务器发送消息(发消息)  2.服务端向客户端发送消息(接收消息)   4.群聊 3.客户端保持心跳 500.异常
     * 备注:即: 客户端发送的统一为 1
     * 服务端发送的统一为 2
     * 前后端心跳包发送统一为 3
     * <p>
     * chatRecord {
     * * @param {Object} msgType 消息类型  0.文字 1.图片,2.视频,
     * * @param {Object} userId 发送的id
     * * @param {Object} recipientId 接收者id
     * * @param {Object} msg 消息(如果msgType为1,2此字段为文件路径)
     * * @param {Object} msgid 对应的消息id  (读取是需要加入)
     * }
     */

    private Integer type;//消息类型

    private ChatRecord chatRecord;//聊天消息

    private Object ext; //附加消息

    public Integer getType() {
   
        return type;
    }

    public Message setType(Integer type) {
   
        this.type = type;
        return this;
    }

    public ChatRecord getChatRecord() {
   
        return chatRecord;
    }

    public Message setChatRecord(ChatRecord chatRecord) {
   
        this.chatRecord = chatRecord;
        return this;

    }

    public Object getExt() {
   
        return ext;
    }

    public Message setExt(Object ext) {
   
        this.ext = ext;
        return this;

    }
}

package com.yj.im.project.entity;

import java.util.Date;
import java.io.Serializable;

/**
 * 聊天记录表(ChatRecord)实体类
 *
 * @author makejava
 * @since 2020-04-07 16:14:52
 */
public class ChatRecord implements Serializable {
   
    private static final long serialVersionUID = 489748139527994068L;
    /**
     * 记录表id
     */
    private Long id;

    private String mongoId;

    /**
     * 用户ID
     */
    private Long userId;
    /**
     * 接收者用户ID
     */
    private Long recipientId;

    /**
     * 用户在群里的昵称
     */
    private String nickName;

    /**
     * 头像
     */
    private String img;

    /**
     * 消息名称
     */
    private String msgName;

    /**
     * 是否已读(0.已读 1.未读)
     */
    private Integer hasRead;
    /**
     * 是否删除(0.已删除 1.未删除)
     */
    private Integer hasDelete;
    /**
     * 消息类型(0.文字 1.图片,3.视频)
     */
    private Integer msgType;

    /**
     * 区分用户消息还是系统消息
     */
    private Integer sysMsgType;

    /**
     * 消息内容(如果是图片则为文件路径)
     */
    private String message;
    /**
     * 状态(0逻辑删除、1数据有效)
     */
    private Integer status;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 修改时间
     */
    private Date updateTime;

    public String getMongoId() {
   
        return mongoId;
    }

    public ChatRecord setMongoId(String mongoId) {
   
        this.mongoId = mongoId;
        return this;
    }

    public String getImg() {
   
        return img;
    }

    public ChatRecord setImg(String img) {
   
        this.img = img;
        return this;
    }

    public String getMsgName() {
   
        return msgName;
    }

    public ChatRecord setMsgName(String msgName) {
   
        this.msgName = msgName;
        return this;
    }

    public String getNickName() {
   
        return nickName;
    }

    public ChatRecord setNickName(String nickName) {
   
        this.nickName = nickName;
        return this;

    }

    public Integer getSysMsgType() {
   
        return sysMsgType;
    }

    public ChatRecord setSysMsgType(Integer sysMsgType) {
   
        this.sysMsgType = sysMsgType;
        return this;
    }

    public Long getId() {
   
        return id;
    }

    public ChatRecord setId(Long id) {
   
        this.id = id;
        return this;
    }

    public Long getUserId() {
   
        return userId;
    }

    public ChatRecord setUserId(Long userId) {
   
        this.userId = userId;

        return this;

    }

    public Long getRecipientId() {
   
        return recipientId;
    }

    public ChatRecord setRecipientId(Long recipientId) {
   
        this.recipientId = recipientId;
        return this;

    }

    public Integer getHasRead() {
   
        return hasRead;
    }

    public ChatRecord setHasRead(Integer hasRead) {
   
        this.hasRead = hasRead;
        return this;

    }

    public Integer getHasDelete() {
   
        return hasDelete;
    }

    public ChatRecord setHasDelete(Integer hasDelete) {
   
        this.hasDelete = hasDelete;
        return this;

    }

    public Integer getMsgType() {
   
        return msgType;
    }

    public ChatRecord setMsgType(Integer msgType) {
   
        this.msgType = msgType;
        return this;

    }

    public String getMessage() {
   
        return message;
    }

    public ChatRecord setMessage(String message) {
   
        this.message = message;
        return this;

    }

    public Integer getStatus() {
   
        return status;
    }

    public ChatRecord setStatus(Integer status) {
   
        this.status = status;
        return this;

    }

    public Date getCreateTime() {
   
        return createTime;
    }

    public ChatRecord setCreateTime(Date createTime) {
   
        this.createTime = createTime;
        return this;

    }

    public Date getUpdateTime() {
   
        return updateTime;
    }

    public ChatRecord setUpdateTime(Date updateTime) {
   
        this.updateTime = updateTime;
        return this;

    }

}

2.后续跟上netty代码:

package com.yj.im.project.nettyServer;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;

public class WebSocketInitializer extends ChannelInitializer<SocketChannel> {
   

    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
   
        ChannelPipeline pipeline = ch.pipeline();

        //-------------
        //用于支持 Http协议
        //-----------------

        //websocket基于 http协议,需要有 http 的编解码器
        pipeline.addLast(new HttpServerCodec())
                //对于大数据流的支持
                .addLast(new ChunkedWriteHandler())
                //添加对HTTP请求和响应的聚合器:只要使用Netty进行 http编码都需要使用到
                //对HttpMessage进行聚合,聚合成FullHttpRequest或者FullHttpResponse
                //在 netty 编程总都会使用到Handler
                .addLast(new HttpObjectAggregator(1024 * 64))
                .addLast(new WebSocketServerProtocolHandler("/ws"))
                //添加Netty空闲超时检查的支持
                //4:读空闲超时,8:写空闲超时,12: 读写空闲超时
                .addLast(new IdleStateHandler(4, 8, 12))
                .addLast(new HearBeatHandler())
                //添加自定有的 handler
                .addLast(new ChatHandler());


    }
}

package com.yj.im.project.nettyServer;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class WebSocketServer {
   


    private EventLoopGroup bossGroup;//主线程

    private EventLoopGroup workerGroup;//工作线程

    private ServerBootstrap server;//服务器

    private ChannelFuture future; //回调

    @PostConstruct
    public void start() {
   
        future = server.bind(8089);
        BaseNettyServer.nettyLog().warn("-----------------------------------------------------------------------------------------");
        BaseNettyServer.nettyLog().warn("---------------------------------netty server   开始启动---------------------------------");
        BaseNettyServer.nettyLog().warn("-----------------------------------------------------------------------------------------");
        BaseNettyServer.nettyLog().warn("-----------------------------------------------------------------------------------------");
        BaseNettyServer.nettyLog().warn("---------------------------------netty server - 启动成功---------------------------------");
        BaseNettyServer.nettyLog().warn("-----------------------------------------------------------------------------------------");

    }

    public WebSocketServer() {
   
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        server = new ServerBootstrap();
        server.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new WebSocketInitializer());
    }
}

netty是需要两个线程来工作的具体的可以去百度看看这里不做介绍,然后定义端口加上@PostConstruct启动的时候执行一次就好了WebSocketServer()是当前的构造器,容器启动的时候会加载这个构造器。

3.这里就是自定义的消息处理handler,在这之前先把一些其他类粘出来

package com.yj.im.project.nettyServer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BaseNettyServer {
   

    private final static Logger log = LoggerFactory.getLogger(HearBeatHandler.class);

    public static Logger nettyLog(){
   
        return log;
    }

}

package com.yj.im.project.nettyServer;

import com.yj.im.project.entity.ChatFriend;
import io.netty.channel.Channel;

import java.util.HashMap;
import java.util.Map;

public class UserChannelMap {
   


    //保存用户id和通道的map对象
    private static Map<String, Channel> userChannelMap;

    static {
   
        userChannelMap = new HashMap<>();
    }

    /**
     * 建立用户和通道直接的关联
     *
     * @param userId
     * @param channel
     */
    public static void put(Long userId, Channel channel) {
   
        userChannelMap.put("im" + userId, channel);
    }

    public static Channel getChannel(Long userId) {
   
        return userChannelMap.get("im" + userId);
    }

    /**
     * 解除用户和通道直接的关系
     *
     * @param userid
     */
    public static void remove(String userid) {
   
        userChannelMap.remove
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值