XIMXIM 开发文档

快速开始

4 步完成 XIM SDK 接入:

1

注册控制台,创建应用

在 XIM 控制台注册账号,创建应用获取 AppID 和 AppSecret

2

安装 XIM SDK

通过 <script> 标签或包管理器安装对应平台的 XIM SDK

3

初始化并连接

使用 AppID 初始化 SDK,调用 login 建立 WebSocket 连接

4

发送第一条消息

调用 sendText 发送文字消息,体验毫秒级送达

js
// 最简示例
XIM.init({ appId: 'your_app_id' });
await XIM.login('user_001', 'token');
await XIM.sendText('user_002', 'Hello!');

JS SDK H5 / 微信小程序 / 支付宝小程序

引入 SDK

浏览器 / H5:

HTML
<script src="/dist/xim-sdk.js"></script>
<script>
  const XIM = xSpace.XIM;
</script>

模块方式(需打包工具):

js
import { XIM } from '@xim/sdk';

初始化

在调用任何其他接口前,必须先调用 XIM.init()

js
XIM.init({
  appId: 'your_app_id',   // 必填,应用 ID
  platform: 'web',        // 可选,平台标识
});
参数类型必填说明
appIdstring应用 ID
serverstringWebSocket 地址,不传则使用 SDK 默认地址
platformstring平台标识,如 web、wxmp
deviceIdstring设备唯一标识
appVersionstring应用版本号
nicknamestring登录时上报的昵称
avatarUrlstring登录时上报的头像 URL

登录与登出

js
await XIM.login('user_001', 'token_string');
参数类型说明
uidstring用户 ID,只允许字母、数字、下划线、连字符
tokenstring鉴权 token,传空字符串时服务端不做鉴权

登录成功后触发 connected 事件,同时自动拉取离线消息。

js
XIM.logout();

事件监听

js
// 监听
XIM.on('message', (msg) => { ... });

// 取消监听
XIM.off('message', handler);
事件名回调参数说明
connected-WebSocket 登录成功
disconnectedreason: string连接断开
kickedreason: string被踢下线
messagemsg: Message收到新消息
binary_messagemsg: Message收到二进制透传消息(type=5)
conversation_updatedconv: Conversation会话列表有更新
msg_recalledconvId, msgId消息被撤回
group_member_changegroupId, uid, action群成员变化
channel_datachannelId, senderId, seqNum, data, ...收到频道数据帧
channel_text{ channelId, senderId, text, ... }收到频道文本消息
channel_member_joinchannelId, userId, ...有人加入频道
channel_member_leavechannelId, userId, ...有人离开频道
channel_destroyedchannelId, reason频道被销毁

消息发送

发送文本消息

js
const msg = await XIM.sendText('user_002', '你好', 1);
参数类型默认值说明
targetIdstring必填单聊传对方 uid,群聊传 groupId
contentstring必填消息正文
convTypenumber11=单聊,2=群聊

发送图片消息

js
const msg = await XIM.sendImage('user_002', file, 1);
// 小程序传 tempFilePath 字符串

SDK 内部自动完成上传、获取宽高、封装 content JSON。接收方 msg.type === 2,content 为:

JSON
{ "url": "https://...", "w": 640, "h": 480, "size": 102400 }

发送文件消息

js
const msg = await XIM.sendFile('user_002', file, 1);

接收方 msg.type === 4,content 为:

JSON
{ "url": "https://...", "name": "report.pdf", "size": 204800 }

发送二进制透传消息

js
const payload = new Uint8Array([1, 2, 3]);
const msg = await XIM.sendData('user_002', payload, 1);

消息接收

js
XIM.on('message', (msg) => {
  if (msg.type === 1) {
    console.log(msg.content);           // 文本
  } else if (msg.type === 2) {
    const { url, w, h } = JSON.parse(msg.content);  // 图片
  } else if (msg.type === 4) {
    const { url, name, size } = JSON.parse(msg.content); // 文件
  } else if (msg.type === 5) {
    console.log(msg.payload);           // 二进制
  }
});
字段类型说明
idstring服务端消息 ID
convIdstring会话 ID,单聊 uidA|uidB,群聊 g_xxx
targetIdstring单聊=对方 uid,群聊=groupId
senderIdstring发送方 uid
typenumber1=文本,2=图片,3=语音,4=文件,5=二进制
contentstring消息内容
timestampnumber服务端时间戳(毫秒)
seqnumber会话内有序序号
payloadUint8Arraytype=5 时的原始二进制

历史消息

js
const { messages, hasMore } = await XIM.getHistory('user_002', 1, beforeTime, 20);

优先读本地 IndexedDB 缓存,不足时向服务端补齐。

