websocket实现

由于安卓资源管理器展示的路径不尽相同,各种软件保存文件的位置也不一定一样.对于普通用户上传文件时,查找文件可能是一个麻烦的事情.后来想到了一个办法,使用pc端进行辅助上传.

实现思路

  1. pc端与服务器建立websocket连接;
  2. 服务器将sessionId传递到pc端;
  3. pc端生成二维码;
  4. 手机端扫描二维码,读取pc端sessionId;
  5. 手机端与服务器建立websocket连接;
  6. 手机端将fileId(后面再解释)、pc端sessionId、token等参数传递给服务器;
  7. 服务器更新pc端session 对应的fileId;
  8. 服务器将fileId、token等发送到pc端;
  9. pc使用token、fileId等请求文件列表并进行展示;
  10. 手机端、pc端进行文件修改后,向服务器发送给更新信号,服务器将更新信号转发到对端。

1.0 实现

定义web与客户端通信数据类型和数据格式

  1. 定义web与客户端通信数据类型
public class MsgType {
   
   
    public static final int UPDATE = 0; //提示客户端数据发生更新
    public static final int REQ = 1; //发送/接受fileId等字段
    public static final int SELF = 3; //建立连接后,web端发送client其sessionId
    public static final int ERR_SESSION = 4; //提示session不存在或已close 
    public static final int HEART_BEAT = 100; //心跳包
}
  1. 定义web与客户端通信数据格式
@Data
public class MsgData {
   
   
    private int type;  //对应 MsgType
    private String sessionId; //SELF 对应自身sessionId; REQ 对应pc端sessionId;
    private String fileId; //建立连接后,向pc端发送fileId等字段

web端websocket实现

创建spring-boot项目,添加web\websocket相关依赖

使用maven引入websocket依赖;

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

配置websocket和访问路径的映射关系

@Configuration   //配置websocket和访问路径的映射关系
@EnableWebSocket // 全局开启WebSocket支持
public class WebSocketConfig implements WebSocketConfigurer {
   
   

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
   
   
        registry.addHandler(new WebSocketServer(), "/websocket").setAllowedOrigins("*");
    }
}

web端对客户端数据的管理

  1. 定义web管理session的数据结构
@Data
public class SessionData {
   
   
    private int sessionType; // 1 master(app). 0 pc
    private String fileId; //pc会话ID
    private WebSocketSession session;
    private String sessionId;
    //虽然可以通过session.getId()获取到sessionId,但session关闭后,读取就会报错

web端对session的管理逻辑

  1. 新创建的连接添加到链表上,web向客户端发送SELF,告知其对应的sessionId;

  2. 断开连接时,如果是pc端session直接从链表中删除,如果是app端session,将其他相同fileId的session全部关闭并从链表删除;

  3. 接收到新消息后,根据消息类型进行分类处理:

    1. 心跳包,则直接返回;
    2. REQ app发送的fileId\pc端sessionId等字段,修改sessions上app连接和pc端SessionData内的fileId字段;
      并将fileId等字段发送给pc端;
    3. UPDATE 给所有相同fileId的session发送更新信号;

注意: sessions遍历\删除\添加必须添加synchronized,否则ConcurrentModificationException

package com.example.im.ws;

import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @ClassName WebSocketServer
 * @Description 处理websocket 连接
 * @Author guchuanhang
 * @date 2025/1/25 14:01
 * @Version 1.0
 **/

@Slf4j
public class WebSocketServer extends TextWebSocketHandler {
   
   

    private final Object syncObject = new Object();
    private final List<SessionData> sessions =
            new ArrayList<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
   
   
        log.info("New connection established: " + session.getId());
        SessionData sessionData = new SessionData(session);
        synchronized (syncObject) {
   
   
            sessions.add(sessionData);
        }
        MsgData msgData = new MsgData();
        msgData.setType(MsgType.SELF);
        msgData.setSessionId(session.getId());
        session.sendMessage(new TextMessage(new Gson().toJson(msgData)));
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {
   
   
        String payload = message.getPayload();
        log.info("handleTextMessage: " + session.getId());
        log.info("Received message: " + payload);
        final MsgData msgData = new Gson().fromJson(payload, MsgData.class);
        //master 发来的需求.
        switch (msgData.getType()) {
   
   

            case MsgType.HEART_BEAT: {
   
   
                //heart beat
                break;
            }

            case MsgType.REQ: {
   
   
                //set master
                {
   
   
                    SessionData sessionData = null;
                    synchronized (syncObject) {
   
   
                        final Optional<SessionData> any = sessions.stream().
                                filter(s -> s.getSessionId()
                   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值