Linux SDK 开发手册

网易云通信服务概要

网易云通信以提供客户端SDK和服务端OPEN API 的形式提供即时通讯云服务。开发者只需想好APP 的创意及UI 展现方案,根据APP 业务需求选择网易云通信的相应功能接入即可。

注:跨平台 SDK 支持x86/x86-x64/ARM/MIPS架构。

开发准备

SDK内容

目录结构:

nim
  |
  |-- include
    |
    |-- api
    |-- export_headers
    |-- util
  |-- libs
    |
    |-- x86
    |-- x86-x64

SDK不提供debug版本的动态链接库供开发者调试,如果遇到问题请联系技术支持或在线客服。

SDK数据目录

当收到多媒体消息后,SDK 会负责下载这些多媒体文件,同时SDK 还要记录一些log,因此SDK 需要一个数据缓存目录。该目录通过nim_client_init 初始化接口传入,如果只传入文件目录名,默认为存放到当前用户的home目录,数据缓存目录默认为"~/[传入的目录名]/NIM/[云信账号Md5]";如果传入绝对路径,则存放到指定的目录中(必须保证有读写权限)。具体某个用户对应的缓存目录下面包含如下子目录:

SDK 提供了接口nim_tool_get_user_specific_appdata_dir 获取某个用户对应的具体类型的App data 目录(如图片消息文件存放目录,语音消息文件存放目录等)(注意:需要调用nim_global_free_buf 接口释放其返回的内存)。

接口调用与约定

SDK 提供的所有API 都是 C接口 ,根据模块划分为不同的API 文件。开发者在链接网易云通信动态库时可以使用隐式链接和显式链接两种方式,显式链接时,开发者可以在程序进行到任何时间点按需加载动态库,功能完成后如果不需要继续使用NIM可以卸载,更加灵活和节省内存,该模式下开发者只需要将SDK包里 include 目录下的模块定义头文件(命名方式如nim_xxx_def.h)加入到工程项目中;隐式链接下,需要将SDK包里ninclude 目录里的API 头文件(命名方式如nim_xxx.h)以及相关常量定义的头文件等其他头文件一并加入到工程项目中,程序编译时链接云信动态库。一般来说,每个模块都有对应的API 头文件和相关常量的定义头文件,命名上一一对应。下面以nim_client_init接口说明两种调用方式(后面文档中接口示例假定采用隐式链接)。

显式链接

typedef bool(*nim_client_init)(const char *app_data_dir, const char *app_install_dir, const char *json_extension);

nim_client_init init_func_ptr;
void* handle = dlopen("libnim.so", RTLD_LAZY);
if(handle)
{
    init_func_ptr = (nim_client_init)dlsym(handle, "nim_client_init");
    init_func_ptr(app_data_dir, app_install_dir, config_json);
}

隐式链接

#include "nim_client.h"

nim_client_init(app_data_dir, app_install_dir, config_json);

SDK 提供了3种类型的接口:

  1. 全局广播类通知的注册接口

    常见的几类消息或通知是通过这些接口注册的回调函数中通知开发者的,比如收到的消息,会话数据更新,用户相关数据的变更等,开发者需要在登录前提前注册好这些接口,例如:

     // 数据同步结果通知(nim_data_sync)
     void nim_data_sync_reg_complete_cb(nim_data_sync_cb_func cb, const void *user_data);        
     // 接收会话消息通知(nim_talk)
     void nim_talk_reg_receive_cb(const char *json_extension, nim_talk_receive_cb_func cb, const void *user_data);        
     // 接收系统消息通知(nim_sysmsg)
     void nim_sysmsg_reg_sysmsg_cb(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void *user_data);    
     // 发送消息结果通知(nim_talk)
     void nim_talk_reg_arc_cb(const char *json_extension, nim_talk_arc_cb_func cb, const void *user_data);
     // 发送自定义系统通知的结果通知(nim_talk)
     void nim_talk_reg_custom_sysmsg_arc_cb(const char *json_extension, nim_custom_sysmsg_arc_cb_func cb, const void *user_data);
     // 群组事件通知(nim_team)
     void nim_team_reg_team_event_cb(const char *json_extension, nim_team_event_cb_func cb, const void *user_data);    
     // 帐号被踢通知(nim_client)
     void nim_client_reg_kickout_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);        
     // 网络连接断开通知(nim_client)
     void nim_client_reg_disconnect_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);    
     // 将本帐号的其他端踢下线的结果通知(nim_client)
     void nim_client_reg_kickout_other_client_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data); 
     // 好友通知 
     void nim_friend_reg_changed_cb(const char *json_extension, nim_friend_change_cb_func cb, const void *user_data);    
     // 用户特殊关系通知
     void nim_user_reg_special_relationship_changed_cb(const char *json_extension, nim_user_special_relationship_change_cb_func cb, const void *user_data);
  2. 异步接口。

    回调函数作为参数,传入执行接口,然后执行接口时,会触发传入的回调函数。注意,回调函数中如果涉及到跨线程资源的需要开发者切换线程操作,即使不涉及到资源问题,也要保证回调函数中不会处理耗时任务,以免堵塞SDK底层线程。

  3. 同步接口。

    为方便开发者使用,我们提供了一些同步接口,调用接口获取的内容同步返回,以block命名的都是同步接口。