参数类型默认值说明
targetIdstring必填对方 uid 或 groupId
convTypenumber必填1=单聊,2=群聊
beforeTimenumber0拉取此时间戳之前的消息(毫秒)
countnumber20每次拉取条数
js
// 下拉加载更多
let minTime = 0;
let hasMore = true;

async function loadOlder() {
  if (!hasMore) return;
  const result = await XIM.getHistory('user_002', 1, minTime, 20);
  if (result.messages.length > 0) minTime = result.messages[0].timestamp;
  hasMore = result.hasMore;
}

会话管理

js
const convs = XIM.getConversations(); // 纯本地,无网络请求

XIM.markRead('user_002', 1);   // 标记已读
XIM.markUnread('user_002', 1); // 标记未读

await XIM.recallMessage(msgId); // 撤回消息
字段类型说明
idstring会话 ID
typenumber1=单聊,2=群聊
targetIdstring单聊=对方uid,群聊=groupId
namestring会话显示名
unreadCountnumber未读数
lastMsgMessage最后一条消息

群组

js
const group = await XIM.createGroup('我的群', ['user_002', 'user_003']);
await XIM.joinGroup('g_xxxxxxxx');
await XIM.leaveGroup('g_xxxxxxxx');

实时频道

频道是 fire-and-forget 的实时数据通道,不持久化,适合游戏对局等实时场景。

js
const info = await XIM.channelJoin('room_001');
XIM.channelSendText('room_001', '你好大家');

XIM.on('channel_text', ({ channelId, senderId, text }) => {
  console.log(`${senderId}: ${text}`);
});

await XIM.channelLeave('room_001');

用户资料

js
await XIM.setMyProfile({ nickname: '张三', avatarUrl: '...' });
const profiles = await XIM.getUserProfiles(['user_001', 'user_002']);
const cached = XIM.getCachedProfile('user_001');

类型定义

ts
interface Message {
  id: string;
  convId: string;
  targetId: string;
  senderId: string;
  type: number;       // 1=文本, 2=图片, 3=语音, 4=文件, 5=二进制
  content: string;
  timestamp: number;
  status: number;     // 0=发送中, 1=已发送
  seq: number;
  payload?: Uint8Array;
  flags?: number;
  extra?: string;
}

interface Conversation {
  id: string;
  type: number;       // 1=单聊, 2=群聊
  targetId: string;
  name: string;
  unreadCount: number;
  lastMsg?: Message;
}

interface InitConfig {
  appId: string;
  server?: string;
  platform?: string;
  deviceId?: string;
  appVersion?: string;
  appName?: string;
  nickname?: string;
  avatarUrl?: string;
}

Unity C# SDK

引入 SDK

通过 Unity Package Manager 导入 XIM SDK,或将 com.xim.sdk 包放入项目的 Packages/ 目录。

C#
// 在脚本顶部引入命名空间
using xSpace;

初始化

在场景加载后调用 XIM.Init() 完成初始化。SDK 会自动创建一个 DontDestroyOnLoad 的 GameObject 来驱动内部网络循环。

C#
using XSpace;

XIM.Init("your_app_id");

可选参数:

C#
XIM.Init(
    appId: "your_app_id",
    server: "wss://your-server.com",  // 可选,自定义服务器地址
    autoDownload: true                 // 可选,是否自动下载语音/图片文件
);
参数类型默认值说明
appIdstring必填应用 ID,在控制台获取
serverstringnull自定义 WebSocket 服务器地址,不传则使用默认地址
autoDownloadbooltrue是否自动下载收到的语音和图片文件到本地缓存

登录与登出

Unity SDK 使用 async/await 模式,所有异步接口返回 Task<T>

C#
Result result = await XIM.LoginAsync("user_001", "token");
if (result.Success) {
    Debug.Log("登录成功");
}
参数类型说明
uidstring用户 ID,只允许字母、数字、下划线、连字符
tokenstring鉴权 token,传空字符串时服务端不做鉴权
nicknamestring可选,登录时上报昵称
avatarUrlstring可选,登录时上报头像 URL

登出:

C#
await XIM.LogoutAsync();

获取当前连接状态:

C#
ConnState state = XIM.ConnectionState;
// ConnState.Connected / Connecting / Disconnected

事件监听

Unity SDK 使用静态委托事件,回调始终在主线程触发,可直接操作 UI。

C#
// 连接事件
XIM.OnConnected += () => Debug.Log("已连接");
XIM.OnDisconnected += (code, reason) => Debug.Log($"断开: {reason}");
XIM.OnKickedOffline += () => Debug.Log("被踢下线");

// 消息事件
XIM.OnNewMessage += (msg) => Debug.Log($"新消息: {msg.Content}");
XIM.OnMessageRecalled += (msgId) => Debug.Log($"撤回: {msgId}");

