即时通讯
  • iOS
  • Android
  • macOS
  • Windows : C++
  • Web
  • 小程序
  • Flutter
  • Unity3D
  • uni-app
  • React Native
  • 产品简介
  • 下载
  • 快速开始
    • 跑通示例源码
    • 实现基本消息收发
  • 用户相关
  • 房间相关
  • 群组相关
  • 消息相关
  • 呼叫邀请
  • 会话管理
  • 缓存管理
  • 离线推送
  • 客户端 API
  • 服务端 API
  • 迁移方案
  • SDK 错误码
  • 常见问题
  • 文档中心
  • 即时通讯
  • 快速开始
  • 实现基本消息收发

实现基本消息收发

更新时间:2024-02-27 11:48

本文介绍如何使用 ZIM SDK 快速实现基本的单聊会话消息收发功能。

1 前提条件

在使用 ZIM SDK 前,请确保:

  • 开发环境满足以下要求:
    • Visual Studio 2015 或以上版本。
    • Windows 7 或以上版本。
  • 已在 ZEGO 控制台 创建项目,获取到了接入 ZIM SDK 服务所需的 AppID、AppSign。ZIM 服务权限不是默认开启的,使用前,请先在 ZEGO 控制台 自助开通 ZIM 服务(详情请参考 项目管理 - 即时通讯),若无法开通 ZIM 服务,请联系 ZEGO 技术支持开通。

2.3.0 及以上版本的 SDK,开始支持 “AppSign 鉴权”,同时仍支持 “Token 鉴权”,若您需要升级鉴权方式,可参考 ZIM 如何从 AppSign 鉴权升级为 Token 鉴权

2 集成 SDK

2.1 (可选)新建项目

此步骤以如何创建新项目为例,如果是集成到已有项目,可忽略此步。
  1. 打开 Microsoft Visual Studio,选择“文件 > 新建 > 项目”菜单。

    /Pics/Windows/ZIM/add_project_1.png

  2. 在新建项目窗口,选择项目类型为“MFC 应用程序”,输入项目名称,选择项目存储路径,并单击“确定”。

    /Pics/Windows/ZIM/add_project_2.png

  3. 进入 MFC 应用程序窗口,选择“应用程序类型”为“基于对话框”,并单击“完成”。

    /Pics/Windows/ZIM/add_project_3.png

2.2 导入 SDK

  1. 请参考 下载 SDK,下载最新版本的 SDK。

  2. 解压 SDK,并拷贝到项目SDK目录下。

    SDK 包含 “include” 和 “lib” 两个目录,每个目录包含的文件说明如下。

    x86            --------------- 32位版本,包含SDK的.lib和.dll文件
    | include      --------------- 包含SDK头文件
    x64            --------------- 64位版本,包含SDK的.lib和.dll文件
    | include      --------------- 包含SDK头文件

2.3 设置项目属性

在解决方案资源管理器窗口中,右击项目名称,单击“属性”,进入项目属性页。在项目属性页面内进行以下配置,配置完成后单击“确定”。

  1. 将 “include” 目录加入到头文件搜索路径。 选择“配置属性 > C/C++ > 常规”菜单,在“附加包含目录”中添加 “SDK/x86/include” 或 “SDK/x64/include” 目录。

    /Pics/Windows/ZIM/project_properties1.png

  2. 将 “lib” 目录加入到库搜索路径。 选择“配置属性 > 链接器 > 常规”菜单,在“附加库目录”中添加 “SDK/x86” 或 “SDK/x64” 目录。

    /Pics/Windows/ZIM/project_properties2.png

  3. 指定链接库 “ZIM.lib”。 选择“配置属性 > 链接器 > 输入”菜单,在“附加依赖项”中添加 “ZIM.lib”。

    /Pics/Windows/ZIM/project_properties3.png

3 实现基本收发消息

以下流程中,我们以客户端 A 和 B 的消息交互为例:

/Pics/ZIM/quick_start_Implementation.png

3.1 实现流程

请参考 跑通示例源码 获取源码,相关功能的源码请查看 “ZIMDemo” 文件下的 “CZIMConversationView.h” 和 “CZIMConversationView.cpp” 文件。

1. 导入头文件

在项目文件中引入头文件 “ZIM.h”,并在项目文件的源文件(“*.cpp” 或 “*.cc” 文件)中定义一次宏 ZIM_MAIN_CONFIG(需要且仅需要定义一次,且需要放到最前面定义)。

