ChatPacket.proto

生成实体类

ChatPacket.proto

syntax = "proto3";

package com.litongjava.protobuf;

option java_outer_classname = "ChatPacketOuter";

// 命令类型枚举
enum CommandType {
    COMMAND_HANDSHAKE_REQ = 1;
    COMMAND_HANDSHAKE_RESP = 2;
    COMMAND_AUTH_REQ = 3;
    COMMAND_AUTH_RESP = 4;
    COMMAND_LOGIN_REQ = 5;
    COMMAND_LOGIN_RESP = 6;
    COMMAND_CHAT_REQ = 11;
    COMMAND_CHAT_RESP = 12;
    COMMAND_HEARTBEAT_REQ = 13;
    COMMAND_CLOSE_REQ = 14;
    COMMAND_GET_USER_REQ = 17;
    COMMAND_GET_USER_RESP = 18;
    COMMAND_CREATE_GROUP_REQ = 19;
    COMMAND_CREATE_GROUP_RESP = 20;
    COMMAND_DISBAND_GROUP_REQ = 21;
    COMMAND_DISBAND_GROUP_RESP = 22;
    COMMAND_INVITE_TO_GROUP_REQ = 23;
    COMMAND_INVITE_TO_GROUP_RESP = 24;
    COMMAND_JOIN_GROUP_REQ = 25;
    COMMAND_JOIN_GROUP_RESP = 26;
    COMMAND_LEAVE_GROUP_REQ = 27;
    COMMAND_LEAVE_GROUP_RESP = 28;
    COMMAND_MUTE_GROUP_REQ = 29;
    COMMAND_MUTE_GROUP_RESP = 30;
    COMMAND_GROUP_MESSAGE_REQ = 31;
    COMMAND_GROUP_MESSAGE_RESP = 32;
}

// 主消息结构
message ChatPacket {
    CommandType cmd = 1;
    int64 sequence = 2;
    int64 ack = 3;
    string from = 4;
    string to = 5;
    int64 createTime = 6;
    string token = 7; // 鉴权令牌

    // groupId 放在通用消息结构里
    string groupId = 8; // 群聊消息时使用

    oneof body {
        HandshakeMessage handshakeMessage = 9;
        AuthMessage authMessage = 10;
        LoginMessage loginMessage = 11;
        LoginResponse loginResponse = 12;
        ChatMessage chatMessage = 13;
        ChatResponse chatResponse = 14;
        HeartbeatMessage heartbeatMessage = 15;
        CloseMessage closeMessage = 16;
        GetUserMessage getUserMessage = 17;
        GetUserResponse getUserResponse = 18;

        // 群聊消息
        CreateGroupMessage createGroupMessage = 19;
        CreateGroupResponse createGroupResponse = 20;
        DisbandGroupMessage disbandGroupMessage = 21;
        DisbandGroupResponse disbandGroupResponse = 22;
        InviteToGroupMessage inviteToGroupMessage = 23;
        InviteToGroupResponse inviteToGroupResponse = 24;
        JoinGroupMessage joinGroupMessage = 25;
        JoinGroupResponse joinGroupResponse = 26;
        LeaveGroupMessage leaveGroupMessage = 27;
        LeaveGroupResponse leaveGroupResponse = 28;
        MuteGroupMessage muteGroupMessage = 29;
        MuteGroupResponse muteGroupResponse = 30;
        GroupChatMessage groupChatMessage = 31;
        GroupChatResponse groupChatResponse = 32;
    }
}

// 握手消息
message HandshakeMessage {
    bytes hbyte = 1;
}

// 鉴权消息
message AuthMessage {
    string token = 1;
}

// 登录请求消息
message LoginMessage {
    string loginname = 1;
    string password = 2;
    string token = 3;
    bool anonymous = 4; // 是否匿名登录,默认为false
}

// 登录响应消息
message LoginResponse {
    int32 result = 1; // 1:成功, 其他:错误代码
    string message = 2;
}

// 聊天请求消息
message ChatMessage {
    string msgType = 1; // "text", "image", "audio", "video", "news", "markdown"
    oneof content {
        TextContent textContent = 2;
        ImageContent imageContent = 3;
        AudioContent audioContent = 4;
        VideoContent videoContent = 5;
        NewsContent newsContent = 6;
        MarkdownContent markdownContent = 7;
    }
}

// 文本消息内容
message TextContent {
    string text = 1;
}

// 图片消息内容
message ImageContent {
    string url = 1;
    int32 width = 2;
    int32 height = 3;
    string format = 4; // 如 "png", "jpg"
}