服务器和客户端上线了频控策略,与服务器有交互的接口(接口命名结尾为_online的查询接口以及命令类接口,如同意群邀请等)增加频控控制,开发者关注错误码416。

如果开发者在调用C接口过程中或者解析接口返回结果过程中出现疑问,可以参考和借鉴C++封装层。

数据格式

SDK 使用JSON进行数据传输,接口的输入参数和返回结果都是JSON字符串,用户可以直接组装字符串或者通过序列化/反序列化在JSON和c 结构之间进行转换,推荐使用第二种方式,可阅读性和扩展性更好。接口数据的JSON schema 在头文件中都有说明。在SDK的结果回调中通常都包含错误码来指示操作是否成功,操作成功错误码被设置为 200 (kNIMResSuccess),其它值表明接口调用有错误发生,详细错误码定义请参考nim_res_code_def.h文件中NIMResCode的定义。

该文档中的代码片段使用 json_buildjsoncpp进行JSON字符串序列化/反序列化,演示了一些接口输入参数和返回结果的JSON格式和处理方法,用户可以使用其他第三方json库

示例

  1. 隐式链接

     #include <stdio.h>
     #include <stdlib.h>
     #include <string.h>
     #include "api/nim_client.h"
     #include "api/nim_tools.h"
    
     void nim_login_cb_func(const char *json_params, const void *user_data)
     {
         printf("login callback:%s \n",json_params);
     }
    
     void nim_logout_cb_func(const char *json_params, const void *user_data)
     {
         printf("logout completed,begin to run cleanup ......\n");
         nim_client_cleanup(NULL);
     }
    
     int main()
     {
         //初始化SDK
         if(nim_client_init("nim_test",NULL,NULL))
         {
             //登录
             nim_client_login("YOUR-APP-KEY","ACCOUNT","PASSWROD",NULL,nim_login_cb_func,NULL);
             getchar();
             //注销
             nim_client_logout(kNIMLogoutAppExit,NULL,nim_logout_cb_func,NULL);
             printf("wait for logout ...... \n");
             getchar();
         }
         else
         {
             printf("nim init failed \n");
         }
         return 0;
     }

    编译:

     隐式链接动态库时需要设置环境变量LD_LIBRARY_PATH,在运行时系统才能正确加载动态库。
    
     export LD_LIBRARY_PATH=./libs/x86-x64:$LD_LIBRARY_PATH
    
     gcc main.c -I./include -I./include/export_headers -L./libs -lnim
  2. 显式链接

     #include <stdio.h>
     #include <dlfcn.h>
     #include "nim_client_def.h"
    
     //初始化和卸载
     typedef bool (*nim_client_init)(const char *app_data_dir, const char *app_install_dir, const char *json_extension);
     typedef void (*nim_client_cleanup)(const char *json_extension);
    
     //登录和退出
     typedef void (*nim_client_login)(const char *app_key, const char *account, const char *password,
                                     const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);
     typedef void (*nim_client_logout)(enum NIMLogoutType logout_type, const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);
    
     //获取函数入口地址
     #define GET_FUNC(function_ptr) \
         (function_ptr)(dlsym(handle, #function_ptr) != NULL ? dlsym(handle, #function_ptr) : NULL)
    
     nim_client_init init_func_ptr;
     nim_client_cleanup cleanup_func_ptr;
     nim_client_login login_func_ptr;
     nim_client_logout logout_func_ptr;
    
     int load_nim_lib()
     {
         //从LD_LIBDARY_PATH路径中读取,或者使用完整路径
         void *handle = dlopen("libnim.so", RTLD_LAZY);
         if (!handle)
             return 0;
         init_func_ptr = GET_FUNC(nim_client_init);
         cleanup_func_ptr = GET_FUNC(nim_client_cleanup);
         login_func_ptr = GET_FUNC(nim_client_login);
         logout_func_ptr = GET_FUNC(nim_client_logout);
         return 1;
     }
    
     void nim_login_cb_func(const char *json_params, const void *user_data)
     {
         printf("login callback:%s \n", json_params);
     }
    
     void nim_logout_cb_func(const char *json_params, const void *user_data)
     {
         printf("logout completed,begin to run cleanup ......\n");
         cleanup_func_ptr(NULL);
     }
    
     int main()
     {
         if (load_nim_lib())
         {
             init_func_ptr("nim_test", NULL, NULL);
             login_func_ptr("YOUR-APP-KEY","ACCOUNT","PASSWROD",NULL,nim_login_cb_func,NULL);
             getchar();
             logout_func_ptr(kNIMLogoutAppExit, NULL, nim_logout_cb_func, NULL);
             printf("wait for logout ...... \n");
             getchar();
         }
         return 0;
     }

    编译:

     gcc main.c -I./include/export_headers -ldl

初始化SDK

在调用任何功能前 必须 先进行初始化,传入数据缓存目录和SDK的配置信息。

void foo()
{
    //SDK 配置,不需要自定义设置为NULL即可
    const char* config = "{\
              \"global_config\": {\
                \"db_encrypt_key\": \"\",\
                \"preload_attach\": true,\
                \"sdk_log_level\": 6,\
              }}";

    //初始化SDK
    nim_client_init("appdata path", "app installation path", config);
}

在程序退出前,调用接口nim_client_cleanup 进行NIM SDK 的清理工作,nim_client_cleanup需要与nim_client_init配对执行,通常在程序启动时调用nim_client_init进行初始化,在程序退出时调用nim_client_cleanup释放资源。对于清理工作的注意事项请查看后续的"登出/退出和清理SDK"章节。

登录与登出

登录集成必读

首次登录

SDK 登录后会同步群信息,离线消息,漫游消息,系统通知等数据,所以首次登录前需要 提前注册所需的全局广播类通知的回调 (具体说明请参阅API 文档),以下以注册最近会话变更通知回调为例:

//全局会话列表变更通知函数
void CallbackSessionChange(int rescode, const char *result, int total_unread_counts, const char *json_extension, const void *user_data)
{
    if (rescode == kNIMResSuccess)
    {

    }
    else
    {
        //错误处理
    }
}

void foo()
{
    //注册全局会话列表变更通知函数
    nim_session_reg_change_cb(NULL, &CallbackSessionChange, NULL);    // 会话列表变更通知(nim_session)
}

通过注册以下接口才能正确维护当前账号的状态:

// 帐号被踢通知(nim_client)
void nim_client_reg_kickout_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);        
// 网络连接断开通知(nim_client)
void nim_client_reg_disconnect_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);    
// 多端登录/登出通知
void nim_client_reg_multispot_login_notify_cb(const char *json_extension, nim_json_transport_cb_func cb, const void *user_data);