// 会话事件
XIM.OnConversationUpdated += (conv) => Debug.Log($"会话更新: {conv.TargetId}");

// 群组事件
XIM.OnGroupMemberJoined += (groupId, userId) => { };
XIM.OnGroupMemberLeft += (groupId, userId) => { };

// 频道事件
XIM.OnChannelText += (channelId, senderId, text, seqNum, timestamp) => { };
XIM.OnChannelData += (channelId, senderId, seqNum, data, contentType) => { };

消息发送

所有发送接口返回 Task<Message>,可 await 获得完整消息对象(含服务端分配的 msgId 和时间戳)。

发送文本消息

C#
Message msg = await XIM.SendTextAsync("user_002", "你好");

发送图片消息

C#
Message msg = await XIM.SendImageAsync("user_002", "/path/to/image.png");

发送语音消息

C#
Message msg = await XIM.SendVoiceAsync("user_002", "/path/to/voice.wav", durationSec: 5);

发送二进制消息

C#
byte[] data = Encoding.UTF8.GetBytes("custom payload");
Message msg = await XIM.SendDataAsync("user_002", data, 0, data.Length, convType: 1, flags: 0);

撤回消息

C#
Result result = await XIM.RecallAsync(msg.MsgId);
方法参数说明
SendTextAsynctargetId, content, convType=1发送文本消息
SendImageAsynctargetId, filePath, convType=1发送图片消息,filePath 为本地绝对路径
SendVoiceAsynctargetId, filePath, durationSec, convType=1发送语音消息
SendDataAsynctargetId, data, offset, len, convType=1, flags=0发送二进制透传消息
RecallAsyncmsgId撤回已发送消息

convType:1 = 单聊,2 = 群聊。

消息接收

Unity SDK 使用静态委托事件,回调始终在主线程触发,可直接操作 UI。

C#
XIM.OnNewMessage += (Message msg) => {
    Debug.Log($"收到消息: {msg.Content} 来自 {msg.SenderId}");
};

XIM.OnMessageRecalled += (string msgId) => {
    Debug.Log($"消息被撤回: {msgId}");
};

XIM.OnBinaryMessage += (string msgId, string convId, string senderId, byte[] payload, uint flags) => {
    Debug.Log($"收到二进制消息: {payload.Length} bytes");
};
事件回调参数说明
OnNewMessageMessage msg收到新消息(文本/图片/语音)
OnMessageRecalledstring msgId消息被撤回
OnBinaryMessagemsgId, convId, senderId, byte[] payload, uint flags收到二进制透传消息

Message 对象关键字段:

字段类型说明
MsgIdstring消息唯一 ID
ConvIdstring会话 ID
SenderIdstring发送者 UID
Contentstring文本内容(文本消息)
FilePathstring本地文件路径(图片/语音消息,autoDownload 开启时自动填充)
FileUrlstring文件远程 URL
Durationint语音时长(秒)
Timestamplong服务端时间戳(毫秒)
Typeint消息类型:1=文本 2=图片 3=语音 5=二进制

历史消息

C#
HistoryResult history = await XIM.GetHistoryAsync(
    targetId: "user_002",
    convType: 1,
    beforeTime: DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(),
    count: 20
);

foreach (Message msg in history.Messages) {
    Debug.Log($"{msg.SenderId}: {msg.Content}");
}
参数类型说明
targetIdstring单聊传对方 uid,群聊传 groupId
convTypeint1=单聊 2=群聊
beforeTimelong拉取此时间戳之前的消息(毫秒)
countint拉取条数,最大 50

会话管理

会话列表由客户端本地维护,收发消息时自动更新。

C#
// 获取会话列表
List<Conversation> convs = await XIM.GetConversationsAsync();

// 删除会话
await XIM.DeleteConversationAsync("user_002", convType: 1);

// 标记会话已读
XIM.MarkRead("user_002", convType: 1);

监听会话更新:

C#
XIM.OnConversationUpdated += (Conversation conv) => {
    Debug.Log($"会话更新: {conv.TargetId} 未读: {conv.UnreadCount}");
};
字段类型说明
TargetIdstring会话目标 ID
ConvTypeint1=单聊 2=群聊
LastMessageMessage最后一条消息
UnreadCountint未读消息数
UpdatedAtlong最后更新时间戳

群组

C#
// 创建群组
GroupInfo group = await XIM.CreateGroupAsync("我的群", new List<string> { "user_002", "user_003" });

// 加入群组
await XIM.JoinGroupAsync("group_001");

// 退出群组
await XIM.LeaveGroupAsync("group_001");

群事件监听:

