编译前需要解压libs\cef_sandbox.rar(64位解压libs\x64\cef_sandbox.rar)。
这篇教程主要介绍NIM Demo的nim工程的内容,Demo的作用主要是展现sdk的功能,方便开发者快速熟悉使用sdk,但Demo的代码不一定是最佳的。Demo使用Visual Studio 2013开发。
Demo源码中目录结构中的C++封装层文件夹名字尾部携带_vs2010的为vs2010创建的工程项目,对应的不携带_vs2010的为vs2013创建的工程项目,即Demo目前加载的工程,该工程依赖Demo工程。如果开发者希望在VS2012或者VS2015下开发,需要加载CPP封装层,开发者要做的工作有:
- 加载VS2010创建的工程项目,并且自动升级到开发者目前的VS版本。
- CPP封装层工程依赖第三方的Jsoncpp,开发者需要自己重新编译jsoncpp源码替换原来链接的lib文件,源码在Demo源码third_party目录下;或者开发者可以替换自己的json库,并替换工程代码中json相关的接口。
Demo的主要功能由Demo工程本身以及UI组件
工程共同完成,Demo工程和UI组件
工程具有相同的目录结构。UI组件
位于tool_kits\ui_component\ui_kit
目录,UI组件的
相关文档详见:云信UI组件
-
callback:注册到SDK的一些回调的处理
-
gui:所有功能的界面相关实现
-
module:所有功能的逻辑相关实现
-
util:一些公用的工具类
开发者在打包自己的应用时,应确保将以下云信SDK相关文件打包进去。
-
nim.dll:云信SDK主要功能库文件。
-
nim_audio.dll:语音消息功能库文件。
-
nrtc.dll:音视频通话功能库文件。
-
nim_tools_http.dll:http功能库文件。
-
msvcr100.dll:SDK依赖的VS2010动态链接库。
-
msvcp100.dll:SDK依赖的VS2010动态链接库。
-
nim_conf:云信SDK配置文件目录,包含SDK版本控制等。
其他的文件及目录是应用程序相关的,开发者根据自己程序的使用情况选择是否打包。
-
image_ole.dll:图像显示库文件,支持在RichEdit组件中插入和显示图片。
-
translation.bin:中文翻译成拼音依赖的文件。
-
app_ver.dll:云信Demo应用程序版本控制,开发者请勿打包到自己的应用。
-
lang:Demo界面文案对照表,可支持多国语言。
-
res:Demo资源文件目录。
-
themes:Demo皮肤目录,包含XML配置文件和图片文件。
因为SDK所有接口都是C接口,为了方便使用C++的同学使用,我们提供了nim_cpp_sdk
静态库。静态库位于libs\nim_sdk_desktop\nim_cpp_sdk
目录,它将C接口SDK封装为C++代码,demo和UI组件
都直接使用nim_cpp_sdk
静态库的C++封装层代码。开发者可以直接在解决方案中导入nim_cpp_sdk
工程。
封装层提供的包装文件如下:
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_client.h: 全局管理功能;主要包括SDK初始化/清理、客户端登录/退出等功能
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_data_sync.h: 数据同步相关接口
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_friend.h: 好友功能,包含添加好友、删除好友、监听好友变化
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_global.h: NIM SDK提供的一些全局接口;释放从SDK申请的内存
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_msglog.h: 消息历史接口
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_nos.h: NOS云存储服务接口;上传或下载文件资源
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_rts.h: 白板功能
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_session.h: 会话列表管理功能;主要包括查询会话列表、删除会话列表等功能
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_sysmsg.h: 系统消息接口;主要包括查询系统消息、删除系统消息等功能
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_talk.h: 聊天功能;主要包括发送消息、接收消息等功能
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_team.h: 群组功能;主要包括查询群信息、查询群成员信息、加人、踢人等功能
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_tool.h: 提供的一些工具接口,主要包括获取SDK里app account对应的app data目录,计算md5、语音转文字等
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_vchat.h: 音视频(包括设备)相关接口
-
nim_sdk_cpp\nim_sdk_cpp_lib\api\nim_cpp_user.h: 用户功能,包含黑名单设置、个人消息提醒设置、状态设置
除了nim_cpp_sdk
静态库,另外还提供了负责语音和http传输的dll的C++封装类:
- libs\nim_sdk_desktop\nim_cpp_sdk\nim_audio_cpp\nim_tools_audio_cpp_wrapper.h:提供的语音录制和播放接口
- libs\nim_sdk_desktop\nim_cpp_sdk\nim_http_cpp\nim_tools_http_cpp_wrapper.h:提供的http传输相关接口
SDK的初始化在main.cpp中InitNim方法进行。
示例:
//初始化nim sdk
nim::SDKConfig config;
//sdk能力参数(必填)
config.database_encrypt_key_ = "Netease"; //string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
bool ret = nim::Client::Init("Netease", "", config); // 载入云信sdk,初始化安装目录和用户目录
assert(ret);
//初始化语音和http
nim_http::Init();
std::string res_audio_path = nbase::UTF16ToUTF8(app_data_audio_path); // app_data_audio_path为语音资源所在用户目录
ret = nim_audio::Audio::Init(res_audio_path);
assert(ret);
云信PC demo以及UI组件的界面开发都依赖云信DuiLib库
,关于云信DuiLib库
的使用方法和注意事项,请参考:云信Duilib
登录相关界面代码在gui/login
目录下,登录相关的控制逻辑已经封装到了UI组件的
,逻辑代码在callback\login\login_callback.cpp
文件中。登录界面可以直接调用UI组件
的登录函数,示例如下:
std::string username = "123456789";
std::string password = "123456789";
nim_ui::LoginManager::GetInstance()->DoLogin(username, password);
登录窗体的开发注意事项请看:云信UI组件。如果不使用UI组件
提供的登录逻辑,可以直接调用C++封装层的登录函数,示例如下。请注意demo的登录密码是做了md5加密的,如果客户把app key替换成自己应用的app key,请记得去掉密码的md5加密。
std::string app_key = "45c6af3c98409b18a84451215d0bdd6e";//app key 申请获得
std::string pass_md5 = QString::GetMd5(password); //MD5加密(用户自己的app请不要加密)
nim::Client::Login(app_key, user, pass_md5, OnLoginCallback, nullptr);
Demo主窗体界面直接使用UI组件
中开发好的最近会话列表组件、好友列表组件和群组列表组件。UI组件的
相关文档详见:云信UI组件。在demo的主窗体类MainForm
的初始化函数InitWindow
中集成了最近会话列表组件、好友列表组件和群组列表组件。示例如下:
((OptionBox*) FindControl(L"btn_session_list"))->Selected(true, true);
ui::ListBox* session_list = (ListBox*)FindControl(L"session_list");
nim_ui::SessionListManager::GetInstance()->AttachListBox(session_list);
ui::TreeView* friend_list = (TreeView*) FindControl(L"friend_list");
nim_ui::ContactsListManager::GetInstance()->AttachFriendListBox(friend_list);
ui::TreeView* group_list = (TreeView*) FindControl(L"group_list");
nim_ui::ContactsListManager::GetInstance()->AttachGroupListBox(group_list);
nim_ui::ContactsListManager::GetInstance()->InvokeGetAllUserInfo();
nim_ui::SessionListManager::GetInstance()->InvokeLoadSessionList();
nim_ui::SessionListManager::GetInstance()->QueryUnreadCount();
会话相关的界面代码在UI组件的
的gui/session
目录下,逻辑代码在module/session
目录下。Demo直接使用UI组件
中开发好的会话窗体组件,也可以调用C++封装层的会话相关函数直接收发消息。
发送消息
SDK 支持文本、图片、音频、视频、地理位置、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息,Demo目前展示了其中三种消息的发送,包括文本、图片和文件;同时支持用户自定义消息类型,用户可根据自己需要使用。发送消息的需要把数据组装成json、调用SDK接口发送。
发送文本消息示例:
Json::Value json;
json[nim::kNIMMsgKeyToType] = nim::kNIMSessionTypeP2P; //会话类型,好友是"kNIMSessionTypeP2P",群组是"kNIMSessionTypeTeam"
json[nim::kNIMMsgKeyType] = nim::kNIMMessageTypeText; //消息类型
json[nim::kNIMMsgKeyBody] = text; //消息内容
json[nim::kNIMMsgKeyToAccount] = receiver; //消息接收者账号
json[nim::kNIMMsgKeyTime] = 1000 * nbase::Time::Now().ToTimeT();; // 消息发送时间(毫秒)
json[nim::kNIMMsgKeyClientMsgid] = QString::GetGUID(); //消息id,一般使用guid
json[nim::kNIMMsgKeyLocalLogStatus] = nim::kNIMMsgLogStatusSending; //消息状态
nim::Talk::SendMsg(json.toStyledString());
发送图片消息示例:
std::wstring utf16_image_local_path = nbase::UTF8ToUTF16(image_local_path);
Json::Value json;
json[nim::kNIMMsgKeyToType] = nim::kNIMSessionTypeP2P; //会话类型,好友是"kNIMSessionTypeP2P",群组是"kNIMSessionTypeTeam"
json[nim::kNIMMsgKeyType] = nim::kNIMMessageTypeImage; //消息类型
//图片本地路径
json[nim::kNIMMsgKeyLocalFilePath] = image_local_path; //文件本地路径
//图片详细信息
Json::Value image_key;
std::string md5 = GetFileMD5(image_local_path);
image_key[nim::kNIMImgMsgKeyMd5] = md5; //文件MD5
long sz = nbase::GetFileSize(utf16_image_local_path);
image_key[nim::kNIMImgMsgKeySize] = sz; //文件大小
Gdiplus::Image image(utf16_image_local_path.c_str());
if (image.GetLastStatus() == Gdiplus::Ok)
{
image_key[nim::kNIMImgMsgKeyWidth] = image.GetWidth(); //宽度
image_key[nim::kNIMImgMsgKeyHeight] = image.GetHeight(); //高度
}
json[nim::kNIMMsgKeyAttach] = image_key.toStyledString();
json[nim::kNIMMsgKeyToAccount] = receiver; //消息接收者账号
json[nim::kNIMMsgKeyTime] = 1000 * nbase::Time::Now().ToTimeT();; //消息发送时间(毫秒)
json[nim::kNIMMsgKeyClientMsgid] = QString::GetGUID(); //消息id,一般使用guid
json[nim::kNIMMsgKeyLocalLogStatus] = nim::kNIMMsgLogStatusSending; //消息状态
nim::Talk::SendMsg(json.toStyledString());
接收消息
接收消息的需要在程序刚启动时,提前注册好接收消息的回调函数。回调函数的参数是一个json字符串,首先我们要解析json,json字段跟发送消息相近,只需要根据这些字段解析就可以了。解析完之后,一般我们会需要把解析到的文本、图片等消息在会话窗口中进行展示。
示例:
void TalkCallback::OnReceiveMsgCallback( const std::string& json_str)
{
QLOG_PRO(L"OnReceiveMsgCallback: {0}") << json_str;
Json::Value value;
Json::Reader reader;
if (reader.parse(json_str, value))
{
int code = value[nim::kNIMMsgKeyLocalRescode].asInt();
int feature = value[nim::kNIMMsgKeyLocalMsgFeature].asInt();
Json::Value json = value[nim::kNIMMsgKeyLocalReceiveMsgContent];
json[nim::kNIMMsgKeyLocalRescode] = code;
json[nim::kNIMMsgKeyLocalMsgFeature] = feature;
MsgData msg;
JsonToMsg(json, msg);
std::string id = GetSessionId(msg);
//会话窗口
if (msg.feature == nim::kNIMMessageFeatureDefault)
{
if (msg.msg_type == nim::kNIMMessageTypeNotification)
{
SessionForm* session = SessionManager::GetInstance()->Find(id);
if (session)
{
session->AddNewMsg(msg, false);
}
}
else
{
SessionManager::GetInstance()->AddNewMsg(msg);
}
}
else if (msg.feature == nim::kNIMMessageFeatureSyncMsg || msg.feature == nim::kNIMMessageFeatureRoamMsg)
{
SessionForm* session = SessionManager::GetInstance()->Find(id);
if (session)
{
session->AddNewMsg(msg, false);
}
}
else if (msg.feature == nim::kNIMMessageFeatureCustomizedMsg)
{
SessionForm* session = SessionManager::GetInstance()->Find(id);
if (session)
{
session->AddNewMsg(msg, false);
}
}
}
else
{
QLOG_ERR(L"parse receive msg fail: {0}") << json_str;
}
}