XMPP资源绑定(Resource Binding)与单台设备登录控制
原文:http://blog.csdn.net/brasbug/article/details/26353511
一个XMPP的账号由三部分组成: 用户名(user/node),域名(domain)和资源(resource) 。例如 alice@xmpp.irusher.com/mobile ,user部分(或node)是alice,domain是xmpp.irusher.com,resource部分是mobile。user和domain组合也叫Bare JID,例如:alice@xmpp.i8i8i8.com ,Bare JID常用标识一个用户。包含了user,domain和resource的ID也叫Full JID,在Full JID中,resource一般用来区分一个用户的多个会话,可以由服务端或客户端指定。下面介绍一下resource的绑定过程。
资源绑定
客户端通过服务端的验证之后,应该给XMPP流绑定一个特殊的资源以使服务端能够正确的定位到客户端。客户端Bare JID后必须附带resource,与服务端交互时使用Full JID,这样就确保服务端和客户端传输XML段时服务端能够正确的找到对应的客户端。
当一个客户端通过一个资源绑定到XML流上后,它就被称之为"已连接的资源"。服务器应该允许同时处理多个”已连接的资源“,每个”已连接的资源“由不同的XML流合不同的resource来区分。
绑定过程
1. 验证通过
服务端在SASL协议成功,发送了响应的stream头之后,必需紧接着发送一个由'urn:ietf:params:xml:ns:xmpp-bind'标识的<bind/>元素。
2. 生成resource标识
一个资源标识至少在同一个Bare JID的所有resource标识中是唯一的,这一点需要由服务端来负责。
2.1 服务端生成resource标志
客户端通过发送一个包含空的<bind/>元素,类型为的set的IQ来请求服务端生成resource标志。
C: <iq id='tn281v37' type='set'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/></iq>
服务端生成后发送响应给客户端:
S: <iq id='tn281v37' type='result'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>juliet@im.example.com/4db06f06-1ea4-11dc-aca3-000bcd821bfb</jid></bind></iq>
失败情况:
- 一个Bare JID下已经达到了同时在线的上限;
- 客户端不被允许绑定资源
2.2 客户端设置resource标志
客户端也可以自己设置resource。
客户端通过发送一个包含<bind/>元素,类型为的set的IQ来请求服务端生成resource标志。<bind/>元素包含一个子元素<resource/>。<resource/>元素包含长度非零的字符串。
C: <iq id='wy2xa82b4' type='set'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><resource>balcony</resource></bind></iq>
服务端应该接受客户端提交的resource标志。服务端通过IQ返回一个<bind/>元素,其中包含了一个<jid>元素,<jid>元素中包含一个Full JID,其中的resource是客户端提交的resource标志。
S: <iq id='wy2xa82b4' type='result'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>juliet@im.example.com/balcony</jid></bind></iq>
服务端有可能会拒绝客户端提供的resource标志,而使用服务端生成的resource标志。
失败情况:
- 提交的标志包含非法字符,地址格式可以在这里查到: Address Format
- 提交的标志已经被占用
2.2.1 resource标志冲突
当客户端提供的resource标志冲突时,服务端应该遵循以下三个策略之一:
- 重新生成新连接提交的resource标志,使新连接能够继续;
- 拒绝新的连接,并维持现有的连接;
- 断开现有的连接,并尝试绑定新的连接;
如果是第一种情况,服务端返回重新生成的resource标志:
S: <iq id='wy2xa82b4' type='result'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>juliet@im.example.com/balcony 4db06f06-1ea4-11dc-aca3-000bcd821bfb</jid></bind></iq>
如果是第二种情况,服务端向新连接返回一个<conflict/>流错误:
S: <iq id='wy2xa82b4' type='error'><error type='modify'><conflict xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/></error></iq>
如果是第三种情况,服务端向已连接的客户端发送<conflict/>流错误,关闭已连接的客户端的流,然后向新的连接发送绑定的结果:
S: <iq id='wy2xa82b4' type='result'><bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'><jid>juliet@im.example.com/balcony</jid></bind></iq>
应用:强制下线
类似QQ,不同设备上最多只能有一个账户在线,例如一个账号不能在两个iPhone上同时在线,在第二台上登录,就要把第一台踢下线,但是,又允许桌面或web上登录相同的账号。像这样的需求,可以通过resource标志来实现。
假定策略如下:一个账号最多只能在相同系统的设备上有一处登录,例如,用户在一台iPhone上登录,如果又在另外一台设备上登录,那就把第一台踢下线,但是可以允许有一个Android设备登录相同的账号。
实现: 在iOS版本中,登录时,客户端提交自定义的resource标志: iOS,同样,Android版本中,提交自定义的resource标志: Android。这样就可以限制相同系统只能有一处登录了。
假如要求一个账号只能在一个移动设备上登录,实现的时候,则需要iOS和Android使用相同的resource标志,例如: Mobile.
需要特别说明的是,当旧的连接被踢下线后,服务端向客户端发送<conflict/>流错误,并关闭流。客户端需要正确的处理这种情况下的应用逻辑。
Openfire配置冲突解决策略
在管理器的的 服务器>服务器管理器>系统属性 中设置属性xmpp.session.conflict-limit的值。
Openfire相关的源码如下,可以根据需要配置对应的属性值:
String username = authToken.getUsername().toLowerCase();// If a session already exists with the requested JID, then check to see// if we should kick it off or refuse the new connectionClientSession oldSession = routingTable.getClientRoute(new JID(username, serverName, resource, true));if (oldSession != null) {try {int conflictLimit = sessionManager.getConflictKickLimit();if (conflictLimit == SessionManager.NEVER_KICK) {reply.setChildElement(packet.getChildElement().createCopy());reply.setError(PacketError.Condition.conflict);// Send the error directly since a route does not exist at this point.session.process(reply);return null;}int conflictCount = oldSession.incrementConflictCount();if (conflictCount > conflictLimit) {// Kick out the old connection that is conflicting with the new oneStreamError error = new StreamError(StreamError.Condition.conflict);oldSession.deliverRawText(error.toXML());oldSession.close();}else {reply.setChildElement(packet.getChildElement().createCopy());reply.setError(PacketError.Condition.conflict);// Send the error directly since a route does not exist at this point.session.process(reply);return null;}}catch (Exception e) {Log.error("Error during login", e);}}
- xmpp.session.conflict-limit == -1 :向新连接发送资源绑定冲突的流错误;
- xmpp.session.conflict-limit <= 1 && != -1 : 向旧连接发送资源绑定冲突的流错误,并且关闭旧的连接(会话);
- xmpp.session.conflict-limit > 1: 向新连接发送资源绑定冲突的流错误;
ps: 修改完需要重启openfire。
XMPPFramework处理
自定义resource
设置XMPPStream类的实例变量myJID,附带自定义的resource即可。
XMPPJID *myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@/%@",user,XMPP_SERVER_ACCOUNT_HOSTNAME,@"xmpp"]];[self.xmppStream setMyJID:myJID];
处理冲突
在XMPPStreamDelegate的回调方法中处理资源绑定冲突产生的流错误:
- (void)xmppStream:(XMPPStream *)sender didReceiveError:(id)error{NSXMLElement *element = (NSXMLElement*) error;NSString *elementName = [element name];//<stream:error xmlns:stream="http://etherx.jabber.org/streams">// <conflict xmlns="urn:ietf:params:xml:ns:xmpp-streams"/>//</stream:error>if ([elementName isEqualToString:@"stream:error"] || [elementName isEqualToString:@"error"]){NSXMLElement *conflict = [element elementForName:@"conflict" xmlns:@"urn:ietf:params:xml:ns:xmpp-streams"];if (conflict){}}}
XMPP资源绑定(Resource Binding)与单台设备登录控制的更多相关文章
- XMPP资源绑定(Resource Binding)
一个XMPP的账号由三部分组成: 用户名(user/node),域名(domain)和资源(resource) .例如 alice@xmpp.irusher.com/mobile ,user部分(或n ...
- DNS资源纪录(Resource Record)介绍
http://dns-learning.twnic.net.tw/bind/intro6.html 类型 SOA NS A AAAA PTR CNAME MX -------------- ...
- php的redis 操作类,适用于单台或多台、多组redis服务器操作
redis 操作类,包括单台或多台.多组redis服务器操作,适用于业务复杂.高性能要求的 php web 应用. redis.php: <?php /* redis 操作类,适用于单台或多台. ...
- 高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少
高性能网络编程(一):单台服务器并发TCP连接数到底可以有多少 阅读(81374) | 评论(9)收藏16 淘帖1 赞3 JackJiang Lv.9 1 年前 | 前言 曾几何时我 ...
- spring mvc: 资源绑定视图解析器(不推荐)
spring mvc: 资源绑定视图解析器(不推荐) 不适合单控制器多方法访问,有知道的兄弟能否告知. 访问地址: http://localhost:8080/guga2/hello/index 项目 ...
- 网络编程释疑之:单台服务器上的并发TCP连接数可以有多少
曾几何时我们还在寻求网络编程中C10K问题的解决方案,但是现在从硬件和操作系统支持来看单台服务器支持上万并发连接已经没有多少挑战性了.我们先假设单台服务器最多只能支持万级并发连接,其实对绝大多数应用来 ...
- Spring MVC资源绑定视图解析器
ResourceBundleViewResolver使用属性文件中定义的视图bean来解析视图名称. 以下示例显示如何使用Spring Web MVC框架中的ResourceBundleViewRes ...
- 读懂在单台机器上创建RabbitMQ集群
在优锐课java中了解有关在单台计算机上安装集群以及如何向集群添加更多节点的更多信息,码了很多专业的相关知识, 分享给大家参考学习. 如果你在单台计算机上设置群集时遇到问题,那么以下文章可能会帮助回答 ...
- 背水一战 Windows 10 (22) - 绑定: 通过 Binding 绑定对象, 通过 x:Bind 绑定对象, 通过 Binding 绑定集合, 通过 x:Bind 绑定集合
[源码下载] 背水一战 Windows 10 (22) - 绑定: 通过 Binding 绑定对象, 通过 x:Bind 绑定对象, 通过 Binding 绑定集合, 通过 x:Bind 绑定集合 作 ...
随机推荐
- 【HDU5306】Gorgeous Sequence
这个题目是Segment-Tree-beats的论文的第一题. 首先我们考虑下这个问题的不同之处在于,有一个区间对x取max的操作. 那么如何维护这个操作呢? 就是对于线段树的区间,维护一个最大值标记 ...
- Swift 特殊关键字 与符号
#available() 函数来检查API函数的可用性 // 判断当前版本是否 iOS8.0+,OSX10.10+以及以其他平台 if #available(iOS 8.0, OSX 10.10, * ...
- Makefile系列之一 : 书写规则
1. 规则 target : prerequisites command 2. example excute 为最终生成的可执行文件. 可以通过命令 make clean来删除所有编译时产生的中间文 ...
- [How to]简单易用的拷贝Mac文件路径方法
效果: 在你想拷贝路径的文件夹或者文件上右键会出现 copy path 选项! 实现: 1.打开finder的的Automator组件 2.选择[服务]选项,点击[选取]按钮 3.搜索操作项目中[拷贝 ...
- 如何设置static tableview的section区域高度
重写代理方法- (CGFloat) tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section { i ...
- keil问题:Error: failed to execute 'C:\Keil\ARM\BIN\ArmCC'
1.打开cmd,进入相应的路径下 cd C:\Keil\ARM\BIN\ 输入 armcc 若显示如下界面则说明keil已经注册 2.若注册成功还没解决问题,则说明软件是在网上下载的破解版的,建议卸 ...
- 查看及连接指定 docker container
环境: 1.centos7 2.以root身份登录 3.已安装docker并且运行着一个container A.查看正在运行的container docker ps 效果: B.查看本地所有conta ...
- 调用手机端硬件功能 汇总(android/ios) Native.js示例汇总
Native.js示例汇总 NJS Native.JS 示例 Native.js虽然强大和开放,但很多web开发者因为不熟悉原生API而难以独立完成.这篇帖子的目的就是汇总各种写好的NJS代码,方便w ...
- JS怎么刷新当前页面
JS怎么刷新当前页面 reload 方法,该方法强迫浏览器刷新当前页面.语法:location.reload([bForceGet]) 参数: bForceGet, 可选参数, 默认为 false,从 ...
- linux命令(50):top命令
TOP是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止.比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中C ...