Windows(PC) SDK Getting Started

快速集成

以一个VS2015创建的MFC对话框为主的工程(以下统称Demo)为例,我们介绍下如何快速集成云信。

编译第三方库

云信提供的C++封装层工程文件是由VS2010平台创建,因此如果开发者当前开发环境为VS2010以上(比如GettingStarted开发环境为VS2015),则需要下载C++封装层依赖的第三方库(Jsoncpp)源码,编译一个开发环境一致的lib库,第三方库源码可以从这里下载,下载解压后将third_party整个目录拷贝到Demo工程目录中合适的位置,例如:

图1

VS2015打开third_party\vs2013.sln,将工程升级到2015,分别编译lib_json的debug版本和release版本。编译好的文件拷贝到上图中的libs目录下,如下:

图2

暂时我们只需要这个第三方库,如果需要其他第三方库,如法炮制。

下载并导入SDK

首先我们从官网下载页面下载Windows PC SDK(C/C++),下载解压后会得到如下目录:

图3

我们提供了纯C接口,同时提供了C++封装层,方便GUI开发,我们也推荐开发者直接导入C++封装层进行开发,下面我们就以此为例介绍如何导入SDK:

  1. 首先将SDK解压包中的目录全部拷贝到Demo工程目录中合适的位置,例如统一放到图一中的nim_sdk目录中。接下来将SDK解压包中的所有DLL文件拷贝到Demo工程目录中的bin目录下。
  2. 用VS2015打开NIMGetStarted.sln,如下图将IM C++封装层工程文件(nim_sdk\nim_cpp_sdk_vs2010\nim_sdk_cpp_lib.vcxproj)添加进Demo工程,并升级VC++编译器和库:

    图4 (添加现有项目)

    图5 (升级编译器)

  3. 修改C++封装层工程属性,包括输出目录、附加包含目录、预处理器定义、附加依赖项和附加库目录:

    图6 (输出目录,修改的意义在于目录结构更加美观合理)

    图7 (附加包含目录,修改后方便包含接口相关的定义头文件、辅助方法定义的头文件以及第三方库的接口头文件,例如根据当前工程目录结构,这里设置为:$(ProjectDir);$(ProjectDir)api\;$(ProjectDir)helper\;$(ProjectDir)util\; $(SolutionDir)third_party\jsoncpp\include\json\; $(SolutionDir)nim_sdk\nim_c_sdk\include\;$(SolutionDir)nim_sdk\nim_c_sdk\util)

    图8 (预处理器定义,云信SDK需要在这里设置NIM_SDK;NIM_SDK_DLL_IMPORT)

    图9 (附加依赖项和附加库目录,这里设置之前编译的第三方Json库)

  4. 完成后C++封装层就能够编译成功了,接下来我们要设置Demo工程属性(NIMGetStarted),让Demo工程能够正常调用SDK C++接口。

    1. 修改属性 常规-输出目录 : $(SolutionDir)bin\
    2. 修改C/C++ - 附加包含目录, 添加包含含有C++ API定义头文件的目录和相关的常量定义的头文件的目录: $(SolutionDir)nim_sdk\nim_cpp_sdk_vs2010\;$(SolutionDir)nim_sdk\nim_cpp_sdk_vs2010\api\;$(SolutionDir)nim_sdk\nim_cpp_sdk_vs2010\helper\;$(SolutionDir)nim_sdk\nim_cpp_sdk_vs2010\util\; $(SolutionDir)nim_sdk\nim_c_sdk\include\;$(SolutionDir)nim_sdk\nim_c_sdk\util\; $(SolutionDir)third_party\jsoncpp\include\json\; $(SolutionDir)nim_sdk\nim_chatroom_c_sdk\include\;$(SolutionDir)nim_sdk\nim_chatroom_c_sdk\util\; $(SolutionDir)nim_sdk\nim_chatroom_cpp_vs2010\helper\;$(SolutionDir)nim_sdk\nim_chatroom_cpp_vs2010\util\;$(SolutionDir)nim_sdk\nim_chatroom_cpp_vs2010\api\;$(SolutionDir)nim_sdk\nim_chatroom_cpp_vs2010\
    3. 修改链接器 - 附加依赖项, 设置 C++编译好的lib文件 : nim_cpp_sdk_d.lib, 同时修改链接器 - 常规 - 附加库目录 : $(SolutionDir)libs\

以上步骤完成后,Demo工程就成功集成了IM C++ SDK了,对于聊天室 C++ SDK的集成如法炮制,最终Demo工程结构如下图

图10

导入工具类SDK