登录

void CallbackLogin(const char* res, const void *user_data)
{
    json_value *value = fcx_null;
    int length = fcx_strlen(g_login_cb_json_params);
    value = json_parse(g_login_cb_json_params, length);
    if (value)
    {
        int code = json_value_find_as_int(value, "err_code");
        int step = json_value_find_as_int(value, "login_step");

        switch (step)
        {
        case kNIMLoginStepLinking:
            break;
        case kNIMLoginStepLogining:
            break;
        case kNIMLoginStepLogin:
            //登录鉴权结果
            break;
        default:
            FCX_DEBUG_ERROR("Unknown login step!");
            break;
        }
    }
    json_value_free(value);
}

void foo()
{
    nim_client_login("app key", "app account", "token", NULL, &CallbackLogin, NULL);
    //app key: 应用标识,不同应用之间的数据(用户,消息,群组等)是完全隔离的。开发自己的应用时,需要替换成自己申请来的app key
    //注意:替换成客户自己的app key之后,请不要对登录密码进行md5加密。
}

自动重新登录

SDK 在网络连接断开后,会监听网络状况,如果网络状况好转,那么SDK 会进行重新登录,登录结果可以在注册的重新登录回调函数中处理。

注意: 自动重新登录的机制可能会失效:

如果重新登录回调函数返回的的错误号既不是kNIMResSuccess,也不是网络错误相关错误号(kNIMResTimeoutError或者kNIMResConnectionError),那么说明自动重新登录的机制已经失效,需要开发者退回到登录界面进行手动重新登录。(详见nim_client_reg_auto_relogin_cb接口说明)

手动重新登录

SDK 在重新登录失败后,可以由用户手动调用重新登录接口nim_client_relogin

登出/退出和清理SDK

在账号已经登录时需要调用接口nim_client_logout进行登出或退出,需要使用相应的Logout类型才能保证SDK功能正常。 登出或退出的过程因为涉及到和服务器的交互以及需要将缓存中的数据持久化到本地,该接口可能需要一些时间才能执行完成。如果需要调用nim_client_cleanup,必须在完全退出(收到退出回调)后执行。

void CallbackLogout(const char* res, const void *user_data)
{
    //nim_client_logout执行完成,可以安全调用nim_client_cleanup
}

void foo()
{
    nim_client_logout(kNIMLogoutAppExit, NULL, &CallbackLogout, NULL);
}

断开连接通知

通过接口nim_client_reg_disconnect_cb来注册连接断开回调函数,断开连接后根据实际需要做后续处理(比如提示用户)。

被踢出通知

通过接口nim_client_reg_kickout_cb来注册被踢出回调函数,当收到此通知时,一般是退出程序然后回到登录窗口,回调函数参数中详细说明了被踢出的原因。

多端登录

通过调用nim_client_reg_multispot_login_notify_cb来注册多端登录通知的回调函数,当用户在某个客户端登录时,其他没有被踢掉的端会触发这个回调函数,并携带当前时间登录的设备列表的数据。登录时如果有其他端也处于登录状态,在登录回调函数中也包含这些设备的信息。

网易云通信内置踢人策略为:移动端(Android,iOS)互踢,桌面端(PC,Web)互踢,移动端和桌面端共存。

如果当前的互踢策略无法满足业务需求的话,可以联系我们取消内置互踢,根据多端登录的回调和当前的设备列表,判断本设备是否需要被踢出。如果需要踢出,直接调用登出接口并在界面上给出相关提示即可。

网易云通信 ID

网易云通信的每个用户由网易云通信 ID 唯一标识。网易云通信 ID是由用户帐号和token成功同步到网易云通信服务器后自动分配的,可以通过 NIMMessage 的 from 字段获取消息发送方的网易云通信 ID。

基础消息功能

消息功能概述

SDK支持文本、图片、音频、视频、地理位置、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息。消息定义在头文件nim_talk_def.h中。

发送消息

以发送图片消息的为例:

void foo()
{
    //组装消息
    Json::Value json;
    json[kNIMMsgKeyToType]            = ; //int 会话类型(NIMSessionType 个人0 群组1)
    json[kNIMMsgKeyToAccount]        = ; //string 消息接收者id
    json[kNIMMsgKeyTime]            = ; //int64  消息发送时间(毫秒),可选
    json[kNIMMsgKeyType]            = ; //NIMMessageType 消息类型
    json[kNIMMsgKeyBody]            = ; //string 消息内容
    json[kNIMMsgKeyAttach]            = ; //string 附加内容
    json[kNIMMsgKeyClientMsgid]        = ; //string 消息id,一般使用guid
    json[kNIMMsgKeyResendFlag]        = ; //int    重发标记(0不是重发, 1是重发)
    json[kNIMMsgKeyLocalLogStatus]    = ; //NIMMsgLogStatus 消息状态
    json[kNIMMsgKeyType]             = kNIMMessageTypeImage;

    //图片详细信息
    Json::Value attach;
    attach[kNIMImgMsgKeyMd5] = ;      //string 文件md5,选填
    attach[kNIMImgMsgKeySize] = ;     //long   文件大小,选填
    attach[kNIMImgMsgKeyWidth] = ;  //int     图片宽度,必填
    attach[kNIMImgMsgKeyHeight] = ; //int     图片高度,必填
    json[kNIMMsgKeyAttach] = attach.toStyledString();

    json[kNIMMsgKeyLocalFilePath]    = ; //string    图片本地路径
    json[kNIMMsgKeyLocalLogStatus] = kNIMMsgLogStatusSending;

    //发送消息
    nim_talk_send_msg(json.toStyledString().c_str(), "", NULL, "");
}

发送消息状态通过登录前注册的全局回调获取。

void CallbackSendMsgResult(const char* result, const void *user_data)
{
    //解析result {"msg_id" : "" , "talk_id" : "" , "rescode" : ""}
    //发送消息结果
}

void foo()
{
    nim_talk_reg_ack_cb(NULL, &CallbackSendMsgResult, NULL);
}

如果要停止正在发送中的消息(目前只支持发送文件消息时的终止),可以调用nim_talk_stop_send_msg接口实现。

消息属性设置

针对群组消息,提供强推属性设置(可以用于实现@功能等):

反垃圾字段:

消息多端推送开关

static void CallbackMultiportPushConfig(int rescode, const char *content, const char *json_extension, const void *user_data)
{
    //rescode 操作结果
    //content 通知内容
}

void foo()
{
    //登录前注册的全局回调获取当前多端推送开关状态
    nim_client_reg_sync_multiport_push_config_cb("", &CallbackMultiportPushConfig, NULL);

    //当PC/Web端在线时,可以配置消息是否推送给移动端
    nim_client_set_multiport_push_config(switch_on, "", &CallbackMultiportPushConfig, NULL);

    //主动获取当前多端推送开关状态
    nim_client_get_multiport_push_config("", &CallbackMultiportPushConfig, NULL);
}

接收消息

提前注册好接收消息的回调函数。在接收到消息时,如果是图片、语音消息,那么SDK 会自动下载,然后通过注册的NOS回调函数通知。如果下载失败,则调用接口nim_nos_download_media重新下载。此外,还可以调用nim_nos_stop_download_media停止下载(目前仅对文件消息类型有效)。

void CallbackReceiveMsg(const char* msg, const char *json_exten, const void *user_data)
{
    Json::Value value;
    Json::Reader reader;
    if (reader.parse(msg, value))
    {
        int rescode = value[nim::kNIMMsgKeyLocalRescode].asInt();
        int feature = value[nim::kNIMMsgKeyLocalMsgFeature].asInt();

        switch (feature)
        {
        case kNIMMessageFeatureDefault:
            ...
        case kNIMMessageFeatureSyncMsg:
            ...
        ...
        }
    }
}

void foo()
{
    nim_talk_reg_receive_cb(NULL, &CallbackReceiveMsg, NULL);
}

转发消息

用户通过转发消息接口获取消息结构Json字符串,然后需要调用发送消息接口。

void foo(const char *src_msg_json, const char *msg_id, NIMSessionType session_type, const char *session_id, const char* msg_sessiong_json, __int64 timetag)
{
    //创建转发消息
    char *msg = nim_talk_create_retweet_msg(src_msg_json, msg_id, session_type, session_id, msg_sessiong_json, timetag);

    //发送消息
    nim_talk_send_msg(msg, "", NULL, "");

    nim_global_free_buf((void *)msg);
}

撤回消息

用户通过nim_talk_send_msg发的消息可以通过nim_talk_recall_msg执行撤回操作,撤回操作一般有时限限制(该限制为全局的APP设置),超过限制返回508。 开发者通过注册撤回消息通知回调,接收其他端的撤回消息的通知,收到通知后SDK会标记主动在消息历史中标记该条消息为删除状态,同时开发者根据自身需求,删除界面上显示的消息,甚至插入一条提示。

最近会话

最近会话记录了与用户最近有过会话的联系人信息,包括联系人帐号、联系人类型、最近一条消息的时间、消息状态、消息缩略和未读条数等信息。nim_session_query_all_recent_session_async查询近期所有会话信息。

