五.添加&删除好友
- 在XMPP框架中提供的添加好友的机制是一种订阅(Subscription)机制,类似于微博中的关注,即A可以添加B为好友,但是B不一定要添加A为好友
- 如果要实现双向添加和删除,可以将xmppRoster的autoAcceptKnownPresenceSubscriptionRequests属性设置为YES The default value is YES.
- 添加好友之前对它进行判断如果是自己和已经存在的好友不予添加
-(void)addUser{
NSString *user = self.textField.text;
//1.不能添加自己为好友
if ([userisEqualToString:[WCAccountshareAccount].loginUser]) {
[self showMsg:@"不能添加自己为好友"];
return;
}
//2.已经存在好友无需添加
XMPPJID *userJid = [XMPPJIDjidWithUser:userdomain:[WCAccountshareAccount].domainresource:nil];
BOOL userExists = [[WCXMPPToolsharedWCXMPPTool].rosterStorageuserExistsWithJID:userJidxmppStream:[WCXMPPToolsharedWCXMPPTool].xmppStream];
if (userExists) {
[self showMsg:@"好友已经存在"];
return;
}
//3.添加好友 (订阅)
[[WCXMPPToolsharedWCXMPPTool].rostersubscribePresenceToUser:userJid];
}
备注:添加好友在现有openfire存在的问题 添加不存在的好友通讯录也显示了好友
解决办法1.服务器可以拦截好友添加的请求,如当前数据库没有好友,不要返回信息
解决办法2. 过滤数据库的Subscription字段查询请求
none 对方没有同意添加好友
to 发给对方的请求
from 别人发来的请求
both 双方互为好友
#pragma mark 删除好友
-(void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath{
XMPPUserCoreDataStorageObject *user =_resultsContr.fetchedObjects[indexPath.row];
if (editingStyle ==UITableViewCellEditingStyleDelete) {
//删除好友
[[WCXMPPToolsharedWCXMPPTool].rosterremoveUser:user.jid];
}
//刷新表格?
}
#pragma mark -结果控制器的代理
#pragma mark -数据库内容改变
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller{
//刷新表格
[self.tableViewreloadData];
}
六.聊天数据
#pragma mark 发送聊天数据
-(BOOL)textFieldShouldReturn:(UITextField *)textField{
NSString *txt = textField.text;
//怎么发聊天数据 chat格式参考收到的数据格式可知
XMPPMessage *msg = [XMPPMessagemessageWithType:@"chat"to:self.friendJid];
[msg addBody:txt];
[[WCXMPPToolsharedWCXMPPTool].xmppStreamsendElement:msg];
// 清空输入框的文本
textField.text = nil;
return YES;
}
接受到消息的xml数据格式
/
/加载数据库的聊天数据
-(void)setChatdata{
// 1.上下文
NSManagedObjectContext *msgContext = [WCXMPPToolsharedWCXMPPTool].msgArchivingStorage.mainThreadManagedObjectContext;
// 2.查询请求
NSFetchRequest *request = [NSFetchRequestfetchRequestWithEntityName:@"XMPPMessageArchiving_Message_CoreDataObject"];
// 过滤(当前登录用户并且好友的聊天消息)
NSString *loginUserJid = [WCXMPPToolsharedWCXMPPTool].xmppStream.myJID.bare;
WCLog(@"%@",loginUserJid);
NSPredicate *pre = [NSPredicatepredicateWithFormat:@"streamBareJidStr = %@ AND bareJidStr = %@",loginUserJid,self.friendJid.bare];
request.predicate = pre;
// 设置时间排序
NSSortDescriptor *timeSort = [NSSortDescriptorsortDescriptorWithKey:@"timestamp"ascending:YES];
request.sortDescriptors = @[timeSort];
// 3.执行请求
_resultContr = [[NSFetchedResultsControlleralloc]initWithFetchRequest:requestmanagedObjectContext:msgContextsectionNameKeyPath:nilcacheName:nil];
_resultContr.delegate =self;
NSError *err = nil;
[_resultContr performFetch:&err];
}
六.表情的输入
- 在iOS中,表情文字是通过Unicode字符实现的
- Unicode(中文:万国码、国际码、统一码、单一码)是计算机科学领域里的一项业界标准。它对世界上大部分的文字系统进行了整理、编码,使得电脑可以用更为简单的方式来呈现和处理文字
- Unicode伴随着通用字符集的标准而发展,同时也以书本的形式对外发表。Unicode至今仍在不断增修,每个新版本都加入更多新的字符,目前最新的版本为第六版,已收入了超过十万个字符
- Unicode涵盖的数据除了视觉上的字形、编码方法、标准的字符编码外,还包含了字符特性,如大小写字母
- Unicode备受认可,并广泛地应用于电脑软件的国际化与本地化过程。有很多新科技,如可扩展置标语言、Java编程语言以及现代的操作系统,都采用Unicode编码
- 表情符号的参照地址:
- http://www.easyapns.com/iphone-emoji-alerts
XMPP中的文件传输
- XEP-0096: File Transfer这是传输文件的统一接口,客户端之间用它来协商到底采用那种具体的传输方式,包括以下三种:
- XEP-0047: In-Band ByteStreams带内字节流,这个协议实际上用于小数据量传输,只是它用的字节流传输,所以也顺便说一下。带内,也就是夹带在XML流中,通过XMPP服务器中转传输。具体用法是把数据用base64编码放在XML流中传给对方。这个办法不好,base64编码效率很低,而且所有数据必须由服务器中转(字符串格式的数据中转)
- XEP-0066: Out of Band Data带外字节流,带内不行就走带外,也就是不经过XMPP服务器。这个用法是在发起传输的客户端临时建立一个http服务(当然也可以是别的服务),把自己的IP和端口(通过XMPP消息)告诉接收方,让对方直接来下载。这个方法有一个问题,发送一方必须是公网IP,否则对方无法访问。 注:目前Pandion,Linq支持这个XEP
- XEP-0065: SOCKS5 Bytestreams SOCKS5字节流,使用SOCKS5传输文件,有直连式和代理传输两种方式。发送方把预定的IP和端口(通过XMPP消息)告诉接收方。如果双方都在公网,采用SOCKS直接传输。如果任何一方在内网,经过SOCKS5代理服务器传输,发送方把代理服务器的IP和端口告诉给接收方。这里的SOCKS5代理服务器和通用的代理服务器稍有差别,因为它需要通过发送方提出的一个sessionID由XMPP服务器通知SOCKS5代理服务器把双方的SOCKS通道连通,也就是激活。 注:目前Psi,Linq支持这个XEP
Base64
- Base64是网络上最常见的用于传输8Bit字节代码的编码方式之一,大家可以查看RFC2045~RFC2049,上面有MIME的详细规范。Base64编码可用于在HTTP环境下传递较长的标识信息
- 采用Base64编码不仅比较简短,同时也具有不可读性,即所编码的数据不会被人用肉眼所直接看到
#pragma mark 用户选择的图片
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{
WCLog(@"%@",info);
UIImage *img = info[UIImagePickerControllerOriginalImage];
//发送附件
[selfsendAttachmentWithData:UIImagePNGRepresentation(img)bodyType:@"image"];
//隐藏图片选择的控制器
[selfdismissViewControllerAnimated:YEScompletion:nil];
}
#pragma mark 发送图片附件
-(void)sendAttachmentWithData:(NSData *)data bodyType:(NSString *)bodyType{
//发送图片
XMPPMessage *msg = [XMPPMessagemessageWithType:@"chat"to:self.friendJid];
//设置类型
[msg addAttributeWithName:@"bodyType"stringValue:bodyType];//对应标签内部的属性
#pragma mark 没有body就不认
[msg addBody:bodyType];
// [msg addBody:@"sound"];
// [msg addBody:@"doc"];
// [msg addBody:@"xls"];
//把附件经过"base64编码"转成字符串
NSString *base64Str = [database64EncodedStringWithOptions:0];
//定义附件
XMPPElement *attachment = [XMPPElementelementWithName:@"attachment"stringValue:base64Str];
//添加字节点
[msg addChild:attachment];
[[WCXMPPToolsharedWCXMPPTool].xmppStreamsendElement:msg];
}
两种比较好的处理图片方式
#pragma mark接受图片数据1
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *ID =@"ChatCell";
UITableViewCell *cell = [tableViewdequeueReusableCellWithIdentifier:ID
];
//获取聊天信息
XMPPMessageArchiving_Message_CoreDataObject *msgObj =_resultContr.fetchedObjects[indexPath.row];
// 判断消息的类型到有没有附件
// 1.获取原始的xml数据
XMPPMessage *message = msgObj.message;
// 获取附件的类型
NSString *bodyType = [message attributeStringValueForName:@"bodyType"];
if ([bodyType isEqualToString:@"image"]) {//图片
// 2.遍历message的子节点
NSArray *child = message.children;
for (XMPPElement *notein child) {
// 获取节点的名字
if ([[note name] isEqualToString:@"attachment"]){
WCLog(@"获取到附件....");
//获取附件字符串,然后转成NSData,接转成图片
NSString *imgBase64Str = [notestringValue];
NSData *imgData = [[NSDataalloc] initWithBase64EncodedString:imgBase64Stroptions:0];
UIImage *img = [UIImageimageWithData:imgData];
cell.imageView.image = img;
}
}
}else if([bodyTypeisEqualToString:@"sound"]){//音频
}else{//纯文本
cell.textLabel.text = msgObj.body;
}
return cell;
}