//头文件
#include "ZIM.h"
...
//源文件
#include "pch.h"
#define ZIM_MAIN_CONFIG
#include "framework.h"
...

2. 创建 ZIM 实例

首先我们需要在 SDK 中创建 ZIM 实例,一个实例对应的是一个用户,表示一个用户以客户端的身份登录系统。

例如,客户端 A、B 分别调用 create 接口,传入在 1 前提条件 中获取到的 AppID、AppSign,创建了 A、B 的实例:

// 创建 ZIM 对象,传入 appID、appSign,目前仅建议一个客户端创建一个zim实例
// 请注意:ZIM 从 2.3.0 版本开始支持 AppSign 鉴权,SDK 也默认为 AppSign 鉴权,如果您需要切换鉴权方式:
// (1) 2.3.3 及以上版本的 SDK,支持鉴权方式的自主切换; (2) 2.3.0 ~ 2.3.1 版本的 SDK,需要切换为 “Token 鉴权” 时,请联系 ZEGO 技术支持处理
zim::ZIMAppConfig app_config;
app_config.appID = 12345;     //替换为您申请到的 AppID
app_config.appSign = "appSign";   //替换为您申请到的 AppSign
zim_ = zim::ZIM::create(app_config);
ZIM 创建单例的示例

由于大多数开发者,在整个流程中,只需要将 ZIM 实例化一次,因此 ZEGO 建议您将 ZIM 单例化并保存。

创建一个 ZIMManager 类,在该类中声明并实现了一个 “sharedInstance” 静态方法,该方法将会在首次调用时创建并返回一个 ZIM 的对象,此后在项目的任意位置导入 ZIMManager 头文件,调用 “sharedInstance” 将会返回 zim 对象。

...
class ZIMManager {
    ...
    zim::ZIM * sharedInstance() {
        static zim::ZIM* zim_instance = nullptr;
        if(!zim_instance){
          zim::ZIMAppConfig app_config;
          app_config.appID = 12345;
          app_config.appSign = "appSign";
          zim_instance = zim::ZIM::create(app_config);    
        }
        return zim_instance;
    }
    ...
}
...
// 在其他位置使用
#include "ZIMManager.h"
...

// 以登录为例
ZIMManager::sharedInstance()->login(userInfo, token, [=](zim::ZIMError errorInfo){
    // 登录结果回调
});
...

3. 设置 setEventHandler 回调

在客户端登录前,开发者可以通过调用 setEventHandler 接口,自定义 ZIM 中的事件回调,接收到 SDK 异常、消息通知回调等的通知。

class CZIMEventHandler :public zim::ZIMEventHandler
{
public:
    CZIMEventHandler();
    ~CZIMEventHandler();
private:
    // 错误消息回调
    virtual void onError(zim::ZIM* /*zim*/, zim::ZIMError /*errorInfo*/, const std::string& /*desc*/) override;
    // 连接状态变更回调
    virtual void onConnectionStateChanged(zim::ZIM* /*zim*/, zim::ZIMConnectionState /*state*/, zim::ZIMConnectionEvent /*event*/, const std::string& /*extendedData*/)  override;
    // token即将过期提醒回调
    virtual void onTokenWillExpire(zim::ZIM* /*zim*/, unsigned int /*second*/)  override;
    // 收到 1v1 通信的消息回调
    virtual void onReceivePeerMessage(zim::ZIM* /*zim*/, const std::vector<std::shared_ptr<zim::ZIMMessage>>& /*messageList*/, const std::string& /*fromUserID*/)  override;
}
im_event_handler_ = std::make_shared<CZIMEventHandler>();
zim_->setEventHandler(im_event_handler_);

// 开发者接下来可通过 Register##callback_name 注册自己希望接受到的回调

详细的接口介绍,请参考 onConnectionStateChangedonTokenWillExpireonReceivePeerMessage

请注意,收到 SDK 回调消息时,开发者需要切换到自己的线程进行操作。

例如收到房间消息时,若要处理该消息,开发者需要将其切换至自己的线程进行操作(示例代码中以切换至 UI 线程为例)。