C#
XIM.OnGroupMemberJoined += (string groupId, string userId) => { };
XIM.OnGroupMemberLeft += (string groupId, string userId) => { };
XIM.OnGroupDismissed += (string groupId) => { };
方法参数说明
CreateGroupAsyncname, memberIds创建群组并拉入初始成员
JoinGroupAsyncgroupId主动加入群组
LeaveGroupAsyncgroupId退出群组

实时频道

频道用于低延迟实时数据广播,适用于游戏同步、直播互动等场景。

C#
// 加入频道
ChannelInfo channel = await XIM.ChannelJoinAsync("room_001");

// 发送二进制数据
byte[] payload = ...;
XIM.ChannelSend("room_001", payload, 0, payload.Length, reliable: true, contentType: 0);

// 发送文本
XIM.ChannelSendText("room_001", "Hello everyone!");

// 离开频道
await XIM.ChannelLeaveAsync("room_001");

频道事件:

C#
XIM.OnChannelData += (string channelId, string senderId, uint seqNum, byte[] data, uint contentType) => { };
XIM.OnChannelText += (string channelId, string senderId, string text) => { };
XIM.OnChannelMemberJoined += (string channelId, string userId) => { };
XIM.OnChannelMemberLeft += (string channelId, string userId) => { };
XIM.OnChannelDestroyed += (string channelId, string reason) => { };
方法参数说明
ChannelJoinAsyncchannelId加入频道,返回 ChannelInfo
ChannelLeaveAsyncchannelId离开频道
ChannelSendchannelId, data, offset, len, reliable, contentType发送二进制帧
ChannelSendTextchannelId, text发送文本消息

用户资料

C#
// 设置自己的资料
UserProfile myProfile = await XIM.SetMyProfileAsync(new UserProfile {
    Nickname = "小明",
    AvatarUrl = "https://example.com/avatar.png"
});

// 批量获取用户资料
List<UserProfile> profiles = await XIM.GetUserProfilesAsync(
    new List<string> { "user_001", "user_002" }
);

// 获取缓存的资料(不发起网络请求)
UserProfile cached = XIM.GetCachedProfile("user_001");
方法参数说明
SetMyProfileAsyncUserProfile profile设置当前用户资料
GetUserProfilesAsyncList<string> uids批量获取用户资料(网络请求)
GetCachedProfilestring uid从本地缓存获取用户资料

文件缓存

SDK 内置文件缓存系统,自动管理下载的图片、语音等文件。也可手动调用下载。

C#
// 获取缓存根目录
string cacheRoot = XIM.CacheDir;

// 获取指定类型缓存目录
string imageDir = XIM.CacheDirForType(CacheType.Image);

// 获取已缓存文件的本地路径(未缓存返回 null)
string path = XIM.GetCachedPath("https://example.com/file.png", CacheType.Image);

// 手动下载文件
string localPath = await XIM.DownloadFileAsync("https://example.com/file.png", CacheType.Image);

// 清除所有缓存
XIM.ClearCache();

// 清除指定类型缓存
XIM.ClearCache(CacheType.Voice);
CacheType说明
Image图片文件缓存
Voice语音文件缓存
File通用文件缓存

类型定义

C#
// 操作结果
public class Result {
    public bool Success;
    public int Code;       // 错误码,0 = 成功
    public string Error;   // 错误信息
}

// 消息
public class Message {
    public string MsgId;
    public string ConvId;
    public string SenderId;
    public int Type;          // 1=文本 2=图片 3=语音 5=二进制
    public string Content;    // 文本内容
    public string FilePath;   // 本地文件路径
    public string FileUrl;    // 远程文件 URL
    public int Duration;      // 语音时长(秒)
    public long Timestamp;    // 服务端时间戳(毫秒)
}

// 会话
public class Conversation {
    public string TargetId;
    public int ConvType;      // 1=单聊 2=群聊
    public Message LastMessage;
    public int UnreadCount;
    public long UpdatedAt;
}

// 群组信息
public class GroupInfo {
    public string GroupId;
    public string Name;
    public string OwnerId;
    public List<string> MemberIds;
}

// 用户资料
public class UserProfile {
    public string Uid;
    public string Nickname;
    public string AvatarUrl;
}

// 频道信息
public class ChannelInfo {
    public string ChannelId;
    public List<string> Members;
}

// 历史消息结果
public class HistoryResult {
    public List<Message> Messages;
    public bool HasMore;
}

// 连接状态
public enum ConnState {
    Disconnected,
    Connecting,
    Connected
}

// 缓存类型
public enum CacheType {
    Image,
    Voice,
    File
}

Unity SDK 还提供语音相关控制属性:

属性/方法类型说明
XIM.ReceiveVoicebool是否接收并播放语音消息(PTT 开关)
XIM.VoiceVolumefloat语音播放音量,0.0 ~ 1.0

