https://www.jianshu.com/p/84d15683b61e

https://www.cnblogs.com/lurenq/p/7026983.html

1. xmpp简介

XMPP 是一组基于 XML 的技术。用于实时应用程序。最初。XMPP 作为一个框架开发。目标是支持企业环境内的即时消息传递和联机状态应用程序。详细简介

xmpp客户端:

iOS端:

  • 单聊:xmpp单聊是基于服务器端好友设置来进行聊天的,类似于微信,你可以添加好友,经过同意以后就可以进行聊天。
  • 群聊: xmpp 群聊类似于聊天室的类型,你可以创建一个房间,可以邀请其他人加入这个房间,也可以主动加入群,当房间里面大于等于两个人,就可以进行群聊。不过跟微信不同的是,用户在退出这个聊天,性质就跟退出这个群。如果下次再进入这个群,需要在加入这个群,这样会通过传给服务器的最后一次登录时间,服务器会把这个时间段以后的消息,全部推送给你。
xmpp服务器:

Openfire + Spark 简介:下载地址及简单介绍:
openfire + Spark这边服务器端了解不多,只是知道通过下载这个应用,通过配置来创建好友和创建房间,进行简单的单聊群聊。如果做服务器的话,肯定不止这么少东西,如果要学习的话还是自己查资料吧。 Openfire下载地址

Openfire + Saprk 简单介绍

2. xmpp聊天原理

 
xmpp原理图.png

xmpp是基于xml类型进行消息传递的。所有的消息传递类型都是xmpp类型的。
(1) 发送消息类型:

<message type="chat" to="110095@domain" id="d851f47818db46b58abf4e982327ab36"><request xmlns="urn:xmpp:receipts"></request><body>{
"messageId" : "d851f47818db46b58abf4e982327ab36", //messageid
"content" : "你好", //消息内容
"fromUserName" : "张三", //发送人
"isSecret" : 0, //单聊是否是密聊消息 0:正常聊天 1:密聊
"timeLen" : 0,
"isAt" : 0, //群聊是否@人
"timeSend" : 1522034423, //发送时间
"type" : 1 //消息类型
}</body></message>

(2) 接收消息类型:

<message xmlns="jabber:client" id="cdc6007ca03b401f8c9e4a2ccb75d8d5" type="chat" to="111751@domain" from="109965@domain/resource"><request xmlns="urn:xmpp:receipts"></request><body>{
"messageId" : "cdc6007ca03b401f8c9e4a2ccb75d8d5",
"content" : "天津",
"fromUserName" : "李四",
"isSecret" : 0,
"timeLen" : 0,
"isAt" : 0,
"timeSend" : 1522034878,
"type" : 1
}</body></message>

(3)发送回执消息类型:

<message to="109965@domain/resource" type="chat"><received xmlns="urn:xmpp:receipts" id="cdc6007ca03b401f8c9e4a2ccb75d8d5"/></message>

(4)接收回执消息类型:

<message xmlns="jabber:client" type="chat" to="111751@domain/resource" from="109965@domain/resource"><received xmlns="urn:xmpp:receipts" id="626b61f461eb485fa99177b69512273e"></received></message>

流程:客户端A给客户端B发送一条消息,消息先从客户端A传到服务器,服务器通过判断客户端B是否在线,如果在线的就直接把消息推送给客户端B,客户端B收到消息以后,则发送后回执,服务器收到回执以后再推给客户端A。
如果客户端B不在线,则消息在服务器端保存,服务器发送回回执告诉客户端A发送消息成功,等客户端B上线以后再把消息推送给客户端B。

问题1: 当客户端A给B推送消息时,客户端B正好此时进入后台。由于后台心跳检测还没有检测到B已经离线,这个时候服务器会把消息推送给客户端B,并且服务器没有保存这条消息。而此时客户端B已经进入后台,无法接收到消息,也就无法发送回执。会造成客户端A发送消息失败。
解决方法:服务器每次收到客户端A都会发送的消息,都会由服务器发送回执告诉A已经发送成功。客户端首先保存聊天信息,再客户端B是否在线。如果在线,则发送消息,接受回执,如果没有收到回执,则当成不在线,保存聊天消息为未发送状态,下次等客户端B连线后推送。