云信PC SDK除了提供通讯云能力以外,也提供了封装好的语音采集播放和Http上传下载的能力,下面我们以导入Http工具为例介绍下:

  1. Demo工程下新建一个过滤器nim_http_cpp,添加nim_sdk\nim_tools_cpp_sdk\nim_http_cpp\nim_tools_http_cpp.h以及cpp文件,这两个文件是Http工具C++封装层的头文件和实现文件。
  2. 修改C/C++ - 附加包含目录, 添加包含含有C++ API定义头文件的目录和相关的常量定义的头文件的目录: $(SolutionDir)nim_sdk\nim_tools_cpp_sdk\nim_http_cpp\;

以上步骤完成后,Demo工程就成功集成了Http工具了,对于语音采集播放工具的集成如法炮制。

快速上手IM SDK

初始化SDK

在应用初始化实例时初始化SDK

...
//IM SDK接口定义头文件
#include "nim_cpp_api.h"
...

...

// CNIMGetStartedApp 初始化
BOOL CNIMGetStartedApp::InitInstance()
{
    ...

    //初始化云信SDK
    nim::SDKConfig sdk_config;
    //设置数据库密钥前缀
    sdk_config.database_encrypt_key_ = "NIMGetStarted";
    //初始化IM SDK
    nim::Client::Init("NIMGetStarted", "", sdk_config);

    ...
    CNIMGetStartedDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    ...

    return FALSE;
}

清理SDK

在退出实例前需要调用接口清理SDK

//清理云信SDK
//清理IM SDK
nim::Client::Cleanup();

登录和注销

  1. 登录前的准备。

    登录前需要按需求提前注册全局的广播通知,例如接收消息、发送消息的回调、多端登陆通知、被踢通知、断线和重连通知等:

     ...    
     //IM SDK接口定义头文件
     #include "nim_cpp_api.h"
     ...
    
     //前置声明,依次为收到批量离线或同步消息通知函数、收到在线消息通知函数、发送消息结果通知函数、踢他端通知函数、多端登录通知函数、断线通知函数、被踢通知函数、登录重连通知函数。
     void CallbackReceiveMsgs(const std::list<nim::IMMessage>& msgs);
     void CallbackReceiveMsg(const nim::IMMessage& msg);
     void CallbackSendMsgArc(const nim::SendMessageArc& arc);
     void CallbackKickOther(const nim::KickOtherRes& res);
     void CallbackMultiSpotLogin(const nim::MultiSpotLoginRes& res);
     void CallbackDisconnect();
     void CallbackKickout(const nim::KickoutRes& res);
     void CallbackLogin(const nim::LoginRes& res);
    
     BOOL CNIMGetStartedDlg::OnInitDialog()
     {
         CDialogEx::OnInitDialog();
         ...
    
         // 注册云信全局广播通知的监听
         nim::Client::RegKickoutCb(&CallbackKickout);
         nim::Client::RegDisconnectCb(&CallbackDisconnect);
         nim::Client::RegKickOtherClientCb(&CallbackKickOther);
         nim::Client::RegMultispotLoginCb(&CallbackMultiSpotLogin);
         nim::Client::RegReloginCb(&CallbackLogin);
    
         nim::Talk::RegReceiveCb(&CallbackReceiveMsg);
         nim::Talk::RegReceiveMessagesCb(&CallbackReceiveMsgs);
         nim::Talk::RegSendMsgCb(&CallbackSendMsgArc);
    
         ...
         return TRUE;
     }
  2. 登录。

    我们在界面上随意添加两个Edit作为用户名和密码的输入框,添加一个Button作为登录按钮,在登录按钮的事件处理函数里调用登录接口:

     void CNIMGetStartedDlg::OnBnClickedButtonLogin()
     {
         // TODO: 在此添加控件通知处理程序代码
    
         char username[64], password[64];
         GetDlgItemTextA(this->GetSafeHwnd(), IDC_EDIT_USERNAME, username, 64);
         GetDlgItemTextA(this->GetSafeHwnd(), IDC_EDIT_PASSWORD, password, 64);
    
         nim::Client::Login(app_key_, username, nim::Tool::GetMd5(password), &CallbackLogin);
     }

    登录过程经历3个过程,对于开发者来说,需要关心登录结果以及nim::kNIMLoginStepLogin这个步骤。

     void CallbackLogin(const nim::LoginRes &res)
     {
         if (res.login_step_ == nim::kNIMLoginStepLogin)
         {
             printf("Login result : %d, %s.\r\n", res.res_code_, res.res_code_ == nim::kNIMResSuccess ? "succeed" : "failed");
         }
         else
         {
             printf("Logining %d step...\r\n", res.login_step_);
         }
     }
  3. 注销和退出

    注销和退出通过传入不同的参数区分,注销就是退到登录界面,用户可能是重新登录或者切换账号登录,退出则是完全退出应用,两者需要做区分。同样我们在界面上添加两个Button作为注销和退出的按钮,在时间处理函数里调用接口:

     //注销
     void CNIMGetStartedDlg::OnBnClickedButtonLogout()
     {
         // TODO: 在此添加控件通知处理程序代码
    
         nim::Client::Logout(nim::kNIMLogoutChangeAccout, [](nim::NIMResCode code) {
             printf("Logout, rescode : %d.\r\n", code);
         });
     }
    
     //退出
     void CNIMGetStartedDlg::OnBnClickedButtonExit()
     {
         // TODO: 在此添加控件通知处理程序代码
    
         nim::Client::Logout(nim::kNIMLogoutAppExit, [](nim::NIMResCode code) {
             printf("Exit, rescode : %d.\r\n", code);
         });
     }

