默认监听地址以 server/config/config.ini [server] 为准(当前 0.0.0.0:6000)。传输为 裸 TCP,应用层自行组包,不是 HPSocket PACK 模型。
本文与 Qt 客户端界面一一对应:登录、注册、验证码、找回/修改密码、会话列表、好友列表、群聊、发消息、退出登录。
每个消息 = 12 字节包头 + body。多包可粘在同一 TCP 流里,按 length 切包。
| 偏移 | 长度 | 字段 | 说明 |
|---|---|---|---|
| 0 | 2 | magic |
固定 0x494D(ASCII IM),配置里写十六进制字符串 0x494D |
| 2 | 1 | type |
业务大类,见第 2 节 |
| 3 | 1 | cmd |
该 Type 下的命令号 |
| 4 | 4 | seq |
调用方自增序号,响应原样带回;服务端推送为 0 |
| 8 | 4 | length |
仅 body 字节数,不含包头 |
| 12 | length |
body |
UTF-8 JSON,可为 0 字节 |
length 上限:config.ini [protocol] max_packet,默认 1048576。聊天图片/文件走 COS 直传,不再占用 TCP 包体。magic 不对,或 length > max_packet:服务端立即断开type / cmd:不回包、不断开(仅打日志)out = []
out += be16(0x494D)
out += be8(type)
out += be8(cmd)
out += be32(seq)
out += be32(len(body))
out += body # UTF-8 bytes
magic == 0x494Dlength 字节 bodyseq 匹配请求;可并发多个未完成请求seq=0,按 type+cmd 处理,不要当请求应答线上为 1 字节 type + 1 字节 cmd(原 16 位 cmd 槽拆开)。Dispatcher 按 type 进业务 Handler,Handler 再解析 cmd。
| Type | 值 | Handler | Cmd |
|---|---|---|---|
| System | 1 | 系统 | Heartbeat(1),SetConfig(13)(应答带 limits),推送 Kicked(101) |
| Account | 2 | 账户 | Login(2) Logout(3) Register(5) SendCode(6) ResetPassword(7) ChangePassword(8) GetPrivacy(17) SetPrivacy(18) SetProfile(27) GetAvatar(28) |
| Friend | 3 | 好友 | FriendList(9) AddFriend(12) SearchUser(14) FriendRequestList(15) HandleFriendRequest(16) DeleteFriend(24) SetRemark(25) GetProfile(26),推送 FriendRequestNotify(102) FriendAcceptedNotify(103) PresenceNotify(104) |
| Message | 4 | 消息 | Chat(4) ConvList(10) MsgHistory(11) DeleteConv(22) SetUnread(23) PutFile(29) GetFile(30) DeleteMsg(36),推送 ChatNotify(100)。群消息带 group_id |
| Favorite | 6 | 收藏夹 | FavoriteAdd(31) FavoriteList(32) FavoriteGet(33) FavoriteUpdate(34) FavoriteDelete(35) |
| Group | 7 | 群聊 | GroupCreate(37) GroupInfo(38) GroupMembers(39) GroupUpdate(40) GroupInvite(41) GroupSearch(42) GroupJoin(43) GroupLeave(44) GroupDismiss(45) GroupKick(46) GroupSetAdmin(47) GroupTransfer(48) GroupRequestList(49) GroupHandleRequest(50) GroupList(51),推送 GroupRequestNotify(105) GroupChangeNotify(106) |
与 Client 界面的对应关系:
| cmd | 名称 | 登录 | Client | 说明 |
|---|---|---|---|---|
| 1 | Heartbeat | 否 | 建议 30s 一次 | 心跳 |
| 2 | Login | 否 | 登录窗 | 账号(username)+ 密码登录 |
| 3 | Logout | 是 | 设置 → 退出登录 | 清会话,不断开 TCP |
| 4 | Chat | 是 | 聊天输入框发送 | 文本 / 表情 / 文件 / 名片;群聊带 group_id |
| 5 | Register | 否 | 注册账号 | 注册 |
| 6 | SendCode | 否 | 获取验证码 | 短信验证码 |
| 7 | ResetPassword | 否 | 忘记密码 | 用验证码重置 |
| 8 | ChangePassword | 是 | 设置 → 通用 → 修改密码 | 校验旧密码后改密 |
| 9 | FriendList | 是 | 好友页 | 好友列表 |
| 10 | ConvList | 是 | 消息页会话列表 | 会话摘要 + 未读 |
| 11 | MsgHistory | 是 | 点开会话 | 历史消息,并清未读 |
| 12 | AddFriend | 是 | 好友页 + | 按账号发起申请,对方同意后才成为好友 |
| 14 | SearchUser | 是 | 添加好友对话框 | 按账号/手机号搜索 |
| 15 | FriendRequestList | 是 | 新的朋友 | 待处理申请 |
| 16 | HandleFriendRequest | 是 | 新的朋友 同意/拒绝 | 1同意 2拒绝 |
| 17 | GetPrivacy | 是 | 设置 → 隐私 | 读取隐私开关 |
| 18 | SetPrivacy | 是 | 设置 → 隐私 | 保存隐私开关 |
| 19 | StickerPackList | 是 | 聊天表情面板 | 表情包列表 |
| 20 | StickerPack | 是 | 聊天表情面板 | 某包下表情元数据 |
| 21 | GetSticker | 是 | 表情缓存 | 下载表情图片 |
| 22 | DeleteConv | 是 | 会话右键 → 删除 | 删除自己的会话摘要,并标记自己侧消息已删除 |
| 23 | SetUnread | 是 | 会话右键 → 已读/未读 | unread=0 已读,unread=1 未读 |
| 24 | DeleteFriend | 是 | 好友右键 → 删除 | 双向解除好友,并标记自己侧消息已删除 |
| 25 | SetRemark | 是 | 好友右键 → 备注 | 只改自己这一侧的备注 |
| 26 | GetProfile | 是 | 好友右键 → 查看名片 | 按 uid 拉资料 |
| 27 | SetProfile | 是 | 设置 → 资料 | 改昵称、签名、头像 |
| 28 | GetAvatar | 是 | 头像缓存 | 按 uid 或 group_id 下载头像 JPEG |
| 29 | PutFile | 是 | 聊天 → 图片/文件 | 申请 COS 临时上传凭证,返回 file_id + token |
| 30 | GetFile | 是 | 点击文件气泡 | 按 file_id 下载 |
| 31 | FavoriteAdd | 是 | 消息右键 → 收藏 / 新建笔记 | 收藏会话内容或自定义笔记 |
| 32 | FavoriteList | 是 | 左侧收藏夹 | 收藏列表 |
| 33 | FavoriteGet | 是 | 点开一条收藏 | 收藏详情 |
| 34 | FavoriteUpdate | 是 | 编辑笔记 | 只允许改文本/笔记 |
| 35 | FavoriteDelete | 是 | 收藏右键 → 删除 | 删除自己的收藏及 COS 副本 |
| 36 | DeleteMsg | 是 | 聊天记录右键 → 删除 / 多选删除 | 仅标记自己侧消息已删除 |
| 37 | GroupCreate | 是 | 好友页 + → 发起群聊 | 选好友建群 |
| 38 | GroupInfo | 是 | 群资料 | 群名、进群方式、人数、头像版本、自己的角色 |
| 39 | GroupMembers | 是 | 群资料成员网格 | 成员列表 |
| 40 | GroupUpdate | 是 | 群资料 | 改群名 / join_mode / 群头像 |
| 41 | GroupInvite | 是 | 群资料 → 邀请 | 拉好友进群 |
| 42 | GroupSearch | 是 | 添加好友对话框搜群号 | 按群号精确搜索 |
| 43 | GroupJoin | 是 | 群名片 / 搜群号 | 按 join_mode 直接进或提交申请 |
| 44 | GroupLeave | 是 | 群资料 → 退出 | 群主须先转让或解散 |
| 45 | GroupDismiss | 是 | 群资料 → 解散 | 仅群主 |
| 46 | GroupKick | 是 | 群成员右键 → 移除 | 群主可踢管理员/成员,管理员只能踢成员 |
| 47 | GroupSetAdmin | 是 | 群成员右键 | 仅群主设置/取消管理员 |
| 48 | GroupTransfer | 是 | 群资料 → 转让 | 仅群主 |
| 49 | GroupRequestList | 是 | 群资料 → 入群申请 | 待处理申请 |
| 50 | GroupHandleRequest | 是 | 入群申请 同意/拒绝 | 1同意 2拒绝 |
| 51 | GroupList | 是 | 好友页 → 群聊 | 我加入的群 |
| 100 | ChatNotify | 推送 | 聊天区收消息 | 对方/群成员在线时推送 |
| 101 | Kicked | 推送 | 被挤下线 | 同一账号在别处登录 |
| 102 | FriendRequestNotify | 推送 | 好友申请提示 | 收到加好友申请 |
| 103 | FriendAcceptedNotify | 推送 | 好友同意提示 | 对方同意申请 |
| 104 | PresenceNotify | 推送 | 会话/好友头像绿点 | 好友上线/离线 |
| 105 | GroupRequestNotify | 推送 | 入群申请提示 | 群主/管理员收到申请 |
| 106 | GroupChangeNotify | 推送 | 群资料/会话 | event: join / leave / kick / dismiss / role / info / invite |
未登录可发:Heartbeat / SetConfig / Login / Register / SendCode / ResetPassword。
连接建立后客户端应立刻发送 SetConfig,服务端按该连接语言返回 msg,并在应答中下发 [limits](账号/密码长度、文件头像上限等)。客户端不再在本地 config.ini 保留这些限制。其余请求若未登录,回 code=2。
请求的应答 body 均为 JSON 对象:
| 字段 | 类型 | 说明 |
|---|---|---|
code |
number | 0 成功,非 0 失败,见 3.1 |
msg |
string | 文本说明 |
| 其它 | — | 按命令扩展 |
响应 type、cmd、seq 与请求相同(推送除外)。
| code | 含义 |
|---|---|
| 0 | 成功 |
| 1 | 参数/JSON 错误 |
| 2 | 未登录 |
| 3 | 账号或密码错误 / 手机号不匹配 |
| 4 | 用户不存在 |
| 5 | 账号已存在 |
| 6 | 手机号已注册 |
| 7 | 验证码错误或过期 |
| 8 | 密码长度不符合 6-20 |
| 9 | 不允许(系统账号、加自己等) |
| 10 | 数据库不可用或 SQL 失败 |
| 11 | 账号格式不符合 4-16 位字母或数字 |
| 12 | 验证码发送过于频繁 |
| 13 | 账号已禁用 |
字段:登录用 uid;注册仍用 account 作为用户名(展示/加好友资料,不用于登录)。
请求 body:空,或任意 JSON(服务端不解析)。
{"code":0,"msg":"pong","ts":1788146838644}
| 字段 | 类型 | 说明 |
|---|---|---|
ts |
number | 服务端毫秒时间戳 |
Type=System。连接成功后立即发送,上报客户端语言并拉取服务端配置。
请求:
{"lang":"zh"}
lang:zh 或 en(决定本连接回包 msg 语言)。
成功:
{
"code":0,
"msg":"成功",
"lang":"zh",
"limits":{
"account_min":4,
"account_max":16,
"password_min":6,
"password_max":20,
"phone_len":11,
"nickname_max":32,
"signature_max":64,
"content_max":4096,
"preview_max":120,
"history_default":50,
"history_max":100,
"avatar_max":81920,
"file_max":524288,
"group_max_members":200,
"group_name_max":32,
"group_max_admins":10,
"code_len":6,
"max_packet":1048576,
"heartbeat_sec":30,
"interval_sec":60
},
"cdn":{
"domain":"https://cdn.example.com"
},
"update":{
"version":"1.0.1",
"url":"https://cdn.example.com/NIM-1.0.1.zip",
"notes":"修复文件上传"
}
}
limits 与服务端 config.ini 的 [limits]、[protocol] max_packet、[client] heartbeat_sec、[sms] code_len / interval_sec 一致。客户端用其做输入长度和本地预校验;最终仍以服务端校验为准。
cdn.domain 只来自 [cdn] domain,不会按 COS 自动推导。未配置时下发空串,客户端不走 HTTP 下载。
update 来自服务端 [update]:version 为最新客户端版本,url 为 ZIP 安装包地址,notes 为说明。客户端用本地 [app] version 比较,更高则提示更新。ZIP 须把安装目录内容放在根(NIM.exe 在压缩包根目录);客户端先下载 ZIP,再复制一份自身到临时目录并以 --apply-update 用自带 3rdparty/7z/x64/7za.exe 解压覆盖原目录(失败再回退系统 tar / Expand-Archive),然后拉起新程序并清理临时副本。url 为空时客户端忽略。
对应登录窗:账号(im_user.username)+ 密码。请求字段为 account(兼容 username),不再使用 uid 登录。
请求:
{"account":"alice","password":"******"}
成功:
{"code":0,"msg":"ok","uid":10002,"account":"alice","nickname":"Alice","phone":"13800138000","signature":"","avatar_ver":0}
同一账号在新连接登录成功时,旧连接会收到 Kicked 推送并被断开。系统账号(文件传输助手)不可登录。
请求 body 可空。成功后该连接变为未登录,TCP 不断开。关连接无需发 Logout。
对应注册窗:昵称、账号、手机号、验证码、密码。须先对同一手机号发送 scene=register 的验证码。
请求:
{
"nickname":"Alice",
"account":"alice",
"phone":"13800138000",
"sms_code":"123456",
"password":"******"
}
约束(与客户端提示一致,长度见 [limits]):
1 开头成功:
{"code":0,"msg":"ok","uid":10002,"account":"alice","nickname":"Alice"}
注册成功后自动加好友 文件传输助手(uid=1)。
对应「获取验证码」。
{"phone":"13800138000","scene":"register"}
| 字段 | 说明 |
|---|---|
scene |
register 注册;reset 找回密码。缺省为 register |
reset |
要求该手机号已注册 |
成功:
{"code":0,"msg":"ok","ttl":300,"sms_code":"123456"}
sms_code 仅在 config.ini [sms] echo_code=1 时返回,便于客户端联调。生产环境必须改为 0。默认 60 秒内不可对同一手机号+场景重复发送。
对应忘记密码:uid + 绑定手机号 + 验证码 + 新密码。验证码 scene 必须是 reset。
{"uid":10002,"phone":"13800138000","sms_code":"123456","password":"newpass"}
对应设置里的修改密码,须已登录。
{"old_password":"oldpass","password":"newpass"}
对应左侧「好友」列表。请求 body 可空。
{
"code":0,
"msg":"ok",
"pending":1,
"list":[
{"uid":1,"account":"filehelper","nickname":"文件传输助手","signature":"文件助手","online":0},
{"uid":10003,"account":"bob","nickname":"Bob","signature":"","online":1}
]
}
online:1 在线,0 离线。这是拉取瞬间的快照;好友上线/离线时服务端会再推 PresenceNotify(cmd=104),客户端应立刻更新会话列表与好友列表头像绿点。pending 为待处理的好友申请数。
按账号精确搜索,11 位手机号则按手机号搜。受对方隐私设置约束:不允许被搜到时返回用户不存在。须已登录。
{"keyword":"alice"}
成功:
{
"code":0,
"uid":10003,
"account":"alice",
"nickname":"Alice",
"signature":"",
"relation":0,
"allow_add":1
}
relation:0 非好友,1 已发出申请,2 已是好友,3 对方已申请加我。
按账号或 uid 发起好友申请,须对方同意后才成为好友。对方在线会收到 FriendRequestNotify 推送。
{"account":"alice","message":"我是同事,请通过"}
message 为选填验证信息,最长约 50 字。
成功时 relation=1 表示申请已发出。若对方刚好也申请了自己,则直接成为好友(relation=2)。
列出待处理申请;action=1 同意,action=2 拒绝。同意后双向写入好友关系,并向对方推送 FriendAcceptedNotify。
{"id":12,"action":1}
设置是否允许通过账号、手机号被搜索,以及是否允许他人添加。缺省字段保持原值。
{"allow_search_account":1,"allow_search_phone":1,"allow_add_friend":1}
Type=Sticker(5)。表情包由服务端下发,客户端按 sha256 缓存。发消息时 msg_type=2,content 为 {"pack_id":1,"code":"smile"}。
GetSticker 成功:
{"id":1,"pack_id":1,"code":"smile","name":"微笑","sha256":"...","mime":"image/png","data":"<base64>"}
对应左侧「消息」会话列表。
{
"code":0,
"msg":"ok",
"list":[
{
"conv_type":0,
"peer_uid":10003,
"account":"bob",
"nickname":"Bob",
"signature":"",
"last_msg_id":88,
"preview":"hello",
"ts":1788146838644,
"unread":2,
"online":1,
"avatar_ver":1
},
{
"conv_type":1,
"group_id":5,
"name":"周末局",
"member_count":6,
"last_msg_id":12,
"preview":"hello",
"ts":1788146838644,
"unread":1,
"avatar_ver":1
}
]
}
conv_type:0 私聊(peer_uid),1 群(group_id + name)。缺省按私聊处理。unread 对应会话头像角标。私聊 online:1 在线,0 离线。实时变化见 PresenceNotify。群会话无 online。群会话的 avatar_ver 为群头像版本,0 表示未设置。
点开会话时拉取历史,并将会话未读清零。
{"peer_uid":10003,"last_id":0,"limit":50}
群会话用 group_id 替代 peer_uid:
{"group_id":5,"last_id":0,"limit":50}
| 字段 | 说明 |
|---|---|
last_id |
0 表示最新一页;大于 0 时拉比该 id 更早的消息 |
limit |
默认与上限见 [limits] history_default / history_max(50 / 100) |
{
"code":0,
"msg":"ok",
"list":[
{"msg_id":87,"from_uid":10003,"to_uid":10002,"msg_type":1,"content":"hi","ts":1788146800000},
{"msg_id":88,"from_uid":10002,"to_uid":10003,"msg_type":1,"content":"hello","ts":1788146838644}
]
}
列表按时间正序(旧 → 新)。from_uid 等于当前用户即自己发的气泡。
对应输入框发送。msg_type=1 文本;msg_type=2 表情,content 为 {"pack_id":1,"code":"smile"};msg_type=3 文件/图片,content 为 {"file_id":12,"name":"a.pdf","size":1024,"mime":"application/pdf"}(须先 PutFile,再用 token 直传 COS,对象落盘后再发 Chat;服务端会 HEAD 校验);msg_type=4 名片,好友名片 content 为 {"uid":10003,"account":"bob","nickname":"Bob"},群名片为 {"kind":"group","group_id":5,"name":"周末局","member_count":6}(缺省 kind 视为好友名片);msg_type=5 群系统通知(服务端写入,客户端只展示)。文本最长、文件体积上限见服务端 [limits] content_max / file_max(连接后经 SetConfig 下发给客户端)。
私聊:
{"to_uid":10003,"msg_type":1,"content":"hello"}
群聊(须为成员;有 group_id 时忽略 to_uid):
{"group_id":5,"msg_type":1,"content":"hello"}
成功:
{"code":0,"msg":"ok","msg_id":88,"ts":1788146838644}
对方在线时,服务端另推 ChatNotify(cmd=100,seq=0):
{
"msg_id":88,
"from_uid":10002,
"from_account":"alice",
"from_nickname":"Alice",
"to_uid":10003,
"msg_type":1,
"content":"hello",
"ts":1788146838644
}
双方会话预览会更新;接收方 unread+1。群消息 ChatNotify 另带 group_id,向群内其他在线成员 fan-out。
删除当前用户的会话列表项,并标记该用户侧的消息为已删除(对方聊天记录不受影响)。再次聊天会从空白开始。
{"peer_uid":10003}
群会话:{"group_id":5}。只清自己的会话摘要(群 inbox)并隐藏自己侧消息,不退群。
删除当前用户聊天记录中的若干条消息(软删除,对方仍可见)。ids 最多 100 个,且须属于该 peer_uid 会话。
{"peer_uid":10003,"ids":[87,88]}
群会话用 group_id 替代 peer_uid。
成功:peer_uid、ids、以及更新后的会话预览 last_msg_id、preview、ts(无剩余消息时为空/0)。
{"peer_uid":10003,"unread":0}
群会话:{"group_id":5,"unread":0}。
unread=0 标记已读;unread>=1 标记未读。点开会话拉历史(MsgHistory)仍会清未读。
{"uid":10003}
删除 im_friend 双向关系,并标记当前用户侧消息为已删除、清除自己的会话摘要。对方聊天记录不受影响。不是好友时 code=4。
{"uid":10003,"remark":"老王"}
remark 最长 32 字节,空字符串表示清除备注。会话列表展示优先用备注。
{"uid":10003}
成功:
{"code":0,"uid":10003,"account":"bob","nickname":"Bob","signature":"","avatar_ver":1,"relation":2,"remark":"老王"}
改自己的昵称、签名;可选带 JPEG 头像(base64)。头像须为 JPEG,体积不超过 avatar_max。
{"nickname":"Alice","signature":"hello","avatar":"/9j/..."}
成功返回 nickname、signature、avatar_ver。
按用户或群下载 JPEG。group_id 优先于 uid。
{"uid":10003}
{"group_id":5}
成功:uid 或 group_id、ver、mime、url(COS 对象键;无 CDN 时另带 data JPEG base64)。未设置头像时 code=4。
聊天图片/文件不再经 TCP 上传。PutFile 只申请对象键和限时凭证,客户端用返回的 token 直接 PUT 到腾讯云 COS,然后再用 Chat msg_type=3 发送该 file_id。
请求(只传元数据,不要带 data):
{"name":"a.pdf","mime":"application/pdf","size":1024}
| 字段 | 类型 | 说明 |
|---|---|---|
name |
string | 文件名,服务端会去掉路径并截断 |
mime |
string | 可选,缺省 application/octet-stream |
size |
number | 必填,字节数,1 … file_max |
sha256 |
string | 可选,十六进制,最长 64 |
purpose |
string | 可选。favorite 时对象键走 {prefix}/favorites/{uid}/{file_id},供收藏笔记插附件 |
成功:
{
"code":0,
"msg":"ok",
"file_id":12,
"name":"a.pdf",
"size":1024,
"mime":"application/pdf",
"url":"im/files/12.pdf",
"token":{
"tmp_secret_id":"AKIDxxx",
"tmp_secret_key":"xxx",
"session_token":"xxx",
"start_time":1788146838,
"expired_time":1788148638,
"bucket":"examplebucket-1250000000",
"region":"ap-beijing",
"key":"im/files/12.pdf",
"host":"examplebucket-1250000000.cos.ap-beijing.myqcloud.com",
"upload_url":"https://examplebucket-1250000000.cos.ap-beijing.myqcloud.com/im/files/12.pdf",
"method":"PUT",
"authorization":"q-sign-algorithm=sha1&q-ak=...&q-signature=...",
"headers":{
"Host":"examplebucket-1250000000.cos.ap-beijing.myqcloud.com",
"Authorization":"q-sign-algorithm=sha1&...",
"Content-Type":"application/pdf",
"Content-Length":"1024",
"x-cos-security-token":"xxx"
}
}
}
客户端上传(二选一,推荐 HTTP PUT):
PUT token.upload_url,请求体为原始文件字节,请求头必须与 token.headers 逐项一致(含 Content-Length、Content-Type;有 session_token 时必须带 x-cos-security-token)。不要改用 chunked,不要增删已签名头。HTTP 200/204 即成功。tmp_secret_id / tmp_secret_key / session_token,region+bucket,对象键 key,PutObject 上传。url 仍是对象键,与 CDN 拼接方式不变:cdn.domain + "/" + url。凭证默认约 30 分钟有效。STS 申请失败时服务端会回退为永久密钥签发的限时 PUT 签名,此时 tmp_secret_* / session_token 可能为空,客户端仍可用 headers + upload_url 直传。
对象上传完成前不要发 Chat;服务端对 msg_type=3 会 HEAD COS,对象不存在则 code=4。
GetFile 仍可用于无 CDN 时的回退下载;配置了 [cdn] domain 时客户端应走 HTTP 下载,不要再经 TCP 拉文件。
{"file_id":12}
成功:file_id、name、size、mime、data(base64)。
从会话收藏,或新建笔记。图片/文件会 Copy 到 COS {prefix}/favorites/{uid}/{file_id},与聊天文件独立。
会话收藏:
{"msg_type":1,"content":"一段话","src_peer_uid":10003,"src_msg_id":88}
msg_type:1 文本、3 图片/文件(content 为 Chat 同款 JSON,含 file_id)、4 名片。
新建笔记:
{"kind":5,"content":"{\"html\":\"<p>笔记</p>\",\"files\":[12]}"}
成功:id、kind、title。kind:1文本 2图片 3文件 4名片 5笔记。
{"limit":200}
成功:list 数组,每项 id、kind、title、ts。
{"id":1}
成功:id、kind、title、content、src_msg_id、src_peer_uid、ts、updated_ts。
仅文本/笔记。笔记 content 格式同 FavoriteAdd。
{"id":1,"content":"{\"html\":\"<p>改过了</p>\",\"files\":[12]}"}
{"id":1}
只删当前用户这条收藏,以及其 favorites/ 下的 COS 副本。聊天 im/files/ 不动。
{"name":"周末局","uids":[10003,10004]}
uids 为好友 uid,不含自己,至少 1 人。缺省 name 时服务端用成员昵称拼接。创建者为群主,被选好友直接入群。join_mode 默认 1(需审核)。
成功:group_id、name、join_mode、member_count、role。
join_mode:0 无需审核;1 群主或管理员审核;2 外人不可申请(群主/管理员仍可邀请好友)。
{"group_id":5}
GroupInfo 成功:group_id、name、owner_uid、join_mode、member_count、avatar_ver、role(当前用户,非成员为 -1)、pending(群主/管理员可见的待审申请数)。
GroupMembers 须为成员。list 每项:uid、account、nickname、avatar_ver、role(0 成员 / 1 管理员 / 2 群主)。
GroupList 请求可空。list 每项:group_id、name、member_count、join_mode、role、avatar_ver。
群主或管理员。Update 缺省字段保持原值;avatar 为 JPEG 的 base64(与 SetProfile 相同):
{"group_id":5,"name":"新名字","join_mode":0,"avatar":"/9j/..."}
成功返回与 GroupInfo 相同的群字段(含 avatar_ver)。GroupChangeNotify event=info 会带上最新 name / join_mode / avatar_ver。
Invite:
{"group_id":5,"uids":[10005]}
被邀请人须是邀请者的好友。即使 join_mode=2 也可拉人。
按群号(group_id)精确搜索;Join 按 join_mode 直接入群或写入申请。
{"group_id":5,"message":"我是同事"}
Join 成功时 joined=1 表示已入群;joined=0 且 pending=1 表示已提交申请。
{"group_id":5}
Kick 另带 uid。群主不能 Leave(须 Transfer 或 Dismiss)。管理员只能踢普通成员。
仅群主。admin=1 设为管理员,admin=0 取消。转让后原群主变为普通成员。
{"group_id":5,"uid":10003,"admin":1}
{"group_id":5,"uid":10003}
列出待处理入群申请。可带 group_id 只看某一群;不带则列出自己作为群主/管理员的全部待审。action=1 同意,action=2 拒绝。
{"id":8,"action":1}
入群申请推给在线群主/管理员:
{"id":8,"group_id":5,"name":"周末局","from_uid":10006,"from_nickname":"Eve","message":""}
群变更推给在线成员(seq=0):
{"event":"kick","group_id":5,"name":"周末局","uid":10006,"nickname":"Eve"}
event:join / leave / kick / dismiss / role / info / invite。
{"code":0,"msg":"kicked"}
收到后应回到登录窗。随后 TCP 会被服务端断开。
好友上线或离线时,向其在线好友推送(seq=0)。文件传输助手不参与。同一账号被踢后由新连接继续保持在线,不会误推离线。
{"uid":10003,"online":1}
| 字段 | 说明 |
|---|---|
uid |
状态变化的用户 |
online |
1 上线,0 离线 |
登录成功后推 online=1;主动 Logout、连接断开且该 uid 已无其它会话时推 online=0。客户端应同时刷新消息列表与好友列表头像绿点,不必为此再拉 FriendList。
ChatNotify=100、Kicked=101、FriendRequestNotify=102、FriendAcceptedNotify=103、PresenceNotify=104、GroupRequestNotify=105、GroupChangeNotify=106,seq=0客户端只关心 [server] 的 host/port 和 [protocol] 的 magic/max_packet。magic 为十六进制字符串(如 0x494D)。改 magic 后客户端必须同步。
资源下载域名见 [cdn] domain,由 SetConfig 原样下发;未配置则为空。COS 对象键前缀见 [cos] prefix。聊天图片/文件由客户端持 PutFile 下发的临时 token 直传 COS,字节不经过 IM 服务端。
长度、条数、验证码间隔等见 [limits]、[sms]。
[sms] echo_code:开发联调为 1 时,SendCode 响应带回 sms_code;上线改为 0。
新库执行 server/sql/init.sql。已有 im_user / im_message 的库执行 server/sql/migrate.sql(已存在的列报错可忽略)。
| 表 | 用途 |
|---|---|
im_user |
用户(含手机号、签名;status:1 正常 / 0 禁用 / 2 系统账号) |
im_sms_code |
验证码 |
im_friend |
双向好友(含 remark 备注) |
im_message |
消息 |
im_conversation |
会话摘要与未读 |
im_favorite |
收藏(文本/图片/文件/名片/笔记) |
im_group |
群资料(join_mode:0 开放 / 1 审核 / 2 禁止) |
im_group_member |
群成员(role:0 成员 / 1 管理员 / 2 群主) |
im_group_request |
入群申请 |
im_group_message |
群消息 |
im_group_msg_deleted |
群消息自己侧隐藏 |
im_group_inbox |
群会话摘要与未读 |
uid=1 为文件传输助手(username=filehelper,不可登录)。
import socket, struct, json
MAGIC = 0x494D
TYPE_SYSTEM, TYPE_ACCOUNT = 1, 2
CMD_HEARTBEAT, CMD_LOGIN = 1, 2
def pack(typ: int, cmd: int, seq: int, body: bytes = b"") -> bytes:
return struct.pack(">HBBII", MAGIC, typ, cmd, seq, len(body)) + body
def recv_packet(sock: socket.socket):
header = sock.recv(12)
if len(header) < 12:
raise ConnectionError("closed")
magic, typ, cmd, seq, length = struct.unpack(">HBBII", header)
if magic != MAGIC:
raise ValueError("bad magic")
body = b""
while len(body) < length:
chunk = sock.recv(length - len(body))
if not chunk:
raise ConnectionError("closed")
body += chunk
return typ, cmd, seq, body
s = socket.create_connection(("127.0.0.1", 6000))
s.sendall(pack(TYPE_SYSTEM, CMD_HEARTBEAT, 1))
print(recv_packet(s)[3])
s.sendall(pack(TYPE_ACCOUNT, CMD_LOGIN, 2, json.dumps(
{"account": "alice", "password": "123456"}).encode()))
print(json.loads(recv_packet(s)[3]))
s.close()
空 body 心跳(System/Heartbeat,seq=1):
49 4D 01 01 00 00 00 01 00 00 00 00