3. xmpp IOS端使用架构

封装的xmpp类图

 
xmppHelper类图

(1) XMPPStream 类主要是设置主机名(HostName), 端口号(port), 链接xmpp服务器,及获取其delegate,来获取登录状态及输入xmppPassword来进行登录

(2)XMPPReconnect来设置重新连接的时间,及激活xmppStream

(3) xmppRoster 获取好友花名册

(4)xmppRosterStorage 来通过xmpp自带的coreData数据库保存用户数据

(5)roomPool是保存用户进入房间的池子,可以通过池子中的所有房间,获取群聊房间的离线消息和退出登录时,退出所有群聊房间。

APPDelegate中调用xmpp的流程

  • applicationWillEnterForeground方法中[[NTXmpp sharedInstance] login];
  • applicationDidEnterBackground方法中[[NTXmpp sharedInstance] disconnect];
  • 登录成功以后[[NTXmpp sharedInstance] performSelector:@selector(login) withObject:nil afterDelay:0.5];//0.5秒后执行xmpp登录

XMPPHelper方法代码

+ (NTXmpp*)sharedInstance
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[NTXmpp alloc] init];
[sharedManager setupStream];
});
return sharedManager;
} //开始配置xmppstream
#pragma mark--------配置XML流---------
- (void)setupStream
{
NSAssert(xmppStream == nil, @"Method setupStream invoked multiple times"); xmppStream = [[XMPPStream alloc] init]; #if !TARGET_IPHONE_SIMULATOR
{
xmppStream.enableBackgroundingOnSocket = YES;
}
#endif xmppReconnect = [[XMPPReconnect alloc] init];
xmppReconnect.reconnectTimerInterval = 10; //xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init]; //xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:xmppRosterStorage]; //xmppRoster.autoFetchRoster = YES;
//xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES; [xmppReconnect activate:xmppStream];
//[xmppRoster activate:xmppStream]; // Add ourself as a delegate to anything we may be interested in [xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[xmppReconnect addDelegate:self delegateQueue:dispatch_get_main_queue()]; [xmppStream setHostName:g_config.XMPPHost];
[xmppStream setHostPort:5222]; // You may need to alter these settings depending on the server you're connecting to
//allowSelfSignedCertificates = NO;
//allowSSLHostNameMismatch = NO;
self.roomPool = [[NTRoomPool alloc] init];
} //xmpp登陆
- (void)login
{
if (isLogined == login_status_yes)
return;
if (![self connect]) {
[HUDNotificationCenter showMessage:@"服务连接失败" hideAfter:2.0f];
};
} - (BOOL)connect
{
if (![xmppStream isDisconnected])
return YES; NSString* myJID = g_loginUser.userId;
//NSString *myPassword = [[NSUserDefaults standardUserDefaults] stringForKey:kMY_USER_PASSWORD]; 112003
NSString* myPassword = g_loginUser.xmppPassword; if (myJID == nil || myPassword == nil) {
return NO;
}
self.isLogined = login_status_ing;
[self notify];
[xmppStream setMyJID:[XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@/%@", myJID, g_config.XMPPDomain, XMPP_RESOURCE]]];
password = myPassword; NSError* error = nil;
if (![xmppStream connectWithTimeout:10 error:&error]) {
self.isLogined = login_status_no;
[self notify];
return NO;
}
return YES;
} - (void)disconnect
{
[self goOffline];
[xmppStream disconnect];
}
#xmppStreamDelegate

- (void)xmppStreamDidConnect:(XMPPStream*)sender
{
NTLog(@"xmpp connect success");
isXmppConnected = YES; NSError* error = nil; if (![xmppStream authenticateWithPassword:password error:&error]) {
self.isLogined = login_status_no;
[self notify];
//DDLogError(@"Error authenticating: %@", error);
}
} - (void)xmppStreamDidDisconnect:(XMPPStream*)sender withError:(NSError*)error
{
NTLog(@"xmpp did disconnect");
[self logout]; if (!isXmppConnected) {
//[xmppStream authenticateWithPassword:password error:&error];
}
} - (void)xmppStreamDidAuthenticate:(XMPPStream*)sender
{
NTLog(@"xmpp did authenticate");
//DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
[self doLogin]; // NSError* error;
//[xmppStream authenticateWithPassword:password error:&error];
} - (void)xmppStream:(XMPPStream*)sender didNotAuthenticate:(NSXMLElement*)error
{
NTLog(@"xmpp did not Autherticate");
NTLog(@"didNotAuthenticate :密码校验失败,登录不成功,原因是:%@", [error XMLString]);
self.isLogined = login_status_no;
[self notify];
//DDLogVerbose(@"%@: %@", THIS_FILE, THIS_METHOD);
}

4. xmpp发送消息

xmpp发送消息需要把消息包装成xml类型

#pragma mark------收发消息-------
- (void)sendMessage:(NTMessage*)msg roomName:(NSString*)roomName //TODO: 添加是否secret参数
{
XMPPMessage* aMessage;
NSString* from = [NSString stringWithFormat:@"%@@%@", msg.fromUserId, g_config.XMPPDomain];
if (roomName == nil) {
//单聊
NSString* to = [NSString stringWithFormat:@"%@@%@", msg.toUserId, g_config.XMPPDomain];
aMessage = [XMPPMessage messageWithType:@"chat" to:[XMPPJID jidWithString:to]];
to = nil;
}
else {
//群聊
NSString* roomJid = [NSString stringWithFormat:@"%@@muc.%@", roomName, g_config.XMPPDomain];
aMessage = [XMPPMessage messageWithType:@"groupchat" to:[XMPPJID jidWithString:roomJid]];
roomJid = nil;
}
[aMessage addAttributeWithName:@"id" stringValue:msg.messageId]; NSXMLElement* request = [NSXMLElement elementWithName:@"request" xmlns:@"urn:xmpp:receipts"];
[aMessage addChild:request];
request = nil; DDXMLNode* node = [DDXMLNode elementWithName:@"body" stringValue:jsonString];
[aMessage addChild:node];
[xmppStream sendElement:aMessage];
//[aMessage release]; [_poolSend setObject:msg forKey:msg.messageId];
//NSrunloop的一个方法
[self performSelector:@selector(onSendTimeout:) withObject:msg afterDelay:[msg getMaxWaitTime]]; from = nil;
}

5. xmpp 接收消息流程

 
接收消息流程
- (void)xmppStream:(XMPPStream*)sender didReceiveMessage:(XMPPMessage*)message
{
//analysis message
}

6 xmpp遇到的问题

  • 群组消息同时推送过来的压力

在xmpp登录以后,需要通过接口获取群列表,在使用for循环进行请求每个群的离线消息,如果距离上次时间太长,就会有很多离线消息等待解析,存储到数据库。
解决方法:采用异步请求服务器,获取离线消息,使用多线程来执行消息的数据库存储。使用主线程来把用户加入到当前群。

  • 当过多消息同时推送过来,内存耗用过多,cpu耗能过大造成卡顿。

主要是xmpp框架中有内置的coredata数据库,每条消息会存储到coreData中。并且这个是xmpp内部控制的。
解决方法。在xmpp框架中,手动设置信息不保存到coreData中。我们是用的fmdb来保存消息。

XMPP详解的更多相关文章

  1. 基于XMPP的即时通信系统的建立(二)— XMPP详解

    XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...

  2. 基于XMPP的即时通信系统的建立 — XMPP IQ详解

    XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...

  3. XMPP即时通讯协议使用(前传)——协议详解

    XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...

  4. Android高效率编码-第三方SDK详解系列(三)——JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送

    Android高效率编码-第三方SDK详解系列(三)--JPush推送牵扯出来的江湖恩怨,XMPP实现推送,自定义客户端推送 很久没有更新第三方SDK这个系列了,所以更新一下这几天工作中使用到的推送, ...

  5. 理论经典:TCP协议的3次握手与4次挥手过程详解

    1.前言 尽管TCP和UDP都使用相同的网络层(IP),TCP却向应用层提供与UDP完全不同的服务.TCP提供一种面向连接的.可靠的字节流服务. 面向连接意味着两个使用TCP的应用(通常是一个客户和一 ...

  6. ActiveMQ基本详解与总结

    MQ简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过写和检索出入列队的针对应用程序的数据(消息)来通信,而无需专用连接来链接它们.消息传 ...

  7. 详解RPC远程调用和消息队列MQ的区别

    PC(Remote Procedure Call)远程过程调用,主要解决远程通信间的问题,不需要了解底层网络的通信机制. RPC框架 知名度较高的有Thrift(FB的).dubbo(阿里的). RP ...

  8. 了解iOS消息推送一文就够:史上最全iOS Push技术详解

    本文作者:陈裕发, 腾讯系统测试工程师,由腾讯WeTest整理发表. 1.引言 开发iOS系统中的Push推送,通常有以下3种情况: 1)在线Push:比如QQ.微信等IM界面处于前台时,聊天消息和指 ...

  9. 【转】Zabbix 3.0 从入门到精通(zabbix使用详解)

    [转]Zabbix 3.0 从入门到精通(zabbix使用详解) 第1章 zabbix监控 1.1 为什么要监控 在需要的时刻,提前提醒我们服务器出问题了 当出问题之后,可以找到问题的根源   网站/ ...

随机推荐

  1. 「题解」「CF1019B」The hat

    目录 题目 背景 程序输出 交互程序输入 题解 程序 题目 背景 这是一道交互题. 一共有 \(n\) 个人做成一圈,他们的编号从 \(1\) 到 \(n\). 现在每个人的手里面都有一个数字 \(a ...

  2. rancher布控集群启动失败的猜测

    rancher布控集群启动失败的猜测 待办 报告缺少某个文件.多线程启动任务部署的时候某些线程跑在前边了, 导致问题出现 或者 网络问题出现超时,导致出现此类报错 或者 内存不足导致问题出现报错 或者 ...

  3. 1025 PAT Ranking (25分)

    1025 PAT Ranking (25分) 1. 题目 2. 思路 设置结构体, 先对每一个local排序,再整合后排序 3. 注意点 整体排序时注意如果分数相同的情况下还要按照编号排序 4. 代码 ...

  4. 解决win10状态栏的搜索框无法搜索本地应用或无反应

    今天突然出现的问题,在状态栏左下角的搜索框搜索OneNote没有任何反应. 1.首先,打开管理员命令窗口,win+x,可以看到弹出一个窗口,打开windows Powershell(管理员)如图 2, ...

  5. win10显示“没有有效的IP地址”

    可能你没有新建该宽带连接!!!(本人就是蠢到如此地步了_(:з)∠)_)

  6. AcWing 838. 堆排序

    #include <iostream> #include <algorithm> using namespace std; ; int n, m; int h[N], size ...

  7. 使用Quartz Job 简单的做一个定时服务

    第一步:创建一个windows服务 第二步:通过NuGet 安装Quartz (我搜索了Quartz 关键字 安装了 ) 第三步 代码部分 任务类 如 多个任务 就多几个类 public class ...

  8. 用 async/await 来处理异步(转)

    昨天看了一篇vue的教程,作者用async/ await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await 已经被标准化,是时候学习一下了. 先说一下async的用法,它作为一个 ...

  9. Jmeter 测试结果分析之聚合报告简介

    聚合报告(aggregate report) 对于每个请求,它统计响应信息并提供请求数,平均值,最大,最小值,错误率,大约吞吐量(以请求数/秒为单位)和以kb/秒为单位的吞吐量. 吞吐量是以取样目标点 ...

  10. linux shell seq命令详解

    seq: squeue  是一个序列的缩写,主要用来输出序列化的东西 seq常见命令参数 用法:seq [选项]... 尾数 或:seq [选项]... 首数 尾数 或:seq [选项]... 首数 ...