发送和接收消息

  1. 发送消息

    目前云信SDK支持发送文字,图片,语音,视频,文件,位置,提醒以及自定义消息,下面通过文字消息来介绍如果发送云信的Hello World, 在界面上增加文字输入Edit,发送对象ID Edit, 是否为群组CheckBox,以及一个发送按钮,在发送按钮的事件处理函数中:

     void CNIMGetStartedDlg::OnBnClickedButtonSendmsg()
     {
         // TODO: 在此添加控件通知处理程序代码
    
         //发送对象ID
         char account_id[64];
         GetDlgItemTextA(this->GetSafeHwnd(), IDC_EDIT_TOACCOUNT, account_id, 64);
    
         //文字消息内容
         CString content;
         m_richedit.GetWindowTextW(content);
         std::string msg_body = (std::string)GetUTF8FromCString(content);
    
         //创建文字消息Json string
         std::string msg_json = nim::Talk::CreateTextMessage(account_id //发送对象ID
             , IsDlgButtonChecked(IDC_CHECK_TOTEAM) == 1 ? nim::kNIMSessionTypeTeam : nim::kNIMSessionTypeP2P //群组OrP2P消息
             , nim::Tool::GetUuid()    //客户端消息ID
             , msg_body                //文字消息内容
             , nim::MessageSetting());    //消息属性,默认
    
         //发送消息
         nim::Talk::SendMsg(msg_json);
     }

    同时,我们通过在登录前注册的发送消息结果的广播通知的函数来监听消息是否发送成功:

     void CallbackSendMsgArc(const nim::SendMessageArc& arc)
     {
         //arc.msg_id_就是发送消息时填写的客户端消息ID
         printf("The message (%s) sent %s.\r\n", arc.msg_id_.c_str(), arc.rescode_ == nim::kNIMResSuccess ? "succeed" : "failed");
     }
  2. 接收消息

    通过在登陆前注册的接收消息的广播通知的函数来监听是否有收到别人发来的消息:

     void CallbackReceiveMsg(const nim::IMMessage& msg)
     {
         printf("Receive message from %s, message type : %d.\r\n"
             , msg.session_type_ == nim::kNIMSessionTypeTeam ? msg.receiver_accid_.c_str() : msg.sender_accid_.c_str()
             , msg.type_);
     }

    为了提升效率,登录后服务器下发的同步消息和离线消息将以批量的方式通过登陆前注册的接收批量消息的广播通知的函数通知开发者:

     void CallbackReceiveMsgs(const std::list<nim::IMMessage>& msgs)
     {
         printf("Receive %d %s messages from %s.\r\n"
             , msgs.size()
             , msgs.begin()->session_type_ == nim::kNIMSessionTypeTeam ? "team" : "p2p"
             , msgs.begin()->session_type_ == nim::kNIMSessionTypeTeam ? msgs.begin()->receiver_accid_.c_str() : msgs.begin()->sender_accid_.c_str());
     }

快速上手聊天室 SDK

初始化SDK

在应用初始化实例时初始化SDK

//聊天室接口定义头文件
#include "nim_chatroom_cpp_api.h"

...

// CNIMGetStartedApp 初始化
BOOL CNIMGetStartedApp::InitInstance()
{
    ...

    //初始化聊天室SDK
    nim_chatroom::ChatRoom::Init("");

    ...
    CNIMGetStartedDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    ...

    return FALSE;
}

清理SDK

在退出实例前需要调用接口清理SDK

//清理云信SDK
//清理聊天室SDK
nim_chatroom::ChatRoom::Cleanup();

使用HTTP工具获取聊天室列表

使用HTTP工具前需要初始化HTTP SDK:

