即时通讯之环信视频语⾳实时通话与单聊和聊实现
即时通讯
1. 即时通讯简介
即时通讯英⽂名为:Instant Messaging,简称IM。
即时通讯(Instant messaging,简称IM)是⼀个终端服务,允许两⼈或多⼈使⽤⽹路即时的传递⽂字讯息、档案、语⾳与视频交流。即时通讯按使⽤⽤途分为企业即时通讯和⽹站即时通讯,根据装载的对象⼜可分为⼿机即时通讯和PC即时通讯,⼿机即时通讯代表是QQ,。
2. 即时通讯的代表作
主流的代表:Skype/QQ/Google Talk/WhatsApp/Instagram/LINE/Kik/Wechat/Facebook Messenger/Yahoo! Messenger/MSN Messenger/ICQ/IChat
3. 如何实现即时通讯
即时通讯实现需要开发者写⼀个通讯协议,⽐如服务器的通讯协议是⼀致的,服务器跟服务器之间进⾏数据的传输,A客户端和B客户端就能进⾏数据的传输。
协议:定义⼀个标准,如何传输数据和客户端如何通讯。
4. iOS中如何实现即时通讯
1. 使⽤Socket写⼀个通讯协议(⾃⼰写⼀个协议)
2. 使⽤XMPPframework第三⽅框架
3. 使⽤国内第三⽅框架融云
4. 使⽤国内第三框架环信
5. 使⽤国内第三⽅框架LeanCloud
6. 使⽤国内第三⽅框架阿⾥悟空
7. ...
5. 以上⼏种⽅式简单分析
各⾏各业的App使⽤的通讯框架各有差异,但是实现的功能都是相似的,⽬前站在程序员的⾓度来观
看,环信提供的接⼝和服务器都是相对要稳定很多,最重要的是他们的客服有⼏次凌晨来咨询我环信使⽤得怎么样。都快感动爬了。
简单介绍下两款⽐较新的框架
LeanCloud:是⽹易推出的即时通讯云服务器,使⽤这个框架的公司⽬前主要是⽹易新闻、⽹易云⾳乐和⽹易花⽥等其他的App。
阿⾥悟空:阿⾥抱着对社交⼀直不死⼼的⼼态下推出的阿⾥悟空即时通讯云,主要App案例是⼤姨吗、钉钉等
6. 先研究环信的使⽤
EaseMob简介
环信是北京易掌云峰科技有限公司推出的即时通讯云平台,环信将基于移动互联⽹的即时通讯能⼒通过云端开放的 Rest API 和客户端 SDK 包的⽅式提供给开发者和企业。
环信全⾯⽀持iOS、Android、Web等多种平台,在流量、电量、长连接、语⾳、位置、安全等能⼒做了极致的优化,让移动开发者摆脱繁重的移动IM通讯底层开发,最⼤限度地缩短产品开发周期,最短的时间内让App拥有移动IM能⼒。
简单的说:只要集成了EaseMobSDK,然后做简单的配置,实现简单的代码便能让你的App实现聊天的功能
环信是基于Jabber/XMPP协议的即时通讯服务器
接下⾥实现的效果
EaseMobSDK的导⼊
1. 提前准备
下载iOS的环信SDK
注册环信即时通讯云账号
登陆到管理后台
在我的应⽤中创建⼀个应⽤
在苹果的个⼈开发中⼼创建⼀个推送证书(当然不创建也没⽤关系,只是不能推送消息⽽已)
创建完证书导出p12⽂件
在我的应⽤中点击你的应⽤选择推送证书
新增证书选择p12⽂件上传
2. SDK导⼊
将下载完的环信SDK中的EaseMobSDK拖⼊到项⽬中
EaseMobSDK中的lib⽂件夹中包含以下两个.a⽂件
libEaseMobClientSDK:包含所有功能
libEaseMobClientSDKLite:不包含实时语⾳
所以只需要保留⼀个
同时需要在include⽂件夹中也需要删除⼀个⽂件夹
EaseMobSDK⽬录结构
EaseMobSDK
include(包含对应功能服务的头⽂件)
CallService(语⾳服务)
ChatService(聊天服务)
EaseMobClientSDK(客户端主要使⽤的SDK头⽂件)
Utility(硬件相关接⼝和错误码定义)
lib(静态库)
resources(资源⽂件)
在AppDelegate中的didFinishLaunchingWithOptions注册EaseMobSDK
// 注册SDK
// kEaseMobAppKey:环信后台管理->我的应⽤->对应的应⽤->应⽤概述->应⽤标识
// kEaseMobPushName:环信后台管理->我的应⽤->对应的应⽤->应⽤概述->推送证书->iOS->证书名称
[[EaseMob sharedInstance] registerSDKWithAppKey:kEaseMobAppKey apnsCertName:kEaseMobPushName];
此时会报很多错误
需要导⼊框架
MobileCoreServices.framework
CFNetwork.framework
libEaseMobClientSDKLite.a
libsqlite3.dylib
libstdc++.6.0.9.dylib
libz.dylib
libiconv.dylib
libresolv.dylib
libxml2.dylib
需要对象做配置
Build Settings->Linking->Other Linker Flags 中添加-ObjC 或者 force_load 静态库路径SDK集成完毕
应⽤程序⽣命周期⽅法中实现环信中对应的⽅法
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[EaseMob sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
return YES;
}
// App进⼊后台
- (void)applicationDidEnterBackground:(UIApplication *)application
{
[[EaseMob sharedInstance] applicationDidEnterBackground:application];
}
// App将要从后台返回
- (void)applicationWillEnterForeground:(UIApplication *)application
{
[[EaseMob sharedInstance] applicationWillEnterForeground:application];
}
// 申请处理时间
- (void)applicationWillTerminate:(UIApplication *)application
{
[[EaseMob sharedInstance] applicationWillTerminate:application];
}
EaseMob项⽬架构的搭建
1. 创建根控制器
rootNavigationController:根导航控制器
rootViewController:控制器所有的共同的设置应该在这⾥设置
contentView:继承⾃UIScrollView替代控制的根view
EaseMob 注册
注意点:
注册账号不能为中⽂
在环信后台管理创建应⽤时需要选择开放注册
聊天管理器
获取聊天管理器对象后,可以做登陆、聊天等操作
获取⽅式[EaseMob sharedInstance].chatManager
聊天管理器其实就是遵守了⼀堆功能操作的协议
注册账号的⽅式
/*!
@method
@brief 在聊天服务器上创建账号
@discussion
@param username ⽤户名
@param password 密码
@param pError  错误信息
@result 是否注册成功
*/
- (BOOL)registerNewAccount:(NSString *)username
password:(NSString *)password
error:(EMError **)pError;
/*!
@method
@brief 异步⽅法, 在聊天服务器上创建账号
@discussion 在注册过程中, EMChatManagerLoginDelegate中的didRegisterNewAccount:password:error:回调会被触发
@param username ⽤户名
@param password 密码
@result
*/
- (void)asyncRegisterNewAccount:(NSString *)username
password:(NSString *)password;
/*!
@method
@brief 异步⽅法, 在聊天服务器上创建账号
@discussion
@param username ⽤户名
@param password 密码
@param completion 回调
@param aQueue 回调时的线程
@result
*/
- (void)asyncRegisterNewAccount:(NSString *)username
password:(NSString *)password
withCompletion:(void (^)(NSString *username,
NSString *password,
EMError *error))completion
onQueue:(dispatch_queue_t)aQueue;
我们⼀般是使⽤异步block⽅式注册
其它的功能⼀般也是使⽤异步block⽅式
EaseMob登陆
登陆⽅式
使⽤异步block⽅式登陆
/*!
@method
@brief 使⽤⽤户名密码登录聊天服务器
@discussion 如果登陆失败, 返回nil
@param username ⽤户名
@param password 密码
@param pError  错误信息
@result 登录后返回的⽤户信息
*/
- (NSDictionary *)loginWithUsername:(NSString *)username
password:(NSString *)password
error:(EMError **)pError;
/*!
@method
@brief 异步⽅法, 使⽤⽤户名密码登录聊天服务器
@discussion 在登陆过程中, EMChatManagerLoginDelegate中的didLoginWithInfo:error:回调会被触发
@param username ⽤户名
@param password 密码
@result
*/
- (void)asyncLoginWithUsername:(NSString *)username
password:(NSString *)password;
/*!
@method
@brief 异步⽅法, 使⽤⽤户名密码登录聊天服务器
@discussion
@param username ⽤户名
@param password 密码
@param completion 回调
@param aQueue 回调时的线程
@result
*/
- (void)asyncLoginWithUsername:(NSString *)username
password:(NSString *)password
completion:(void (^)(NSDictionary *loginInfo, EMError *error))completion
onQueue:(dispatch_queue_t)aQueue;
关闭打印数据
[[EaseMob sharedInstance] registerSDKWithAppKey:kEaseMobAppKey apnsCertName:kEaseMobPushName otherConfig:@{kSDKConfigEnableConsoleLogger:@(NO)}];
查看登陆成功的信息
登陆成功之后切换窗⼝的跟控制器
在AppDelegate中提供⼀个登陆成功的⽅法⽤来切换控制器
2. ⾃动登陆
实现原理
在登陆成功之后将登陆信息存储到沙盒中
下次程序启动从沙盒中拿到⽤户名和密码直接调⽤登陆的接⼝
以上操作环信SDK已经做好了,我们只需要设置⾃动登陆的属性即可(setIsAutoLoginEnabled)
登陆完成调⽤代理⽅法
// ⾃动登陆完成的回调⽅法
- (void)didAutoLoginWithInfo:(NSDictionary *)loginInfo error:(EMError *)error
{
NSLog(@"loginInfo = %@",loginInfo);
[MBProgressHUD hideAllHUDsForView:self.window animated:YES];
if (error) {
[[TKAlertCenter defaultCenter]postAlertWithMessage:@"登陆失败"];
}else{下载app里的视频
[[TKAlertCenter defaultCenter]postAlertWithMessage:@"登陆成功"];
[self loginSuccess];
}
}
登陆完来到主页,设置tabbar的图⽚和⽂字颜⾊
3. 重新连接
使⽤真机调试
添加代理,遵守代理协议EMChatManagerDelegate
实现代理⽅法即可
/**
*  即将⾃动连接
*/
- (void)willAutoReconnect
{
NSLog(@"即将重新连接");
self.title = @"连接中...";
}
/**
*  ⾃动连接结束
*
*/
- (void)didAutoReconnectFinishedWithError:(NSError *)error
{
NSLog(@"连接完成");
if (!error) {
self.title = @"聊天";
}
}
/**
*  连接状态发⽣改变调⽤
*
*/
- (void)didConnectionStateChanged:(EMConnectionState)connectionState
{
switch (connectionState) {
case eEMConnectionConnected:
NSLog(@"连接成功");
self.title = @"连接成功";
break;
case eEMConnectionDisconnected:
NSLog(@"连接失败");
self.title = @"连接失败";
break;
default:
break;
}
}
EaseMob退出登陆
1. 退出登陆
主动退出登陆
被动退出登陆
账号多处登陆被顶
正在登陆的账号在服务端被移除
2. 退出登陆的⽅式
/*!
@method
@brief 注销当前登录⽤户
@discussion 当接收到【didLoginFromOtherDevice】和【didRemovedFromServer】的回调时,调⽤此⽅法,isUnbind传NO
@param isUnbind 是否解除device token
@param pError 错误信息
@result 返回注销信息
*/
- (NSDictionary *)logoffWithUnbindDeviceToken:(BOOL)isUnbind
error:(EMError **)pError;
/*!
@method
@brief 异步⽅法, 注销当前登录⽤户
@discussion 当接收到【didLoginFromOtherDevice】和【didRemovedFromServer】的回调时,调⽤此⽅法,isUnbind传NO
@result 完成后【didLogoffWithError:】回调会被触发.
*/
- (void)asyncLogoffWithUnbindDeviceToken:(BOOL)isUnbind;
/*!
@method
@brief 异步⽅法, 注销当前登录⽤户
@discussion 当接收到【didLoginFromOtherDevice】和【didRemovedFromServer】的回调时,调⽤此⽅法,isUnbind传NO
@param completion 回调
@param aQueue    回调时的线程
@result
*/
- (void)asyncLogoffWithUnbindDeviceToken:(BOOL)isUnbind
completion:(void (^)(NSDictionary *info, EMError *error))completion
onQueue:(dispatch_queue_t)aQueue;
建议主动退出登陆isUnbind 传YES,被迫退出登陆传NO
退出成功后在AppDelegate⾥提供切换控制器⽅法,并且设置不再⾃动登陆
EaseMob添加好友
通讯录界⾯搭建
在导航栏左侧添加⼀个添加按钮
点击按钮的时候弹出输⼊框
添加好友
⽅式⼀
要发送添加好友的username 和请求信息
返回的BOOL值YES代表请求添加好友成功,NO代表失败
BOOL addSuccess = [[EaseMob sharedInstance].chatManager error:nil];
if (addSuccess) {
[[TKAlertCenter defaultCenter] postAlertWithMessage:@"添加好友请求成功"];
}
⽅式⼆
要发送添加好友的username 和请求信息
发送将好友分到哪个分组中
返回的BOOL值YES代表请求添加好友成功,NO代表失败
BOOL addSuccess = [[EaseMob sharedInstance].chatManager toGroups:@[@"XMG"] error:nil]    if (addSuccess) {
[[TKAlertCenter defaultCenter] postAlertWithMessage:@"添加好友请求成功"];
}
添加好友成功
在添加好友成功之后没有刷新表格
也就是没有调⽤didUpdateBuddyList代理⽅法
那么可以实现didAcceptedByBuddy代理⽅法
在didAcceptedByBuddy中重新获取好友列表并且刷新表格
EaseMob获取好友列表
获取好友列表
如果每次都需要请求好友列表⽤户体验会不好
所以我们需要在⼀次请求到好友列表之后存储到本地数据库
这些操作环信已经给我们做好了
获取本地好友列表
[[EaseMob sharedInstance].chatManager buddyList];
如果本地没有那么再去服务端获取
[[EaseMob sharedInstance].chatManager asyncFetchBuddyListWithCompletion:^(NSArray *buddyList, EMError *error) {
NSLog(@"====%@",buddyList);
_buddies = buddyList;
} onQueue:nil];
EaseMob接收好友请求
使⽤代理⽅法处理
设置代理
实现代理⽅法
- (void)didReceiveBuddyRequest:(NSString *)username message:(NSString *)message
{
}
在代理⽅法中可以做相应的处理
同意添加请求
BOOL isSuccess = [[EaseMob sharedInstance].chatManager acceptBuddyRequest:username error:nil];
拒绝添加请求
BOOL isSuccess = [[EaseMob sharedInstance].chatManager rejectBuddyRequest:username reason:@"不想加" error:nil]; EaseMob删除好友
1. 当前⽤户移除好友
实现tableView的代理⽅法
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {
}
删除好友
[[EaseMob sharedInstance].chatManager removeBuddy:buddy.username removeFromRemote:YES error:nil];
2. 当前⽤户被好友移除
会调⽤以下代理⽅法
- (void)didRemovedByBuddy:(NSString *)username
{
}
EaseMob聊天界⾯的搭建
1.主要的设计
封装底部的⼯具条
添加四个⼦控件
默认设置发送语⾳按钮隐藏
当点击发送语⾳的时候隐藏输⼊框显⽰语⾳按钮
当输⼊⽂字的时候点击键盘上的return使⽤block⽅式通知控制器
点击加号按钮隐藏键盘弹出⾃定义view
⾃定义view中添加发送图⽚、语⾳和视频按钮
封装模仿聊天的Cell
EaseMob发送好友消息
发送消息
使⽤异步发送
[[EaseMob sharedInstance].chatManager asyncSendMessage:msg progress:nil prepare:^(EMMessage *message, EMError *error) {
NSLog(@"即将发送消息");
} onQueue:nil completion:^(EMMessage *message, EMError *error) {
if (!error) {
NSLog(@"发送消息成功");
}
} onQueue:nil];
发送⼀条消息需要创建⼀个消息对象
// 创建⼀个消息对象
EMMessage *msg = [[EMMessage alloc]initWithReceiver:ctr.buddy.username bodies:@[body]];
创建⼀个消息对象需要创建⼀个消息体
// 创建⼀个消息体
EMTextMessageBody *body = [[EMTextMessageBody alloc]initWithChatObject:chatText];
创建⼀个消息体需要创建⼀个⽂本消息实例
/
/ 创建⼀个⽂本消息实例
EMChatText *chatText = [[EMChatText alloc]];
2.消息发送成功之后的操作
将消息存储到数组中
刷新表格
清空输⼊框
滚动到tableView的底部
EaseMob接收好友消息
1.接收在线消息
设置代理
实现代理⽅法
/
/ 接收到好友消息
- (void)didReceiveMessage:(EMMessage *)message
{
NSLog(@"message =====%@",message);
}
2.接收聊天消息需要注意
判断是否是与当前好友聊天