// 此处的 OnReceiveRoomMessage 是绑定的 SDK 中的 onReceiveRoomMessage
void CZIMDemoDlg::OnReceiveRoomMessage(zim::ZIM* zim, const std::vector<std::shared_ptr<zim::ZIMMessage>>& message_list, const std::string& from_room_id)
{
  global_main_dialog_->PostUiThread([=]() {
    global_main_dialog_->im_mode_select_dialog_->OnReceiveRoomMessage(zim, message_list, from_room_id);
  });
}

4. 登录 ZIM

创建实例后,客户端 A 和 B 需要登录 ZIM,只有登录成功后才可以开始发送、接收消息、更新 Token 等。

客户端需要使用各自的用户信息进行登录。调用 login 接口,传入用户信息 ZIMUserInfo 对象,进行登录。

  • “userID”、“userName” 支持开发者自定义规则生成。建议开发者将 “userID” 设置为一个有意义的值,可将其与自己的业务账号系统进行关联。
  • 2.3.0 或以上版本的 SDK,默认鉴权方式为 “AppSign 鉴权”,登录 ZIM 时只需传入 ZIMUserInfo、Token 传入空字符串即可。
  • 如果您使用的是 “Token 鉴权”,请参考 使用 Token 鉴权 文档,获取 Token 后,并在登录 ZIM 时传入 Token,鉴权通过后才能登录成功。
// userID 最大 32 字节的字符串。仅支持数字,英文字符 和 '!', '#', '$', '%', '&', '(', ')', '+', '-', ':', ';', '<', '=', '.', '>', '?', '@', '[', ']', '^', '_', '{', '}', '|', '~'。
// userName 最大 256 字节的字符串,无特殊字符限制。
zim::ZIMUserInfo user_info;
user_info.userID = user_id;
user_info.userName = user_name;

// 登录时:
// 使用 Token 鉴权,请传入您的 Token,详情请参考 [使用 Token 鉴权] 或使用临时 Token
// 使用 AppSign 鉴权 (2.3.0 或以上版本的默认鉴权方式),Token 字段填空字符串

zim_->login(user_info, [=](zim::ZIMError errorInfo){
    // 这里可以获取登录结果返回值,并根据错误码执行用户代码
});

5. 发送消息

客户端 A 登录成功后,可以向客户端 B 发送消息。

目前 ZIM 支持的消息类型如下:

消息类型 说明 特性及适用场景
ZIMTextMessage(1)
文本消息。消息大小不超过 32 KB,单个客户端发送频率限制为 10 次/秒。
消息可靠有序,可存储为历史消息(保存时间请参考 计费说明 - 版本说明 中“历史消息存储天数”。);一般适用于“单聊”、“群聊”等即时聊天的场景和“房间聊天”的公屏聊天场景。房间解散后,“房间聊天”的消息不存储。

相关接口:sendMessage

ZIMCommandMessage(2)
开发者可自定义数据内容的信令消息。消息大小不超过 5 KB,单个客户端发送频率限制为 10 次/秒。
支持更高的并发;一般适用于“语聊房”、“在线课堂”等房间内的信令传输,比如上下麦操作、送礼物,发送在线课堂课件等。

相关接口:sendMessage

ZIMBarrageMessage(20)
房间内弹幕文本消息。消息大小不超过 5 KB,单个客户端发送频率无限制。
专门用于房间内的高频、不可靠、允许丢掉的消息,一般适用于发送“弹幕消息”的场景中。

支持高并发,但不可靠,不保证消息送达。

相关接口:sendMessage

ZIMImageMessage(11)
图片消息。支持主流图片格式,包括 JPG、PNG、BMP、TIFF、GIF、WebP,大小不超过 10 MB,单个客户端发送频率限制为 10 次/秒。
消息可靠有序,可存储为历史消息(保存时间请参考 计费说明 - 版本说明 中“历史消息存储天数”。);一般用于单聊、房间、群聊等即时聊天场景。

相关接口: sendMediaMessage

ZIMFileMessage(12)
文件消息。消息内容为文件,格式不限,大小不超过 100 MB,单个客户端发送频率限制为 10 次/秒。
ZIMAudioMessage(13)
语音消息。支持 MP3、M4A 格式的语音文件,时长不超过 300 秒,大小不超过 6 MB,单个客户端发送频率限制为 10 次/秒。
ZIMVideoMessage(14)
视频消息。支持 MP4、MOV 格式的视频文件,大小不超过 100 MB,单个客户端发送频率限制为 10 次/秒。仅支持视频编码格式为 H264 和 H265 的视频文件在消息发送成功后获取该视频首帧的宽、高信息。
ZIMCombineMessage(100)
合并消息,消息大小无限制,单个客户端发送频率限制为 10 次/秒。
消息可靠有序,可存储为历史消息(保存时间请参考 计费说明 - 版本说明 中“历史消息存储天数”。);一般用于单聊、房间、群聊等即时聊天场景。