//HTTP工具接口定义头文件
#include "nim_tools_http_cpp.h"

...

// CNIMGetStartedApp 初始化
BOOL CNIMGetStartedApp::InitInstance()
{
    ...

    //初始化HTTP工具
    nim_http::Init();

    ...
    CNIMGetStartedDlg dlg;
    m_pMainWnd = &dlg;
    INT_PTR nResponse = dlg.DoModal();
    ...

    return FALSE;
}

应用退出前需要清理HTTP工具:

//清理云信SDK
//清理HTTP工具
nim_http::Uninit();

在界面上增加获取列表Button,事件处理函数中:

...
//HTTP工具接口定义头文件
#include "nim_tools_http_cpp.h"
...

void CNIMGetStartedDlg::OnBnClickedButtonCrGetlist()
{
    // TODO: 在此添加控件通知处理程序代码

    //HTTP请求回调函数,lambda表达式
    auto http_cb = [](bool ret, int response_code, const std::string& reply) {
        Json::Value json_reply;
        Json::Reader reader;
        if (reader.parse(reply, json_reply) && json_reply.isObject())
        {
            int res = json_reply["res"].asInt();
            if (res != 200)
            {
                printf("Get chatroom list error, error_code:%d", res);
                return;
            }

            int count = json_reply["msg"]["total"].asInt();
            Json::Value json_list = json_reply["msg"]["list"];
            if (!json_list.isArray())
            {
                printf("Get chatroom list error, empty");
                return;
            }

            std::string ids;
            for (int i = 0; i < count; i++)
            {
                char id[32];
                sprintf_s(id, 32, "%lld\r\n", json_list[i]["roomid"].asInt64());
                ids += id;
            }

            printf("Get chatroom list:\r\n%s", ids.c_str());
        }
        else
        {
            printf("Get chatroom list error, reply:%s", reply.c_str());
        }
    };

    //http api地址
    std::string api_addr = chatroom_list_api_;
    //SDK app key
    std::string app_key = app_key_;
    //构建一个http请求
    nim_http::HttpRequest request(api_addr, "", 0, http_cb);
    //按要求添加http头
    request.AddHeader("Content-Type", "application/json; charset=utf-8");
    request.AddHeader("appKey", app_key);
    //发起http请求
    nim_http::PostRequest(request);
}

进入和退出

  1. 进入前的准备

    和云信IM SDK一样,进入前需要提前注册全局广播通知,包括聊天室链接状态通知、聊天室进入结果通知、聊天室退出结果通知、发送消息结果通知、接收消息通知等。

         ...    
     //聊天室接口定义头文件
     #include "nim_chatroom_cpp_api.h"
     ...
    
     //前置声明,依次为聊天室链接状态通知函数、聊天室进入结果通知函数、聊天室退出结果通知函数、发送消息结果通知函数、接收消息通知函数。
     void CallbackChatroomLinkConditoon(__int64 room_id, const nim_chatroom::NIMChatRoomLinkCondition condition);
     void CallbackChatroomEnter(__int64 room_id, const nim_chatroom::NIMChatRoomEnterStep step, int error_code, const nim_chatroom::ChatRoomInfo& info, const nim_chatroom::ChatRoomMemberInfo& my_info);
     void CallbackChatroomExit(__int64 room_id, int error_code, nim_chatroom::NIMChatRoomExitReason exit_reason);
     void CallbackChatroomSendMsgAck(__int64 room_id, int error_code, const nim_chatroom::ChatRoomMessage& result);
     void CallbackChatroomReceiveMsg(__int64 room_id, const nim_chatroom::ChatRoomMessage& result);
    
     BOOL CNIMGetStartedDlg::OnInitDialog()
     {
         CDialogEx::OnInitDialog();
         ...
    
         // 注册云信聊天室全局广播通知的监听
         nim_chatroom::ChatRoom::RegEnterCb(&CallbackChatroomEnter);
         nim_chatroom::ChatRoom::RegExitCb(&CallbackChatroomExit);
         nim_chatroom::ChatRoom::RegReceiveMsgCb(&CallbackChatroomReceiveMsg);
         nim_chatroom::ChatRoom::RegSendMsgAckCb(&CallbackChatroomSendMsgAck);
         nim_chatroom::ChatRoom::RegLinkConditionCb(&CallbackChatroomLinkConditoon);
    
         ...
         return TRUE;
     }
  2. 进入聊天室

    在界面上添加聊天室ID Edit和进入Button,从前面获取聊天室列表中随意挑选一个聊天室ID填入Edit,在Button的事件处理函数中:

     void CNIMGetStartedDlg::OnBnClickedButtonCrEnter()
     {
         // TODO: 在此添加控件通知处理程序代码
    
         char id[32];
         GetDlgItemTextA(this->GetSafeHwnd(), IDC_EDIT_CR_ID, id, 32);
         long long chatroom_id = atoll(id);
         nim::PluginIn::ChatRoomRequestEnterAsync(chatroom_id, [chatroom_id](int res, const std::string &token) {
             nim_chatroom::ChatRoom::Enter(chatroom_id, token);
         });
     }

    登录聊天室分两步,第一步是通过IM SDK获取聊天室令牌(这个时候IM SDK必须是登录状态),第二步调用聊天室进入接口登录。对于进入聊天室的结果,开发者通过提前注册的广播通知能知道结果,关注第nim_chatroom::kNIMChatRoomEnterStepServerConnectOver步和错误码。

     void CallbackChatroomEnter(__int64 room_id, const nim_chatroom::NIMChatRoomEnterStep step, int error_code, const nim_chatroom::ChatRoomInfo& info, const nim_chatroom::ChatRoomMemberInfo& my_info)
     {
         if (step == nim_chatroom::kNIMChatRoomEnterStepServerConnectOver)
         {
             printf("Enter chatroom %lld result : %d, %s.\r\n", room_id, error_code, error_code == 200 ? "succeed" : "failed");
         }
         else
         {
             printf("Entering chatroom %lld step %d...\r\n", room_id, step);
         }
     }
  3. 退出聊天室

    void CallbackChatroomExit(__int64 room_id, int error_code, nim_chatroom::NIMChatRoomExitReason exit_reason)
    {
        printf("Exit chatroom %lld, reason:%d, rescode:%d.\r\n", room_id, exit_reason, error_code);
    }

    void CNIMGetStartedDlg::OnBnClickedButtonCrExit()
    {
        // TODO: 在此添加控件通知处理程序代码

        char id[32];
        GetDlgItemTextA(this->GetSafeHwnd(), IDC_EDIT_CR_ID, id, 32);
        long long chatroom_id = atoll(id);
        nim_chatroom::ChatRoom::Exit(chatroom_id);
    }