在收发消息的同时,SDK会更新对应聊天对象的最近联系人资料。当有消息收发时,SDK 会发出最近联系人更新通知,使用nim_session_reg_change_cb 注册通知回调函数。

SDK 也提供了删除单个nim_session_delete_recent_session_async和删除全部最近联系人nim_session_delete_all_recent_session_async的接口。需要注意:删除会话项时本地和服务器会同步删除!

未读数清零,同时需要将该会话的本地消息历史中的未读标记置为已读。

void CallbackNotifySession(int rescode, const char *result, int total_unread_counts, const char *json_extension, const void *user_data)
{
    // 解析result
}

void CallbackModifyMultipleMsglog(int res_code
    , const char *uid
    , nim::NIMSessionType to_type
    , const char *json_extension
    , const void *callback)
{
    ···
}


void foo(nim::NIMSessionType type, const char *id)
{
    nim_session_set_unread_count_zero_async(type, id, "", &CallbackNotifySession, NULL);
    nim_msglog_batch_status_read_async(id, type, "", &CallbackModifyMultipleMsglog, NULL);
}

自定义消息

除了内建消息类型外,SDK 也支持收发自定义消息类型。和透传消息一样,SDK 也不会解析自定义消息的具体内容,解释工作由开发者完成。

消息已读回执

    void CallbackMsgStatusChanged(int rescode, const char *result, const char *json_extent, const void *callback)
    {
        // 解析result
    }

    void foo(const char *json_msg)
    {
        nim_msglog_reg_status_changed_cb( "", &CallbackMsgStatusChanged, NULL);
    }

多媒体消息

语音消息录制和播放

接口示例

    void start_capture_callback(int rescode,void* user_data)
    {
        ···
    }

    void stop_capture_callback(int rescode, const char* file_path, const char* file_ext, long file_size, int audio_duration,void* userdata)
    {
        ···
    }

    void cancel_capture_callback(int rescode,void* user_data)
    {
        ···
    }

    typedef bool(*nim_audio_reg_start_capture_cb)(nim_rescode_cb cb, void* user_data);
    typedef bool(*nim_audio_reg_stop_capture_cb)(nim_stop_capture_cb cb,void* user_data);
    typedef bool(*nim_audio_reg_cancel_audio_cb)(nim_rescode_id_cb cb,void* user_data);

    typedef bool(*nim_audio_start_capture)(int audio_format, int volume, int loudness , const char* capture_device);
    typedef bool(*nim_audio_stop_capture)();
    typedef bool(*nim_audio_cancel_audio)();

    void OnInit()
    {
        nim_audio_reg_start_capture_cb start_func = (nim_audio_reg_start_capture_cb) dlsym(hInst, "nim_audio_reg_start_capture_cb");
        nim_audio_reg_stop_capture_cb stop_func = (nim_audio_reg_stop_capture_cb) dlsym(hInst, "nim_audio_reg_stop_capture_cb");
        nim_audio_reg_cancel_audio_cb cancel_func = (nim_audio_reg_cancel_audio_cb) dlsym(hInst, "nim_audio_reg_cancel_audio_cb");

        nim_audio_reg_stop_capture_cb stop_func = (nim_audio_reg_stop_capture_cb) dlsym(hInst, "nim_audio_reg_stop_capture_cb");
        nim_audio_reg_cancel_audio_cb cancel_func = (nim_audio_reg_cancel_audio_cb) dlsym(hInst, "nim_audio_reg_cancel_audio_cb");

        start_funcb(start_capture_callback,NULL);
        stop_func(stop_capture_callback,NULL);
        cancel_func(cancel_capture_callback,NULL);
    }

    bool StartCapture(int audio_format, int volume, int loudness , const char* capture_device)
    {
        nim_audio_start_capture func = (nim_audio_start_capture) dlsym(hInst, "nim_audio_start_capture");
        return func(audio_format,volume,loudness,capture_device);
    }

    bool StopCapture()
    {
        nim_audio_stop_capture func = (nim_audio_stop_capture) dlsym(hInst, "nim_audio_stop_capture");
        return func();
    }

    bool CancelCapture()
    {
        nim_audio_reg_cancel_audio_cb func = (nim_audio_reg_cancel_audio_cb) dlsym(hInst, "nim_audio_reg_cancel_audio_cb");
        return func();
    }

语音转文字

SDK 提供语音转文字接口。

void nim_tool_get_audio_text_async(const char *json_audio_info, const char *json_extension, nim_tool_get_audio_text_cb_func cb, const void *user_data);

void foo()
{
    Json::Value json_value;
    json_value[nim::kNIMTransAudioKeyMime] = ; //mime类型
    json_value[nim::kNIMTransAudioKeySample] = ; //采样率
    json_value[nim::kNIMTransAudioKeyAudioUrl] = ; //下载地址
    json_value[nim::kNIMTransAudioKeyDuration] = ; //时长(毫秒)

    nim_tool_get_audio_text_async(json_value.toStyledString().c_str(), NULL, &CallbackGetAudioText, NULL);
}

获取图片缩略图

SDK目前默认收到图片消息后会提前下载原图缓存到本地,如果开发者想控制下载图片的质量,可以通过初始化SDK时设置kNIMPreloadImageQuality来控制图片质量,设置kNIMPreloadImageResize来控制图片长宽。

目前SDK提供两种获取缩略图方案:

在生成新的图片url后通过nim_nos_download进行下载(头文件nim_nos.h)

群组功能

群组功能概述

网易云通信SDK提供了普通群(kNIMTeamTypeNormal),以及高级群(kNIMTeamTypeAdvanced)两种形式的群聊功能。高级群拥有更多的权限操作,两种群聊形式在共有操作上保持了接口一致。在群组中,当前会话的ID就是群组的ID。

普通群(讨论组)没有权限操作,适用于快速创建多人会话的场景。每个普通群只有一个管理员。管理员可以对群进行增减员操作,普通成员只能对群进行增员操作。在添加新成员的时候,并不需要经过对方同意。

高级群在权限上有更多的限制,权限分为群主、管理员、以及群成员。在添加成员的时候需要对方接受邀请。高级群的群成员资料提供了实时同步功能,并提供了群开关设置字段、第三方扩展字段(仅负责存储和透传)和第三方服务器扩展字段(该配置项只能通过服务器接口设置,对客户端只读)。

群操作 普通群 高级群
邀请成员 任何人 群主、管理员
踢出成员 群主 群主、管理员(管理员之间无法互相踢)
解散群 群主 群主
退群 任何人 管理员、普通成员
处理入群申请 / 群主、管理员
更改自己的群昵称 / 任何人
更改他人群昵称 / 群主、管理员
更改群名称 任何人 群主、管理员
更改群公告 / 群主、管理员
更改群介绍 / 群主、管理员
更新验证方式 / 群主、管理员
添加(删除)管理员 / 群主
移交群主 / 群主
成员禁言 / 群主、管理员
更新群头像 / 群主、管理员

群聊消息

群聊消息收发和管理与双人聊天完全相同,仅在消息类型(kNIMMsgKeyToType)上做了区分。

获取群组

SDK 在程序启动时会对本地群信息进行同步,所以只需要调用本地缓存接口获取群就可以了。SDK 提供了批量获取自己的群接口、以及根据单个群 ID 查询的接口。同样SDK 也提供了远程获取群信息的接口。

创建群组

网易云通信群组分为两类:普通群和高级群,两种群组的消息功能都是相同的,区别在于管理功能。

普通群所有人都可以拉人入群,除群主外,其他人都不能踢人。

高级群则拥有完善的成员权限体系及管理功能。创建群的接口相同,传入不同的类型参数即可。

void CallbackCreateTeam(int error, int team_event, const char *tid, const char* str, const char *json_exten, const void *user_data)
{

}    

void foo()
{
    //构建群信息json
    Json::Value team_info;
    team_info[kNIMTeamInfoKeyName] = ; //群名称
    team_info[kNIMTeamInfoKeyType] = ; //群类型
    team_info[kNIMTeamInfoKeyIntro] = ; //群介绍
    team_info[kNIMTeamInfoKeyJoinMode] = ; //群验证方式
    team_info[kNIMTeamInfoKeyAnnouncement] = ; //群公告

    //邀请的成员
    Json::Value team_member;
    team_member.append("id01");    
    team_member.append("id02");

    nim_team_create_team_async(team_info.toStyledString().c_str(), team_member.toStyledString().c_str(), "welcome to new team", NULL, &CallbackCreateTeam, NULL);
}

加入群组

用户可以通过被动接受邀请和主动加入两种方式进入群组。

    void CallbackTeamChange(int res_code, int notification_id, const char *tid, const char *result, const char *json_extension, const void *user_data)
    {
        //
    }

    void foo()
    {
        Json::Value friend_list;
        friend_list.append("litianyi01");    
        friend_list.append("litianyi02");

        nim_team_invite_async("TeamID", friend_list.toStyledString().c_str(), "welcome to new team", "", &CallbackCreateTeam, NULL);
    }    
- 通过申请(仅限高级群):

    `nim_team_pass_join_apply_async`


- 拒绝申请(仅限高级群):

    `nim_team_reject_join_apply_async`

踢人出群

调用接口nim_team_kick_async将群成员从群中移除。普通群仅拥有者可以踢人,高级群拥有者和管理员可以踢人,且管理员不能踢拥有者和其他管理员。

主动退群

用户需要主动退出群时调用nim_team_leave_async。除拥有者外,其他用户均可以主动退群。

编辑群组资料

普通群所有人均可以修改群名,高级群仅拥有者和管理员可修改群名及其他群资料。

void foo()
{
    Json::Value values;
    values[kNIMTeamInfoKeyID] = "tid";    
    values[kNIMTeamInfoKeyAnnouncement] = "123"; //修改群公告,同样的,目前可以修改群名称,群简介,具体参阅api文档

    nim_team_update_team_info_async("tid", values.toStyledString().c_str(), NULL, &CallbackTeamOperate, NULL);
}

管理群组权限

高级群群主可以对群进行权限管理,权限管理包括:

群组成员

解散群

群主可以调用接口解散所拥有的群:

void nim_team_dismiss_async(const char *tid, const char *json_extension, nim_team_event_cb_func cb, const void* user_data);

群组通知

用户在创建群或者进入群成功之后,任何关于群的变动(群组资料变动,群成员变动等),网易云通信服务器都会下发一条群通知消息。通过注册全局回调函数接受群组通知。

void nim_team_reg_team_event_cb(const char *json_extension, nim_team_event_cb_func cb, const void *user_data);