// 音频消息内容
message AudioContent {
    string url = 1;
    int32 duration = 2; // 秒
    string format = 3; // 如 "mp3"
}

// 视频消息内容
message VideoContent {
    string url = 1;
    int32 duration = 2; // 秒
    string thumbnail = 3; // 缩略图URL
    string format = 4; // 如 "mp4"
}

// 新闻消息内容
message NewsContent {
    string title = 1;
    string description = 2;
    string url = 3;
    string image = 4; // 新闻图片URL
}

// Markdown消息内容
message MarkdownContent {
    string markdown = 1;
}

// 聊天响应消息(确认)
message ChatResponse {
    int32 status = 1; // 1:成功, 其他:错误代码
    string message = 2;
}

// 心跳消息
message HeartbeatMessage {
    bytes hbbyte = 1;
}

// 关闭、退出消息
message CloseMessage {
    // 无额外字段
}

// 获取用户信息请求消息
message GetUserMessage {
    string userId = 1; // type为0时必填
    string groupId = 2; // type为1时必填
    int32 type = 3;    // 0:指定用户,1:指定组用户,2:所有在线用户,3:所有用户
}

// 获取用户信息响应消息
message GetUserResponse {
    repeated UserInfo users = 1;
}

// 用户信息结构
message UserInfo {
    string userId = 1;
    string username = 2;
    int32 status = 3; // 0:离线,1:在线
}

// 创建群请求消息
message CreateGroupMessage {
    string groupName = 1;
    repeated string members = 2;
    string description = 3;
}

// 创建群响应消息
message CreateGroupResponse {
    int32 result = 1;
    string message = 2;
    string groupId = 3;
}

// 解散群请求消息
message DisbandGroupMessage {
    string groupId = 1;
}

// 解散群响应消息
message DisbandGroupResponse {
    int32 result = 1;
    string message = 2;
}

// 邀请加入群请求消息
message InviteToGroupMessage {
    string groupId = 1;
    repeated string invitees = 2;
}

// 邀请加入群响应消息
message InviteToGroupResponse {
    int32 result = 1;
    string message = 2;
}

// 加入群请求消息
message JoinGroupMessage {
    string groupId = 1;
}

// 加入群响应消息
message JoinGroupResponse {
    int32 result = 1;
    string message = 2;
}

// 退出群请求消息
message LeaveGroupMessage {
    string groupId = 1;
}

// 退出群响应消息
message LeaveGroupResponse {
    int32 result = 1;
    string message = 2;
}

// 屏蔽群请求消息
message MuteGroupMessage {
    string groupId = 1;
    bool mute = 2;
}

// 屏蔽群响应消息
message MuteGroupResponse {
    int32 result = 1;
    string message = 2;
}

// 群聊消息
message GroupChatMessage {
    string msgType = 1;

    oneof content {
        TextContent textContent = 2;
        ImageContent imageContent = 3;
        AudioContent audioContent = 4;
        VideoContent videoContent = 5;
        NewsContent newsContent = 6;
        MarkdownContent markdownContent = 7;
    }
}

// 群聊响应消息
message GroupChatResponse {
    int32 status = 1;
    string message = 2;
}

protoc --java_out=\src\main\java ChatPacket.proto

解析二进制数据

使用 ChatPacket 生成和解析二进制数据的两个实例

下面是两个实例,展示如何使用 ChatPacket 类生成二进制数据并将其解析回 ChatPacket 对象。你可以将这些实例直接应用到你的 Java 项目中。

实例 1:生成二进制数据

在这个实例中,我们将创建一个 ChatPacket 对象并将其序列化为二进制数据(字节数组)。

代码示例

package com.litongjava.im.protobuf;

import com.litongjava.im.protobuf.ChatPacketOuter.ChatPacket;
import com.litongjava.im.protobuf.ChatPacketOuter.CommandType;

public class ChatPacketBinaryExample {
  public static void main(String[] args) {
    // 创建一个 ChatPacket 对象
    ChatPacket chatPacket = ChatPacket.newBuilder().setCmd(CommandType.COMMAND_CHAT_REQ).
        setSequence(1234567890L).
        //
        setFrom("user123").setTo("user456").setToken("abcdefg")
        //
        .setCreateTime(1725338896411L).build();

    // 将 ChatPacket 对象序列化为二进制数据
    byte[] binaryData = chatPacket.toByteArray();

    // 输出二进制数据长度
    System.out.println("Serialized binary data length: " + binaryData.length);
  }
}
Serialized binary data length: 42