相关接口:sendMessage

ZIMCustomMessage(200)
自定义消息。开发者可自定义消息的类型,并自行完成消息的解析,ZIM SDK 不负责定义和解析自定义消息的具体内容。
一般可用于发送投票类型、接龙类型、视频卡片类型等消息。

相关接口: sendMessage

以下为发送单聊文本消息为例:客户端 A 可以调用 sendMessage 接口,传入客户端 B 的 userID、消息内容、消息类型 ZIMConversationType,即可发送一条文本消息到 B 的客户端。

  • ZIMMessageSentCallback 回调,判断消息是否发送成功。
  • onMessageAttached 回调,在消息发送前,可以获得一个临时的 ZIMMessage,以便您添加一些业务处理逻辑。例如:在发送消息前,获取到该条消息的 ID 信息。开发者在发送“视频”等内容较大的消息时,可以在消息上传完成前,获取对应该条消息的 localMessageID,实现发送前 Loading 的效果。
// 发送单聊信息,此处示例为发送文本消息
zim::ZIMMessage* message = nullptr;
zim::ZIMTextMessage text_message;
text_message.message = "message";
// 消息优先级,取值为 低:1 默认,中:2,高:3
zim::ZIMMessageSendConfig config;
config.priority = zim::ZIM_MESSAGE_PRIORITY_LOW;
message = &text_message;

auto smessage = std::make_shared<zim::ZIMTextMessage>("test 1");
auto notification = std::make_shared<zim::ZIMMessageSendNotification>();

notification->onMessageAttached = std::move([=](const std::shared_ptr<zim::ZIMMessage> &message) { int i = 0; });

// 单聊时,conversationID 即是对方的 userID;群组时,conversationID 即是群组的 groupID;房间时,conversationID 即是房间的 roomID
zim_->sendMessage(std::static_pointer_cast<zim::ZIMMessage>(smessage), "conversationID",
                          zim::ZIMConversationType::ZIM_CONVERSATION_TYPE_PEER, sendConfig,
                          notification,
                          [=](const std::shared_ptr<zim::ZIMMessage> &message,
                              const zim::ZIMError &errorInfo) { int i = 0; });

6. 接收消息

客户端 B 登录 ZIM 后,将会收到在 setEventHandler 设置的回调类中 onReceivePeerMessage 监听接口,收到客户端 A 发送过来的消息。

收到消息时,由于类型是基类,首先需要判断消息类型是 Text 还是 Command,开发者需要强转基类为具体的子类,然后从 message 字段获取消息内容。

// 此处的 OnReceivePeerMessage 是绑定的 SDK 中的 onReceivePeerMessage
void ZIMConversationView::OnReceivePeerMessage(zim::ZIM* zim, const std::vector<std::shared_ptr<zim::ZIMMessage>>& message_list, const std::string& from_user_id)
{
  for (auto message : message_list)
  {
    if (message->type == zim::ZIM_MESSAGE_TYPE_COMMAND)
    {
      auto command_message = std::dynamic_pointer_cast<zim::ZIM_MESSAGE_TYPE_COMMAND>(message);

      CFile file;
      file.Open(L"二进制消息文件", CFile::typeBinary | CFile::shareDenyNone | CFile::modeCreate | CFile::modeReadWrite);

      file.Write(& command_message->message[0], command_message->message.size());
    }
  }

  auto conversation = FindConversation(Utf8ToUnicode(from_user_id), kCurrentConversationTypePeer);
  conversation->messages.insert(conversation->messages.end(), message_list.begin(), message_list.end());
  UpdateConversationList();
}

7. 退出登录

如果客户端需要退出登录,直接调用 logout 接口即可。

zim_->logout();

8. 销毁 ZIM 实例

如果不需要 ZIM 实例,可直接调用 destroy 接口,销毁实例。

zim_->destroy();

3.2 API 时序图

通过以上的实现流程描述,API 接口调用时序图如下:

  • 发送消息时,统一使用 sendMessage 接口,并根据消息类型传入对应的 ZIMConversationType 取值。
  • 接收消息时:

相关文档

本篇目录