Cocos C++ SDK Cocos2d-x / 原生 C++

引入 SDK

在项目中引入 XIM 头文件,所有 API 均位于 xspace 命名空间下:

C++
#include <xim/XIM.h>
using namespace xspace;

将 XIM SDK 的 include/ 目录加入头文件搜索路径,链接 libxim_core 静态库。Cocos2d-x 项目在 CMakeLists.txt 中添加:

cmake
target_include_directories(your_target PRIVATE path/to/xim-sdk/include)
target_link_libraries(your_target xim_core)

初始化

在调用任何其他接口前,必须先调用 XIM::init()

C++
xspace::Config cfg;
cfg.appId   = "your_app_id";     // 必填
cfg.server  = "ws://x.x.x.x:9090/ws";  // 可选,私有部署时填写
cfg.dataDir = "/path/to/data";   // 可选,本地数据库/缓存目录
cfg.logLevel = xspace::LogLevel::Info;
cfg.autoDownload = true;         // 收到图片/语音自动下载

XIM::init(cfg);
字段类型必填说明
appIdstring应用 ID,从控制台获取
serverstringWebSocket 地址,不传则使用默认公有云地址
dataDirstring本地数据目录(数据库、缓存),默认使用平台缓存路径
logLevelLogLevelDebug / Info / Warn / Error,默认 Warn
autoDownloadbool收到图片/语音消息时自动下载到本地缓存,默认 true

重要:必须在游戏主循环中每帧调用 XIM::poll(),SDK 的所有回调和事件均在 poll 内触发:

C++
// Cocos2d-x update 回调中
void GameScene::update(float dt) {
    XIM::poll();
}

查询当前连接状态:

C++
ConnState state = XIM::connState();
// Disconnected / Connecting / Connected

登录与登出

C++ SDK 使用异步回调模式。Callback 签名为 std::function<void(int code, const std::string& msg)>,code 为 0 表示成功。

C++
XIM::login("user_001", "token", [](int code, const std::string& msg) {
    if (code == 0) {
        // 登录成功,自动拉取离线消息
    } else {
        // 登录失败,msg 为错误描述
    }
}, "张三", "https://example.com/avatar.png");
参数类型说明
uidconst string&用户 ID,只允许字母、数字、下划线、连字符
tokenconst string&鉴权 token,传空字符串时服务端不做鉴权
cbCallback结果回调
nicknameconst string&可选,登录时上报的昵称
avatarUrlconst string&可选,登录时上报的头像 URL

登出:

C++
XIM::logout([](int code, const std::string& msg) {
    // 登出完成
});

事件监听

C++ SDK 通过 Event 枚举 + const void* 指针传递事件数据。回调签名为 std::function<void(Event evt, const void* data)>,按 Event 类型做 static_cast 取出数据。

C++
XIM::on(Event::NewMessages, [](Event evt, const void* data) {
    auto* d = static_cast<const NewMessagesData*>(data);
    for (auto& msg : d->messages) {
        // 处理新消息
    }
});

XIM::on(Event::Connected, [](Event evt, const void* data) {
    // 连接已建立
});

XIM::on(Event::Disconnected, [](Event evt, const void* data) {
    auto* d = static_cast<const DisconnectedData*>(data);
    // d->code, d->reason
});

// 取消监听
XIM::off(Event::NewMessages);
Event 枚举事件数据类型说明
Connecting-正在连接
Connected-登录成功,连接已建立
DisconnectedDisconnectedData*连接断开(code + reason)
KickedOffline-被踢下线
NewMessagesNewMessagesData*收到新消息(含离线消息批量推送)
MessageRecalledMessageIdData*消息被撤回
MessageUpdatedMessageIdData*消息状态更新
ConversationUpdatedConversationData*会话列表有更新
GroupMemberJoinedGroupEventData*群成员加入
GroupMemberLeftGroupEventData*群成员离开
GroupDismissedGroupEventData*群组被解散
BinaryMessageBinaryMessageData*收到二进制透传消息 (type=5)
ChannelDataChannelDataEvent*收到频道数据帧
ChannelTextChannelTextEvent*收到频道文本消息
ChannelMemberJoinedChannelMemberEvent*有人加入频道
ChannelMemberLeftChannelMemberEvent*有人离开频道
ChannelDestroyedChannelDestroyedEvent*频道被销毁
ChannelFrameAckChannelFrameAckEvent*频道帧确认(reliable 模式)

消息发送

发送接口使用 SendCallback,签名为 std::function<void(int code, const std::string& errMsg, const Message& msg)>,成功时 msg 包含服务端分配的 id 和时间戳。

发送文本消息

C++
XIM::sendText("user_002", "你好", 1, [](int code, const std::string& err, const Message& msg) {
    if (code == 0) {
        // msg.id 为服务端分配的消息 ID
    }
});