解释

  • ChatPacket.newBuilder():使用生成的 ChatPacket 类的 Builder 模式创建一个新的 ChatPacket 对象。
  • .toByteArray():将 ChatPacket 对象序列化为字节数组(二进制数据)。
  • 输出:输出二进制数据的长度。

实例 2:解析二进制数据

这个实例展示了如何将二进制数据反序列化回 ChatPacket 对象。

代码示例

package com.litongjava.im.protobuf;

import com.litongjava.im.protobuf.ChatPacketOuter.ChatPacket;
import com.litongjava.im.protobuf.ChatPacketOuter.CommandType;
import com.google.protobuf.InvalidProtocolBufferException;

public class ChatPacketBinaryParsingExample {
  public static void main(String[] args) {

    // 创建一个 ChatPacket 对象
    ChatPacket chatPacketFrom = ChatPacket.newBuilder().setCmd(CommandType.COMMAND_CHAT_REQ).setSequence(1234567890L).
    //
        setFrom("user123").setTo("user456").setToken("abcdefg")
        //
        .setCreateTime(1725338896411L).build();

    // 将 ChatPacket 对象序列化为二进制数据
    byte[] binaryData = chatPacketFrom.toByteArray();

    try {
      // 从二进制数据解析出 ChatPacket 对象
      ChatPacket chatPacket = ChatPacket.parseFrom(binaryData);

      // 输出解析后的 ChatPacket 对象
      System.out.println("Parsed ChatPacket: ");
      System.out.println("cmd: " + chatPacket.getCmd());
      System.out.println("sequence: " + chatPacket.getSequence());
      System.out.println("from: " + chatPacket.getFrom());
      System.out.println("to: " + chatPacket.getTo());
      System.out.println("token: " + chatPacket.getToken());
      System.out.println("createTime: " + chatPacket.getCreateTime());
    } catch (InvalidProtocolBufferException e) {
      e.printStackTrace();
    }
  }
}
Parsed ChatPacket:
cmd: COMMAND_CHAT_REQ
sequence: 1234567890
from: user123
to: user456
token: abcdefg
createTime: 1725338896411

解释

  • ChatPacket.parseFrom(binaryData):使用 parseFrom 方法将字节数组反序列化为 ChatPacket 对象。
  • 错误处理:捕获 InvalidProtocolBufferException 异常,该异常在二进制数据不符合 ChatPacket 格式时抛出。
  • 输出:输出解析后的 ChatPacket 对象的内容。

总结

  • 生成二进制数据:通过调用 toByteArray() 方法将 ChatPacket 对象序列化为字节数组。
  • 解析二进制数据:通过调用 ChatPacket.parseFrom() 方法将字节数组反序列化回 ChatPacket 对象。

通过这两个实例,你可以将 ChatPacket 对象与二进制数据互相转换,从而方便地在网络传输、数据存储等场景中使用 Protobuf 的高效序列化与反序列化特性。

生成 JSON 数据

使用 ChatPacket 生成 JSON 数据

要将 ChatPacket 对象转换为 JSON 字符串,可以使用 Protobuf 提供的 JsonFormat 类。下面是一个详细的示例,展示如何将 ChatPacket 对象序列化为 JSON 数据。

步骤:将 Protobuf 对象转换为 JSON 字符串

  1. 确保包含 Protobuf Java 库:确保项目中已经包含 Protobuf Java 运行时库和 protobuf-java-util 工具库,因为 JsonFormat 类在 protobuf-java-util 库中。

    如果使用 Maven,添加以下依赖(假设你已经添加过了):

    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>4.28.0-RC2</version> <!-- 选择与您的 .proto 语法兼容的版本 -->
    </dependency>
    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java-util</artifactId>
      <version>4.28.0-RC2</version>
    </dependency>
    
  2. 导入所需的类

    import com.google.protobuf.util.JsonFormat;
    import com.litongjava.im.protobuf.ChatPacketOuter.ChatPacket;
    import com.google.protobuf.InvalidProtocolBufferException;
    
  3. ChatPacket 对象转换为 JSON 字符串

    使用 JsonFormat.Printer 类将 ChatPacket 对象转换为 JSON 字符串。

    public String convertChatPacketToJson(ChatPacket chatPacket) throws InvalidProtocolBufferException {
      // 使用 JsonFormat 创建 Printer
      JsonFormat.Printer printer = JsonFormat.printer().includingDefaultValueFields();
      // 将 ChatPacket 对象转换为 JSON 字符串
      return printer.print(chatPacket);
    }
    

    注意includingDefaultValueFields() 方法可以在 JSON 中包括所有字段的默认值。