发送和接收消息

  1. 发送消息

    目前云信聊天室SDK支持发送文字,图片,语音,视频,文件,位置,提醒以及自定义消息,下面通过文字消息来介绍如果发送云信聊天室的Hello World, 在界面上增加文字输入Edit,聊天室ID Edit, 以及一个发送按钮,在发送按钮的事件处理函数中:

     void CNIMGetStartedDlg::OnBnClickedButtonCrSendmsg()
     {
         // TODO: 在此添加控件通知处理程序代码
    
         //聊天室ID
         char id[32];
         GetDlgItemTextA(this->GetSafeHwnd(), IDC_EDIT_CR_ID, id, 32);
         long long chatroom_id = atoll(id);
    
         //文字消息内容
         CString content;
         m_richedit2.GetWindowTextW(content);
         std::string msg_body = (std::string)GetUTF8FromCString(content);
    
         //创建消息Json string
         std::string msg_json = nim_chatroom::ChatRoom::CreateRoomMessage(nim_chatroom::kNIMChatRoomMsgTypeText //消息类型
             , nim::Tool::GetUuid()    //客户端消息ID
             , msg_body                //消息内容
             , nim_chatroom::ChatRoomMessageSetting());    //消息属性,默认
    
         //发送消息
         nim_chatroom::ChatRoom::SendMsg(chatroom_id, msg_json);
     }

    发送消息结果通过提前注册的广播通知获取:

     void CallbackChatroomSendMsgAck(__int64 room_id, int error_code, const nim_chatroom::ChatRoomMessage& result)
     {
         //result.client_msg_id_是发送消息是填入的客户端消息ID
         printf("The message %s sent to chatroom %lld %s.\r\n", result.client_msg_id_.c_str(), room_id, error_code == 200 ? "succeed" : "falied");
     }
  2. 接收消息

    通过在进入聊天室前注册的接收消息的广播通知的函数来监听是否有收到消息:

     void CallbackChatroomReceiveMsg(__int64 room_id, const nim_chatroom::ChatRoomMessage& result)
     {
         printf("Receive message %s from chatroom %lld, message type:%d\r\n", result.client_msg_id_.c_str(), room_id, result.msg_type_);
     }

下载快速上手Demo源码

点击下载

基于IM Demo开发

C++ Demo及SDK、UIKIT下载

Demo和SDK下载地址

Windows(PC)开发手册

Windows(PC) C API

Windows(PC) C++ API

Windows(PC)的UI开源库