vue3+websocket使用protobuf碰到的一些问题

3,625 阅读3分钟

前言

我们app是面向海外的,客服这块压力比较大,im是基于融云的,考虑再三我们打算先在客服这块自建im,并结合客服排班写一个调度。后端这块用的netty。因为要测试,我写了个前端项目基于websocket来与im项目通信。问题就出在ts这块。

前端项目

作为一个前端渣渣,面向google编程。项目使用了vue3+vite。因为有点vue底子。 vite地址

创建项目
npm init @vitejs/app im-front --template vue-ts
整合jsx写法
// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import jsx from '@vitejs/plugin-vue-jsx'

export default defineConfig({
  plugins: [vue(),jsx()]
})

教程

创建.proto文件

Message.proto

syntax = "proto3";
option java_package = "com.zxs.im.msg";
// 消息体名称
option java_outer_classname = "MessageProto";
enum HeadType
{
  LOGIN_REQUEST = 0;//登陆请求
  LOGIN_RESPONSE = 1;//登录响应
  LOGOUT_REQUEST = 2;//退出请求
  LOGOUT_RESPONSE = 3;
  KEEPALIVE_REQUEST = 4;//心跳请求PING;
  KEEPALIVE_RESPONSE = 5;
  MESSAGE_REQUEST = 6;//消息请求;
  MESSAGE_RESPONSE = 7;//消息回执;
  MESSAGE_NOTIFICATION = 8;//通知消息
}
/*登录信息*/
// LoginRequest对应的HeadType为Login_Request
// 消息名称去掉下划线,更加符合Java 的类名规范
message LoginRequest{
     string uid = 1;        // 用户唯一id
     string deviceId = 2;     // 设备ID
     string token = 3;       // 用户token
     uint32 platform = 4;      //客户端平台 windows、mac、android、ios、web
     string app_version = 5;    // APP版本号
}
//token说明: 账号服务器登录时生成的Token
/*登录响应*/
message LoginResponse{
     bool  result = 1; //true 表示成功,false表示失败
     uint32 code = 2;   //错误码
     string info = 3;   //错误描述
     uint32 expose = 4; //错误描述是否提示给用户:1 提示;0 不提示
     string session_id = 5;     //sessionId
}



/*聊天消息*/
message MessageRequest{
     bool is_private =1;       // 私聊还是群聊
     string session_id = 2;      //sessionId
     string from = 3;       //发送方uId
     string to = 4;         //接收方Id
     uint32 msg_type = 5;  //消息类型  1:纯文本  2:音频 3:视频 4:地理位置 5:其他
     string content = 6;  //消息内容
}

/*聊天响应*/
message MessageResponse
{
     bool result = 1; //true表示发送成功,false表示发送失败
     uint32 code = 2;   //错误码
     string info = 3;   //错误描述
     uint32 expose = 4; //错误描述是否提示给用户:1 提示;0 不提示
     bool last_block = 5;
     fixed32 block_index = 6;
}

/*通知消息*/
message Notification
{
  bool is_private =1; // 私聊还是群聊
  bool is_order =2;  // 普通消息还是命令消息
  string from =3; //谁发的
  string to =4; //发给谁的
  uint32 msg_type = 5;  //消息类型  1:纯文本  2:音频 3:视频 4:地理位置 5:其他
  string order_name =6; // 如果是命令消息,消息的名字
  string content = 7; // 消息内容
  string timestamp = 8; // 发送时间
}

/*顶层消息*/
//顶层消息是一种嵌套消息,嵌套了各种类型消息
//内部的消息类型,全部使用optional字段
//根据消息类型 type的值,最多只有一个有效
message Message
{
  HeadType type = 1; //消息类型
  uint64   sequence = 2;//消息id
  fixed32  session_id =3;
  LoginRequest loginRequest = 4;
  LoginResponse loginResponse = 5;
  MessageRequest messageRequest = 6;
  MessageResponse messageResponse = 7;
  Notification notification = 8;
}

.proto转成js

网上的建议是使用protobufjs。那我就先使用它吧

// package.json
 "scripts": {
    "dev": "vite",
    "build": "vuedx-typecheck . && vite build",
    "proto": "pbjs -t static-module --es6 -w es6 -o src/message/proto.js  src/message/message.proto",
    "pbts": "pbts -o src/message/index.d.ts src/message/proto.js"
  },

是按照npm上的操作走的,但是使用的时候报错了,因为vite的原因加载这个ts报了深引用,解决后使用 Message.encode()居然没有这个方法???毕竟我不是专业的前端不想浪费时间。直接google,好家伙,运气好直接有解决方案

解决方案如下 github.com/timostamm/p…

尝试

看来上面的介绍后,我知道实际操作就是protoc调用一个插件,如下

    protoc \
    --plugin /Users/zhanghua/WebstormProjects/im-front/node_modules/.bin/protoc-gen-ts \
    --ts_out /Users/zhanghua/WebstormProjects/im-front/src/message \
    --ts_opt long_type_string \
    --ts_opt disable_service_client \
    --proto_path /Users/zhanghua/WebstormProjects/im-front/src/message \
    message.proto

生成效果 果然干干净净,没有protobuf生成ts文件的报红。

调用如下

// App.tsx
import {defineComponent,reactive,onMounted} from 'vue'
import {LoginRequest, Message} from "./message/message";

export default defineComponent(()=>{
    let ws:WebSocket ;
    
    const init = ()=>{
        ws = new WebSocket('ws://localhost:8081')
        ws.onclose = close;
        ws.onerror = onError;
        ws.onopen = open;
        ws.onmessage = message;
    }
    const open = ()=>{
        console.log("connect success")
    }
    const message = ()=>{
        console.log("收到消息")
    }

    const close = ()=>{  //关闭
        console.log('断开连接');
    }

    const onError = ()=>{
        console.log('连接异常');
    }
    const sendMessage = ()=>{
        const loginRequest = LoginRequest.create({
            "uid":"1",
            "token":"1",
            "deviceId":"1",
            "platform":4,
            "appVersion":"1.0"
        })
        const type = 0;
        const sequence ="1";
        const message = Message.create({
            type,
            loginRequest,
            sequence
        })
        console.log(message)
        ws.send(Message.toBinary(message).buffer);

    }
    onMounted(()=>{
        init();
    })
    return ()=>(
        <a-button onclick={sendMessage}>提交</a-button>
    )
})

server收到数据