ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

使用websocket实现协同编辑

2022-08-29 10:31:54  阅读:193  来源: 互联网

标签:协同 websocket String imageid 编辑 session import public


1、协同编辑的意思是什么?

其实,协同编辑无非就是字面意思,多人同时编辑,并且能够同步看到对方问保存的数据,典型的例子可以参考石墨文档,腾讯文档。

2、技术解决

核心技术就是信息的实时通信

以及多人编辑时所产生的冲突

这里我采用websocket来进行实时通信,大家都知道他是一个全双工通信协议,经过时间的考证,还是非常好用的,多数流行语言都有与之响应封装好的软件包

它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。详细的概念可以百度自行查找,比比皆是

编辑冲突的问题可以使用合并算法,上锁等技术(这里没有做过多的研究,所以我使用下面的方式,嘻嘻)

编辑冲突问题交给用户,当前用户实时地看到了别人正在编辑,那么当前用户就自觉性地停止编辑。

3、实现思路

1. 用户打开图像编辑页面,与后端建立长连接。
2. 后端将当前用户加入当前图像编辑列表。
3. 前端监听用户对于图像内容的修改,每一次修改将整个修改内容发送给后端。
4. 后端接收到信息,不做任何处理,直接将图像信息发送给图像编辑用户列表中其他的所有用户。
5. 前端收到后端的文本信息直接覆盖掉当前图像内容。

4、示例

废话就不多说了,直接上代码(复制粘贴即可使用呦)

这里是使用java语言编写的,采用的是原生注解方式,还有其他实现方式,在这里不一一介绍了(主要是上百度搜的没几个能用的,不是相关代码不全,就是长篇大论,最后还是不行,我实在是不会用啊!!!)

首先我们要使用websocket,肯定是要在pom里导入依赖包的(maven无法解析的,可以添加一下响应的版本号)

        <!-- webscoket        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

创建MyWebSocketConfig文件来注入bean

package com.example.javawebsocket;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;


//@Configuration注解标识的类中声明了1个或者多个@Bean方法,Spring容器可以使用这些方法来注入Bean
@Configuration
public class MyWebSocketConfig {

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

接下来就是创建一个原生注解的文件

package cn.staitech.system.utils;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;


import cn.staitech.common.security.utils.SecurityUtils;
import com.alibaba.fastjson.parser.JSONToken;
import io.swagger.models.auth.In;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
@Component
//主要是将目前的类定义成一个websocket服务器端, 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
@ServerEndpoint(value = "/websocket/{imageid}")
//此注解相当于设置访问URL
public class WebSocketServer {

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */






    private Session session;

    private String userName;

    /** concurrent包的线程安全Set,用来存放每个客户端对应的CumWebSocket对象。*/
    private static CopyOnWriteArraySet<WebSocketServer> webSockets =new CopyOnWriteArraySet<>();

    /**为了保存在线用户信息,在方法中新建一个list存储一下【实际项目依据复杂度,可以存储到数据库或者缓存】**/
    private static Map<Long,Session> sessionPool = new HashMap<Long,Session>();

    private static Map<Integer, CopyOnWriteArraySet<WebSocketServer>> newwebSockets = new HashMap<Integer,CopyOnWriteArraySet<WebSocketServer>>();





    /**
     * 建立连接
     * @param session
     * @param userName
     */
    @OnOpen
    public void onOpen(Session session,@PathParam(value = "imageid") Integer imageid) throws IOException {
        Long userid = SecurityUtils.getUserId();
        this.session = session;
        webSockets.add(this);
        // 如果id不存在,创建一个新的用户存储池,格式为 图像id:[用户1,用户2]
        if(! newwebSockets.containsKey(imageid)){
            CopyOnWriteArraySet<WebSocketServer> webSocketslist =new CopyOnWriteArraySet<>();
            newwebSockets.put(imageid,webSocketslist);
            newwebSockets.get(imageid).add(this);
        }else{
            newwebSockets.get(imageid).add(this);
        }
        sessionPool.put(userid, session);
        Session res = sessionPool.get(userid);
        System.out.println(imageid+"【websocket消息】有新的连接,总数为:"+newwebSockets.get(imageid).size());
    }


    /**
     * 断开连接
     */
    @OnClose
    public void onClose(@PathParam(value = "imageid") Integer imageid) {
        webSockets.remove(this);
        newwebSockets.get(imageid).remove(this);
        System.out.println(imageid + "【websocket消息】连接断开,总数为:"+newwebSockets.get(imageid).size());
    }


    /**
     * 发送错误
     * @param session
     * @param error
     */
    @OnError
    public void one rror(Session session, Throwable error) {
        System.out.println("[连接ID:{}] 错误原因:{}" + this.session + error.getMessage());
    }


    /**
     * 收到信息
     */
    @OnMessage
    public String onMessage(String message) {
        System.out.println("【websocket消息】收到客户端消息:"+message);
        return message;
    }

    // 此为广播消息
    public void sendAllMessage(String message,Integer imageid) {
        for(WebSocketServer webSocket : newwebSockets.get(imageid)) {
            System.out.println("【websocket消息】广播消息:"+message);
            try {
                webSocket.session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    // 此为单点消息
    public void sendOneMessage(String userName, String message) {
        System.out.println("【websocket消息】单点消息:"+message);
        System.out.println(sessionPool);
        Session session = sessionPool.get(userName);
        if (session != null) {
            try {
                session.getAsyncRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

}

我们我两个接口来调用一下上面两个接口

package com.example.javawebsocket;


import com.example.javawebsocket.WebSocketServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


@RestController
@RequestMapping("/annotation")
public class Websocket {


    @Autowired
    private WebSocketServer webSocketServer;

//    @GetMapping("/sendAllWebSocket")
//    public String test() {
//        String text="你们好!这是websocket群体发送!";
//        webSocketServer.sendAllMessage(text);
//        return text;
//    }

    @GetMapping("/sendOneWebSocket/{userName}")
    public String sendOneWebSocket(@PathVariable("userName") String userName) {
        String text=userName+" 你好! 这是websocket单人发送!";
        webSocketServer.sendOneMessage(userName,text);
        return text;
    }


    @CrossOrigin
    @RequestMapping(value = "/newimage",method = RequestMethod.POST)
    public String newimage(@RequestBody socketvo  req){
        webSocketServer.sendAllMessage(req.getImagename(), req.getImageid());
        System.out.println(req.getImagename());
        return "ok";
    }

}

基本上大致就是酱紫了,可以根据不同需求来自行更改

标签:协同,websocket,String,imageid,编辑,session,import,public
来源: https://www.cnblogs.com/gengjiangtao/p/16635014.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有