示例代码

以下是一个完整示例,展示如何将 ChatPacket 对象转换为 JSON 字符串。

Java 代码

package com.litongjava.im.protobuf;

import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import com.litongjava.im.protobuf.ChatPacketOuter.ChatMessage;
import com.litongjava.im.protobuf.ChatPacketOuter.ChatPacket;
import com.litongjava.im.protobuf.ChatPacketOuter.CommandType;
import com.litongjava.im.protobuf.ChatPacketOuter.TextContent;

public class ChatPacketToJsonExample {

  public static String convertChatPacketToJson(ChatPacket chatPacket) throws InvalidProtocolBufferException {
    // 使用 JsonFormat 创建 Printer
    JsonFormat.Printer printer = JsonFormat.printer();
    // 将 ChatPacket 对象转换为 JSON 字符串
    return printer.print(chatPacket);
  }

  public static void main(String[] args) {
    // 创建一个 ChatPacket 对象
    TextContent textContent = com.litongjava.im.protobuf.ChatPacketOuter.TextContent.newBuilder()
        //
        .setText("Hello, world!").build();

    ChatMessage chatMessage = com.litongjava.im.protobuf.ChatPacketOuter.ChatMessage.newBuilder()
        //
        .setMsgType("text").setTextContent(textContent).build();

    ChatPacket chatPacket = ChatPacket.newBuilder()
        //
        .setCmd(CommandType.COMMAND_CHAT_REQ)
        //
        .setSequence(1234567890L)
        //
        .setFrom("user123")
        //
        .setTo("user456")
        //
        .setToken("abcdefg")
        //
        .setCreateTime(1725338896411L)
        //
        .setChatMessage(chatMessage).build();

    try {
      // 将 ChatPacket 对象转换为 JSON 字符串
      String jsonString = convertChatPacketToJson(chatPacket);
      System.out.println("Generated JSON data: " + jsonString);
    } catch (InvalidProtocolBufferException e) {
      e.printStackTrace();
    }
  }
}

输出

运行上述代码,输出将类似于以下 JSON 字符串:

{
  "cmd": "COMMAND_CHAT_REQ",
  "sequence": "1234567890",
  "from": "user123",
  "to": "user456",
  "createTime": "1725338896411",
  "token": "abcdefg",
  "chatMessage": {
    "msgType": "text",
    "textContent": {
      "text": "Hello, world!"
    }
  }
}

解释

  • JsonFormat.printer():使用 JsonFormatprinter() 方法创建一个 JSON 打印器。
  • printer.print(chatPacket):将 ChatPacket 对象序列化为 JSON 字符串。

总结

通过使用 Protobuf 提供的 JsonFormat 工具类,可以方便地将 ChatPacket 对象转换为 JSON 字符串。这有助于在客户端和服务器之间进行数据交换时,保持数据的可读性和可移植性。

解析 JSON 数据

在 Java 应用中,当从客户端接收到 JSON 字符串时,可以利用 Protobuf 提供的内置 JSON 解析功能,将其转换为由 .proto 文件生成的 ChatPacketOuter.ChatPacket 类的实例。Protobuf 提供了 JsonFormat 类,可以方便地实现 JSON 与 Protobuf 对象之间的转换。

使用 Protobuf 的 JsonFormat 将 JSON 字符串解析为 ChatPacket 对象

以下是实现这一转换的步骤以及示例代码。

步骤:将 JSON 字符串解析为 Protobuf 对象

  1. 确保包含 Protobuf Java 库:首先需要在项目中添加 Protobuf Java 运行时库。
    如果使用 Maven,可以添加以下依赖:

    <dependency>
      <groupId>com.google.protobuf</groupId>
      <artifactId>protobuf-java</artifactId>
      <version>4.28.0-RC2</version> <!-- 选择与您的 .proto 语法兼容的版本 -->
    </dependency>
     <dependency>
       <groupId>com.google.protobuf</groupId>
       <artifactId>protobuf-java-util</artifactId>
       <version>4.28.0-RC2</version>
     </dependency>
    
  2. 导入所需的类

    import com.google.protobuf.util.JsonFormat;
    import com.litongjava.im.protobuf.ChatPacketOuter.ChatPacket;
    import com.google.protobuf.InvalidProtocolBufferException;
    
  3. 解析 JSON 字符串为 Protobuf 对象
    使用 JsonFormat.Parser 类将 JSON 字符串解析为 ChatPacket 对象。

    public ChatPacket parseJsonToChatPacket(String jsonString) throws InvalidProtocolBufferException {
      ChatPacket.Builder builder = ChatPacket.newBuilder();
      JsonFormat.Parser parser = JsonFormat.parser().ignoringUnknownFields();
      parser.merge(jsonString, builder);
      return builder.build();
    }
    

    注意:使用 ignoringUnknownFields() 方法可以忽略 JSON 中与 Protobuf 不匹配的字段。