自定义拓展

SDK 提供了群信息的拓展接口,开发者可以通过维护群信息的两个属性来自行定义内容。

群成员禁言

历史记录

本地记录

    void CallbackModifyMsglog(int res_code, const char *msg_id, const char *json_extension, const void user_data)
    {
        if (user_data)
        {
            ···
        }
    }

    void foo()
    {
        //删除单条消息
        nim_msglog_delete_async("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, msg_id, NULL, &CallbackModifyMsglog, NULL);

        //删除与某个聊天对象的全部消息记录
        nim_msglog_batch_status_delete_async("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, NULL, &CallbackModifyMsglog, NULL);

        //删除指定会话类型的所有消息
        nim_msglog_delete_by_session_type_async(true, is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, NULL, &CallbackModifyMsglog, NULL);

        //删除所有消息记录
        nim_msglog_delete_all_async(delete_sessions, NULL, &CallbackModifyMsglog, NULL);
    }

*nim_msglog.h* 文件中的接口提供了比较完善的消息记录管理功能,开发者可本地查阅、在线查阅、删除和清空聊天记录,还可以调用`nim_msglog_write_db_only_async`接口只往本地消息历史数据库里写入一条消息(通常是APP 的本地自定义消息,并不会发给服务器)。详见API 文档。此外,还要注意:本地消息历史操作(如删除)及状态变化(如发送失败或成功)会与会话列表里的消息状态自动同步,App 上层需要根据`nim_session_reg_change_cb`注册好的会话列表通知回调进行相应通知事件的处理。

导入导出

    void nim_msglog_import_db_async(const char *src_path, const char *json_extension, nim_msglog_modify_res_cb_func res_cb, const void *res_user_data, nim_msglog_import_prg_cb_func prg_cb, const void *prg_user_data);


    void CallbackMsglogRes(int res, const char* msg_id, const char *json_exten, const void *user_data)
    {
        ...
    }

    void CallbackMsgImportPrg(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
    {
        ...
    }

    void foo()
    {
        nim_msglog_import_db_async("src_path", "", &CallbackMsglogRes, NULL, &CallbackMsgImportPrg, NULL);
    }
    void CallbackMsglogRes(int res, const char* msg_id, const char *json_exten, const void *user_data)
    {
        ...
    }

    void foo()
    {
        nim_msglog_export_db_async("src_path", "", &CallbackMsglogRes, NULL);
    }

云端记录

    void CallbackQueryMsgCb(int res_code, const char* id, const char* type, const char* str, const char *json_exten, const void *user_data)
    {
        ...
    }

    void foo()
    {
        nim_msglog_query_msg_online_async("acount_id", is_team ? kNIMSessionTypeTeam : kNIMSessionTypeP2P, 20, 0, msg_time, first_msg_server_msg_id, false, false, NULL, &CallbackQueryMsgCb, NULL);
    }

系统通知

除消息通道外,SDK 还提供系统通知这种通道用于消息之外的通知分发。目前有两种类型:内置系统通知自定义系统通知

所有的系统通知(包括内置系统通知和自定义系统通知)都是通过void nim_sysmsg_reg_sysmsg_cb(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void* user_data)注册后的回调通知给APP。为了保证整个程序逻辑的一致性,APP 需要针对不同类型的系统通知进行相应的操作。

内置系统通知

这是由SDK 预定义的通知类型,目前仅支持几种群操作的通知,如被邀请入群,SDK 负责这些通知的持久化。

此外,SDK 提供了以下接口来获取和维护内置系统通知记录:

自定义系统通知

除了内置系统通知外,SDK 也额外提供了自定义系统给开发者,方便开发者进行业务逻辑的通知。这个通知既可以由客户端发起也可以由开发者服务器发起。

客户端发起的自定义通知,该类型通知格式由开发者自定义(kNIMSysMsgKeyAttach 里),SDK 仅负责发送、接收,支持在线或离线发送,且支持点对点通知和群通知,不做任何解析,也不会存储,因此也不会在聊天记录中体现,可以使用的场景例如发送正在输入的状态等。代码示例如下:

void foo()
{
    //json_msg:key的定义详见系统消息字段nim_sysmsg_def.h
    Json::Value json_msg;
    json_msg[kNIMSysMsgKeyToAccount] = ; //接收者id
    json_msg[kNIMSysMsgKeyFromAccount] = ; //自己id
    json_msg[kNIMSysMsgKeyLocalClientMsgId] = 本地定义的消息id; 
    json_msg[kNIMSysMsgKeyTime] = ; //时间戳
    json_msg[kNIMSysMsgKeyAttach] = ; //通知附件内容
    json_msg[kNIMSysMsgKeyType] = ; //通知类型

    nim_sysmsg_send_custom_notification(json_msg.toStyledString().c_str(), NULL);
}

客户端发起的自定义通知的回执结果通过调用void nim_sysmsg_reg_sysmsg_cb(const char *json_extension, nim_sysmsg_receive_cb_func cb, const void *user_data)注册的回调告知开发者。

此外,自定义系统通知还提供了属性设置如下:

SDK 并不负责自定义通知的持久化,用户需要根据自己的业务逻辑按需进行解析和持久化的工作。

用户名片

概述

SDK 提供了用户帐号资料管理。以下几个接口仅当选择网易云通信托管用户资料时有效,如果开发者不希望网易云通信获取自己的用户数据,则需自行维护用户资料。

nim_user_def.h 里定义了用户信息的JSON Key。

用户信息变更会通过调用void nim_user_reg_user_name_card_changed_cb(const char *json_extension, nim_user_name_card_change_cb_func cb, const void *user_data)注册的回调告知程序:

获取本地用户信息

void nim_user_get_user_name_card(const char *accids, const char *json_extension, nim_user_get_user_name_card_cb_func cb, const void *user_data);

获取服务器用户信息

void (*nim_user_get_user_name_card_online)(const char *accids, const char *json_extension, nim_user_get_user_name_card_cb_func cb, const void *user_data);

单次查询限制150人。

编辑用户资料

void (*nim_user_update_my_user_name_card)(const char *info_json, const char *json_extension, nim_user_update_my_name_card_cb_func cb, const void *user_data);

该接口只能修改自己的信息

用户关系托管

SDK 提供了用户好友关系管理,以及对用户会话的消息设置。在网易云通信中,不是好友也允许聊天。好友关系如果不托管给网易云通信,开发者需要自己在应用服务器维护。

添加/被添加,请求/被请求,删除/被删除的通知以及多端同步等通知通过注册的好友数据变更通知回调告知应用程序:

void nim_friend_reg_changed_cb(const char *json_extension, nim_friend_change_cb_func cb, const void *user_data);

好友关系

黑名单

网易云通信中,黑名单和用户关系是互相独立的,即修改用户关系不会影响黑名单关系,同时,修改黑名单也不会对用户关系进行操作。

黑名单列表有本地缓存,缓存会在手动/自动登录后与服务器自动进行同步更新。通过注册用户关系变更通知回调nim_user_reg_special_relationship_changed_cb获取当前数据变化。

消息提醒

网易云通信中,可以单独设置是否开启某个用户的消息提醒,即对某个用户静音。静音关系和用户关系是互相独立的,修改用户关系不会影响静音关系,同时,修改静音关系也不会对用户关系进行操作。

静音名单列表有本地缓存,缓存会在手动/自动登录后与服务器自动进行同步更新。通过注册用户关系变更通知回调nim_user_special_relationship_change_cb_func获取当前数据变化。

NOS云存储服务

下载

下载资源,回调函数包括了下载结果以及下载进度

下载结果回调函数
@param rescode:200表示下载成功
@param file_path:下载文件的完整路径
@param call_id:对话id
@param res_id:消息id,与对话id一起可定位到具体的消息,然后根据rescode调整UI呈现
typedef void(*nim_nos_download_cb_func)(int rescode, const char *file_path, const char *call_id, const char *res_id, const char *json_extension, const void *user_data);

下载进度回调函数
@param downloaded_size:已下载大小
@param file_size:文件大小,单位都是字节
typedef void(*nim_nos_download_prg_cb_func)(__int64 downloaded_size, __int64 file_size, const char *json_extension, const void *user_data);

停止下载

SDK在收到包含附件的消息时会自动下载文件,如果在自动下载过程中出现异常可以调用void nim_nos_stop_download_media(const char *json_msg)主动结束下载,调用该接口需要传入正确的消息体参数。

上传

上传资源,回调函数包括了上传结果以及上传进度

上传结果回调函数
@param rescode:错误码,200表示成功
@param url:上传成功后返回的url
typedef void(*nim_nos_upload_cb_func)(int rescode, const char *url, const char *json_extension, const void *user_data);

上传进度回调函数
@uploaded_size:已上传大小
@file_size:文件大小
typedef void(*nim_nos_upload_prg_cb_func)(__int64 uploaded_size, __int64 file_size, const char *json_extension, const void *user_data);    

void nim_nos_upload(const char *local_file, nim_nos_upload_cb_func callback_result, const void *res_user_data, nim_nos_upload_prg_cb_func prg_cb, const void *prg_user_data);

默认NOS回调

在接收到文件类消息时SDK会自动下载,通过nim_nos_reg_download_cb注册默认的下载回调函数,SDK会在文件下载完成时通知应用程序,可以避免文件未下载完成就使用的情况。回调函数定义如下:

/** @typedef void (*nim_nos_download_cb_func)(int rescode, const char *file_path, const char *call_id, const char *res_id, const char *json_extension, const void *user_data)
* nim callback function for nos http download
* @param[out] rescode             下载结果,一切正常200
* @param[out] file_path            下载资源文件本地绝对路径
* @param[out] call_id                如果下载的是消息中的资源,则为消息所属的会话id,否则为空
* @param[out] res_id                如果下载的是消息中的资源,则为消息id,否则为空
* @param[out] json_extension        json扩展数据(备用)
* @param[out] user_data            APP的自定义用户数据,SDK只负责传回给回调函数,不做任何处理!
* @return void 无返回值
*/ 
typedef void (*nim_nos_download_cb_func)(int rescode, const char *file_path, const char *call_id, const char *res_id, const char *json_extension, const void *user_data);

在回调函数中通过call_idres_id可以定位到正在自动下载文件的消息。

在发送文件类消息时SDK会自动上传文件,应该程序可以通过nim_nos_reg_upload_cb注册回调函数获取文件上传结果,或者在发送消息的接口nim_talk_send_msg设置文件上传进度回调。