发送图片消息

C++
XIM::sendImage("user_002", "/path/to/image.png", 1,
    [](int code, const std::string& err, const Message& msg) {
        // SDK 自动上传图片、获取宽高、封装 content JSON
    });

发送语音消息

C++
XIM::sendVoice("user_002", "/path/to/voice.aac", 5, 1,
    [](int code, const std::string& err, const Message& msg) {
        // durationSec=5 表示语音时长 5 秒
    });

发送二进制透传消息

C++
uint8_t payload[] = {0x01, 0x02, 0x03};
XIM::sendData("user_002", payload, sizeof(payload), 1, 0,
    [](int code, const std::string& err, const Message& msg) {
        // flags=0 正常持久化; flags=1 不持久化(skip_persist)
    });

撤回消息

C++
XIM::recall("msg_id_xxx", [](int code, const std::string& msg) {
    // 撤回成功后,对方收到 Event::MessageRecalled
});

标记已读

C++
XIM::markRead("user_002", 1); // convType: 1=单聊 2=群聊
方法参数说明
sendTexttargetId, content, convType=1, SendCallback发送文本消息
sendImagetargetId, filePath, convType=1, SendCallback发送图片(自动上传)
sendVoicetargetId, filePath, durationSec, convType=1, SendCallback发送语音消息
sendDatatargetId, data, len, convType=1, flags=0, SendCallback发送二进制透传消息
recallmsgId, Callback撤回已发送消息
markReadtargetId, convType=1标记会话已读

消息接收

消息通过事件驱动接收,所有事件回调在 poll() 中触发。必须每帧调用 poll(),否则不会收到任何回调。

C++
XIM::on(Event::NewMessages, [](Event evt, const void* data) {
    auto* d = static_cast<const NewMessagesData*>(data);
    for (auto& msg : d->messages) {
        if (msg.type == 1) {
            // 文本消息: msg.content
        } else if (msg.type == 2) {
            // 图片消息: msg.content 为 JSON {"url":"...","w":640,"h":480}
        } else if (msg.type == 3) {
            // 语音消息: msg.content 为 JSON {"url":"...","duration":5}
        }
    }
});

// 二进制消息单独事件
XIM::on(Event::BinaryMessage, [](Event evt, const void* data) {
    auto* d = static_cast<const BinaryMessageData*>(data);
    // d->payload, d->payloadLen, d->senderId, d->convId
});

// 消息撤回事件
XIM::on(Event::MessageRecalled, [](Event evt, const void* data) {
    auto* d = static_cast<const MessageIdData*>(data);
    // d->msgId
});

poll() 在 Cocos2d-x 中的典型用法:

C++
bool AppDelegate::applicationDidFinishLaunching() {
    auto scene = Scene::create();
    scene->scheduleUpdate();  // 开启 update
    // ...
}

void MyScene::update(float dt) {
    XIM::poll();  // 每帧调用,驱动所有回调和事件
}
Message 字段类型说明
idstring服务端消息 ID
convIdstring会话 ID
targetIdstring单聊=对方 uid,群聊=groupId
senderIdstring发送方 uid
typeint1=文本,2=图片,3=语音,4=自定义,5=二进制
contentstring消息内容(文本或 JSON)
timestampint64_t服务端时间戳(毫秒)
statusint0=发送中,1=成功,2=失败,3=已读
seqint64_t消息序列号(服务端分配,用于排序和 gap 检测)
payloadvector<uint8_t>type=5 时的原始二进制数据
flagsuint32_tbit0: skip_persist (1=不持久化)
extrastring开发者自定义扩展 JSON,SDK 透传不解析

历史消息

C++
XIM::getHistory("user_002", 1, 0, 20, [](int code, HistoryResult result) {
    if (code == 0) {
        for (auto& msg : result.messages) {
            // 处理历史消息
        }
        if (result.hasMore) {
            // 还有更多历史消息,可继续拉取
        }
    }
});
参数类型默认值说明
targetIdconst string&必填对方 uid 或 groupId
convTypeint必填1=单聊,2=群聊
beforeTimeint64_t0拉取此时间戳之前的消息(毫秒),0 表示从最新开始
countint20每次拉取条数

下拉加载更多:

C++
int64_t minTime = 0;
bool hasMore = true;

void loadOlder() {
    if (!hasMore) return;
    XIM::getHistory("user_002", 1, minTime, 20, [&](int code, HistoryResult result) {
        if (code == 0 && !result.messages.empty()) {
            minTime = result.messages.front().timestamp;
        }
        hasMore = result.hasMore;
    });
}

会话管理