示例代码

以下是一个完整示例,展示如何将 JSON 字符串转换为 ChatPacket 对象。

示例 JSON 字符串

{
  "cmd": 11,
  "sequence": 1234567890,
  "from": "user123",
  "to": "user456",
  "token": "abcdefg",
  "createTime": 1725338896411,
  "body": {
    "msgType": "text",
    "textContent": {
      "text": "Hello, world!"
    }
  }
}

Java 代码

package com.litongjava.im.protobuf;
import com.google.protobuf.util.JsonFormat;
import com.litongjava.im.protobuf.ChatPacketOuter.ChatPacket;
import com.google.protobuf.InvalidProtocolBufferException;

public class ChatPacketParser {

  public static ChatPacket parseJsonToChatPacket(String jsonString) throws InvalidProtocolBufferException {
    // 创建 ChatPacket 的 builder
    ChatPacket.Builder builder = ChatPacket.newBuilder();

    // 创建 JsonFormat 解析器
    JsonFormat.Parser parser = JsonFormat.parser().ignoringUnknownFields();

    // 将 JSON 字符串解析到 builder 中
    parser.merge(jsonString, builder);

    // 构建 ChatPacket 对象
    return builder.build();
  }

  public static void main(String[] args) {
    String jsonString = "{\n" +
        "  \"cmd\": 11,\n" +
        "  \"sequence\": 1234567890,\n" +
        "  \"from\": \"user123\",\n" +
        "  \"to\": \"user456\",\n" +
        "  \"token\": \"abcdefg\",\n" +
        "  \"createTime\": 1725338896411,\n" +
        "  \"body\": {\n" +
        "    \"msgType\": \"text\",\n" +
        "    \"textContent\": {\n" +
        "      \"text\": \"Hello, world!\"\n" +
        "    }\n" +
        "  }\n" +
        "}";

    try {
      ChatPacket chatPacket = parseJsonToChatPacket(jsonString);
      System.out.println("Parsed ChatPacket: " + chatPacket);
    } catch (InvalidProtocolBufferException e) {
      e.printStackTrace();
    }
  }
}

解释

  • Builder 模式:Protobuf 使用 Builder 模式来构建消息对象,因此首先要创建一个 ChatPacket.Builder 实例。
  • JsonFormat.Parser:创建一个解析器实例,并配置忽略未知字段。
  • 解析:使用 parser.merge(jsonString, builder) 方法将 JSON 字符串解析到 builder 中。
  • 构建对象:通过 builder.build() 获得不可变的 ChatPacket 对象。

重要注意事项

  1. JSON 字段名称:JSON 字段名称必须与 Protobuf 字段名称匹配。Protobuf 会将字段名称转换为小驼峰命名法(lowerCamelCase)。
  2. Oneof 字段处理body.proto 文件中定义为 oneof,因此在 JSON 中 body 字段下只能存在一种消息类型。
  3. 枚举:在 Protobuf 中,枚举可以通过名称或整数值来指定。
  4. 错误处理:要处理 InvalidProtocolBufferException 异常,如果 JSON 不符合 Protobuf 模式会抛出此异常。
  5. 未知字段:使用 ignoringUnknownFields() 来忽略 JSON 中未在 Protobuf 消息中定义的字段。

解析不同消息类型的 JSON

如果 body 字段中包含不同类型的消息,如文本消息或图片消息,确保在 JSON 中正确指定 msgType 及相应的内容。例如,对于图片消息:

{
  "cmd": 11,
  "sequence": 1234567890,
  "from": "user123",
  "to": "user456",
  "token": "abcdefg",
  "createTime": 1725338896411,
  "body": {
    "msgType": "image",
    "imageContent": {
      "url": "http://example.com/image.png",
      "width": 800,
      "height": 600,
      "format": "png"
    }
  }
}

结论

通过利用 Protobuf 的 JsonFormat 类,可以轻松地将 JSON 字符串解析为 Protobuf 对象。请确保 JSON 结构与 Protobuf 消息定义一致,尤其要注意字段名称和 oneof 字段。这样可以实现高效、类型安全的消息处理。


参考文档