C++
// 获取会话列表
XIM::getConversations([](int code, std::vector<Conversation> convs) {
    for (auto& conv : convs) {
        // conv.targetId, conv.name, conv.unreadCount, conv.lastMsg
    }
});

// 删除会话
XIM::deleteConversation("user_002", 1, [](int code, const std::string& msg) {
    // 删除完成
});

// 标记已读
XIM::markRead("user_002", 1);

监听会话更新:

C++
XIM::on(Event::ConversationUpdated, [](Event evt, const void* data) {
    auto* d = static_cast<const ConversationData*>(data);
    // d->conversation.targetId, d->conversation.unreadCount
});
Conversation 字段类型说明
idstring会话 ID
typeint1=单聊,2=群聊,3=聊天室
targetIdstring单聊=对方 uid,群聊=groupId
namestring会话显示名
unreadCountint未读消息数
lastMsgMessage最后一条消息

群组

C++
// 创建群组
XIM::createGroup("我的群", {"user_002", "user_003"}, [](int code, GroupInfo info) {
    if (code == 0) {
        // info.id, info.name, info.memberCount
    }
});

// 加入群组
XIM::joinGroup("g_xxxxxxxx", [](int code, const std::string& msg) {});

// 离开群组
XIM::leaveGroup("g_xxxxxxxx", [](int code, const std::string& msg) {});

群事件监听:

C++
XIM::on(Event::GroupMemberJoined, [](Event evt, const void* data) {
    auto* d = static_cast<const GroupEventData*>(data);
    // d->groupId, d->userId
});
XIM::on(Event::GroupMemberLeft, [](Event evt, const void* data) { ... });
XIM::on(Event::GroupDismissed, [](Event evt, const void* data) { ... });
方法参数说明
createGroupname, memberIds, callback创建群组并拉入初始成员
joinGroupgroupId, Callback加入群组
leaveGroupgroupId, Callback退出群组

实时频道

频道是 fire-and-forget 的实时数据通道,不持久化,适合游戏对局、实时同步等场景。

C++
// 加入频道
XIM::channelJoin("room_001", [](int code, ChannelInfo info) {
    // info.channelId, info.memberUids, info.members, info.recentMessages
});

// 发送文本消息(自动 UTF-8 编码,reliable=true)
XIM::channelSendText("room_001", "你好大家");

// 发送二进制数据
uint8_t data[] = {0x01, 0x02};
XIM::channelSend("room_001", data, sizeof(data), false, 0);
// reliable=false 不保证到达;contentType 自定义

// 离开频道
XIM::channelLeave("room_001");

频道事件:

C++
XIM::on(Event::ChannelText, [](Event evt, const void* data) {
    auto* d = static_cast<const ChannelTextEvent*>(data);
    // d->channelId, d->senderId, d->text, d->seqNum, d->timestamp
});

XIM::on(Event::ChannelData, [](Event evt, const void* data) {
    auto* d = static_cast<const ChannelDataEvent*>(data);
    // d->channelId, d->senderId, d->data, d->dataLen, d->contentType
});

XIM::on(Event::ChannelMemberJoined, [](Event evt, const void* data) {
    auto* d = static_cast<const ChannelMemberEvent*>(data);
    // d->channelId, d->userId, d->nickname
});

XIM::on(Event::ChannelMemberLeft, [](Event evt, const void* data) { ... });
XIM::on(Event::ChannelDestroyed, [](Event evt, const void* data) { ... });
方法参数说明
channelJoinchannelId, callback加入频道,返回 ChannelInfo
channelLeavechannelId, Callback离开频道
channelSendchannelId, data, len, reliable=false, contentType=0发送二进制帧
channelSendTextchannelId, text发送文本消息(reliable=true)

用户资料

C++
// 设置自己的资料
UserProfile profile;
profile.nickname  = "张三";
profile.avatarUrl = "https://example.com/avatar.png";
profile.gender    = 1;  // 0=未知 1=男 2=女
profile.signature = "这是签名";
XIM::setMyProfile(profile, [](int code, const std::string& msg) {});

// 批量获取用户资料
XIM::getUserProfiles({"user_001", "user_002"},
    [](int code, std::vector<UserProfile> profiles) {
        for (auto& p : profiles) {
            // p.uid, p.nickname, p.avatarUrl, p.gender, p.signature, p.moreInfo
        }
    });

// 获取缓存的用户资料(纯本地,无网络请求)
UserProfile cached = XIM::getCachedProfile("user_001");
方法参数说明
setMyProfileUserProfile, Callback设置当前用户资料
getUserProfilesvector<string>, callback批量获取用户资料(网络请求)
getCachedProfileuid从本地缓存获取(同步,不发起网络请求)

文件缓存

SDK 内置文件缓存管理,按类型(图片/语音/文件)分目录存储。autoDownload=true 时收到图片和语音会自动下载。

C++
// 获取缓存根目录
std::string dir = XIM::cacheDir();

// 获取指定类型的缓存目录
std::string imgDir = XIM::cacheDirForType(CacheType::Image);

// 获取某个 URL 的本地缓存路径(未下载时为空字符串)
std::string localPath = XIM::cachedPath("https://oss.example.com/a.jpg", CacheType::Image);

// 手动下载文件到缓存
XIM::downloadFile("https://oss.example.com/a.jpg", CacheType::Image,
    [](int code, const std::string& localPath) {
        if (code == 0) {
            // localPath 为本地文件路径
        }
    });

// 清除所有缓存
XIM::clearCache();

// 清除指定类型缓存
XIM::clearCache(CacheType::Voice);

// 删除单个缓存文件
bool ok = XIM::removeCache("https://oss.example.com/a.jpg", CacheType::Image);
CacheType说明
Image图片缓存
Voice语音缓存
File文件缓存

类型定义

C++
namespace xspace {

// ─── 回调类型 ───
using Callback = std::function<void(int code, const std::string& msg)>;
using SendCallback = std::function<void(int code, const std::string& errMsg,
                                        const Message& msg)>;
using EventHandler = std::function<void(Event evt, const void* data)>;

// ─── 枚举 ───
enum class LogLevel  { Debug, Info, Warn, Error };
enum class ConnState { Disconnected, Connecting, Connected };
enum class CacheType { Image = 0, Voice = 1, File = 2 };

enum class Event {
    Connecting, Connected, Disconnected, KickedOffline,
    NewMessages, MessageRecalled, MessageUpdated,
    ConversationUpdated,
    GroupMemberJoined, GroupMemberLeft, GroupDismissed,
    OnRemoteTalkStart, OnRemoteTalkStop, VoiceData,
    BinaryMessage,
    ChannelData, ChannelMemberJoined, ChannelMemberLeft,
    ChannelDestroyed, ChannelFrameAck, ChannelText,
};

// ─── 配置 ───
struct Config {
    std::string appId;
    std::string server;
    std::string dataDir;
    LogLevel logLevel = LogLevel::Warn;
    bool autoDownload = true;
};

// ─── 数据模型 ───
struct Message {
    std::string id;
    std::string convId;
    std::string targetId;
    std::string senderId;
    int type = 0;             // 1=文本 2=图片 3=语音 4=自定义 5=二进制
    std::string content;
    int64_t timestamp = 0;
    int status = 0;           // 0=发送中 1=成功 2=失败 3=已读
    int64_t seq = 0;
    std::vector<uint8_t> payload;
    uint32_t flags = 0;       // bit0: skip_persist
    std::string extra;        // 开发者自定义扩展 JSON
};

struct Conversation {
    std::string id;
    std::string targetId;
    int type = 0;             // 1=单聊 2=群聊 3=聊天室
    std::string name;
    Message lastMsg;
    int unreadCount = 0;
};

struct GroupInfo {
    std::string id;
    std::string name;
    std::string ownerId;
    int memberCount = 0;
};

struct UserProfile {
    std::string uid;
    std::string nickname;
    std::string avatarUrl;
    int gender = 0;           // 0=未知 1=男 2=女
    std::string signature;
    std::string moreInfo;
};

struct ChannelInfo {
    std::string channelId;
    std::vector<std::string> memberUids;
    std::vector<ChannelMember> members;
    std::string metadata;
    std::vector<ChannelMessage> recentMessages;
};

struct HistoryResult {
    std::vector<Message> messages;
    bool hasMore = false;
};

// ─── 事件数据结构 ───
struct DisconnectedData   { int code; std::string reason; };
struct NewMessagesData    { std::vector<Message> messages; };
struct MessageIdData      { std::string msgId; };
struct ConversationData   { Conversation conversation; };
struct GroupEventData     { std::string groupId; std::string userId; };

struct BinaryMessageData {
    std::string msgId, convId, senderId;
    const uint8_t* payload; size_t payloadLen; uint32_t flags;
};

struct ChannelDataEvent {
    std::string channelId, senderId;
    uint32_t seqNum;
    const uint8_t* data; size_t dataLen;
    bool reliable; int64_t timestamp; uint32_t contentType;
};

struct ChannelTextEvent {
    std::string channelId, senderId, text;
    uint32_t seqNum; int64_t timestamp;
};

struct ChannelMemberEvent {
    std::string channelId, userId, nickname, avatarUrl;
};

struct ChannelDestroyedEvent { std::string channelId, reason; };
struct ChannelFrameAckEvent  { std::string channelId; uint32_t seqNum; };

} // namespace xspace
XIM — 为游戏而生的即时通讯 SDK