client登陆openfire,大概总共须要9个来回才完毕登录。

在2G情况下。就表现为client登录特别慢,所以,为解决问题,对openfire进行了例如以下优化

openfire的连接、登陆过程分为几个步骤,完整报文例如以下。总共分为9个round trip:

===================================================================================================================

1  STREAM

RECV:<stream:stream to="jacklin-pc" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">

SENT:<?xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="jacklin-pc" id="96508a6d" xml:lang="en" version="1.0">

SENT:<stream:features><starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"></starttls><mechanisms xmlns="urn:ietf:p 

arams:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism><mechanism>JIVE-SHAREDSECRET</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism><mechanism>CRAM-MD5</mechanism></mechanisms><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><auth
xmlns="http://jabber.org/features/iq-auth"/><register xmlns="http://jabber.org/features/iq-register"/></stream:features>

2  TLS

RECV:<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

SENT:<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

3  STREAM

RECV:<stream:stream to="jacklin-pc" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">

SENT:<?

xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="jacklin-pc" id="96508a6d" xml:lang="en" version="1.0"><stream:features><mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl"><mechanism>DIGEST-MD5</mechanism><mechanism>JIVE-SHAREDSECRET</mechanism><mechanism>PLAIN</mechanism><mechanism>ANONYMOUS</mechanism><mechanism>CRAM-MD5</mechanism></mechanisms><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><auth
xmlns="http://jabber.org/features/iq-auth"/><register xmlns="http://jabber.org/features/iq-register"/></stream:features>

4  SASL

RECV:<auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09ImphY2tsaW4tcGMiLG5vbmNlPSJMamw2RGt4Y3hGSDZxb2dTRE55Nmw2VkYreVQ2YjROMFlTa1BBSlZqIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz</auth>

SENT:<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"></success>

5  STREAM

RECV:<stream:stream to="jacklin-pc" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0">

SENT:<?

xml version='1.0' encoding='UTF-8'?><stream:stream xmlns:stream="http://etherx.jabber.org/streams" xmlns="jabber:client" from="jacklin-pc" id="96508a6d" xml:lang="en" version="1.0"><stream:features><compression xmlns="http://jabber.org/features/compress"><method>zlib</method></compression><bind
xmlns="urn:ietf:params:xml:ns:xmpp-bind"/><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></stream:features>

6  BIND

RECV:<iq id="SfW08-0" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>Spark 2.6.3</resource></bind></iq>

SENT:<iq type="result" id="SfW08-0" to="jacklin-pc/96508a6d"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>test001@jacklin-pc/Spark 2.6.3</jid></bind></iq>

7  SESSION

RECV:<iq id="SfW08-1" type="set"><session xmlns="urn:ietf:params:xml:ns:xmpp-session"/></iq>

SENT:<iq type="result" id="SfW08-1" to="test001@jacklin-pc/Spark 2.6.3"/>

8  PRESENCE

RECV:<presence id="SfW08-6"><status>在线</status><priority>1</priority></presence>

9  ZLIB

RECV:<compress xmlns='http://jabber.org/protocol/compress'><method>zlib</method></compress>

SENT:<compressed xmlns='http://jabber.org/protocol/compress'/>

===================================================================================================================

1  STREAM优化

当中STREAM类似查询server功能,server会把server的特性返回给client,比如SASL策略,iq-auth,zlib压缩,xmpp-bind等等。事实上,假设是内部定制的系统,这些特性server与client都是共知的,所以不须要查询,全然能够省略这些步骤。

可是,我发现,在client第一次发送stream时,是须要初始化一些内容的。所以,须要再例如以下地方。增加例如以下代码:

org.jivesoftware.openfire.nio.ConnectionHandler

  1. @Override
  2. public void sessionOpened(IoSession session) throws Exception {
  3. // Create a new XML parser for the new connection. The parser will be
  4. // used by the XMPPDecoder filter.
  5. final XMLLightweightParser parser = new XMLLightweightParser(CHARSET);
  6. session.setAttribute(XML_PARSER, parser);
  7. // Create a new NIOConnection for the new session
  8. final NIOConnection connection = createNIOConnection(session);
  9. session.setAttribute(CONNECTION, connection);
  10. session.setAttribute(HANDLER, createStanzaHandler(connection));
  11. // Set the max time a connection can be idle before closing it. This
  12. // amount of seconds
  13. // is divided in two, as Openfire will ping idle clients first (at 50%
  14. // of the max idle time)
  15. // before disconnecting them (at 100% of the max idle time). This
  16. // prevents Openfire from
  17. // removing connections without warning.
  18. final int idleTime = getMaxIdleTime() / 2;
  19. if (idleTime > 0) {
  20. session.setIdleTime(IdleStatus.READER_IDLE, idleTime);
  21. }
  22.  
  23. // ADD LOCALSESSION START===========================================================
  24. Log.info("[DO LOCALSESSION]");
  25. int hashCode = Thread.currentThread().hashCode();
  26. XMPPPacketReader parser1 = parsers.get(hashCode);
  27. if (parser1 == null) {
  28. parser1 = new XMPPPacketReader();
  29. parser1.setXPPFactory(factory);
  30. parsers.put(hashCode, parser1);
  31. }
  32.  
  33. String msg = "<stream:stream to='jacklin-pc' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>";
  34.  
  35. StanzaHandler handler = (StanzaHandler) session.getAttribute(HANDLER);
  36. try {
  37. handler.process(msg, parser1);
  38. } catch (Exception e) {
  39. Log.error(
  40. "Closing connection due to error while processing message: "
  41. + msg, e);
  42. connection.close();
  43. }
  44. // ADD LOCALSESSION END==============================================================
  45.  
  46. }

此目的是让server在client建立连接阶段就初始化client的资源。

2  SESSION

session事实上从server端看,仅仅是回复了一下client,并没有起什么作用,所以client能够不发这段报文,server端不须要修改。server端处理步骤例如以下。

org.jivesoftware.openfire.handler.IQSessionEstablishmentHandler

  1. public class IQSessionEstablishmentHandler extends IQHandler {
  2.  
  3. private IQHandlerInfo info;
  4.  
  5. public IQSessionEstablishmentHandler() {
  6. super("Session Establishment handler");
  7. info = new IQHandlerInfo("session", "urn:ietf:params:xml:ns:xmpp-session");
  8. }
  9.  
  10. @Override
  11. public IQ handleIQ(IQ packet) throws UnauthorizedException {
  12. // Just answer that the session has been activated
  13. IQ reply = IQ.createResultIQ(packet);
  14. return reply;
  15. }
  16.  
  17. @Override
  18. public IQHandlerInfo getInfo() {
  19. return info;
  20. }
  21. }

3  BIND,PRESENCE,ZLIB

事实上PRESENCE和ZLIB能够在clientBIND操作之后。server端直接进行,不须要client再次协商。所以。我在下面代码。进行了下面修改:

org.jivesoftware.openfire.handler.IQBindHandler

  1. @Override
  2. public IQ handleIQ(IQ packet) throws UnauthorizedException {
  3. LocalClientSession session = (LocalClientSession) sessionManager
  4. .getSession(packet.getFrom());
  5. // If no session was found then answer an error (if possible)
  6. if (session == null) {
  7. Log.error("Error during resource binding. Session not found in "
  8. + sessionManager.getPreAuthenticatedKeys() + " for key "
  9. + packet.getFrom());
  10. // This error packet will probably won't make it through
  11. IQ reply = IQ.createResultIQ(packet);
  12. reply.setChildElement(packet.getChildElement().createCopy());
  13. reply.setError(PacketError.Condition.internal_server_error);
  14. return reply;
  15. }
  16.  
  17. IQ reply = IQ.createResultIQ(packet);
  18. Element child = reply.setChildElement("bind",
  19. "urn:ietf:params:xml:ns:xmpp-bind");
  20. // Check if the client specified a desired resource
  21. String resource = packet.getChildElement().elementTextTrim("resource");
  22. if (resource == null || resource.length() == 0) {
  23. // None was defined so use the random generated resource
  24. resource = session.getAddress().getResource();
  25. } else {
  26. // Check that the desired resource is valid
  27. try {
  28. resource = JID.resourceprep(resource);
  29. } catch (StringprepException e) {
  30. reply.setChildElement(packet.getChildElement().createCopy());
  31. reply.setError(PacketError.Condition.jid_malformed);
  32. // Send the error directly since a route does not exist at this
  33. // point.
  34. session.process(reply);
  35. return null;
  36. }
  37. }
  38. // Get the token that was generated during the SASL authentication
  39. AuthToken authToken = session.getAuthToken();
  40. if (authToken == null) {
  41. // User must be authenticated before binding a resource
  42. reply.setChildElement(packet.getChildElement().createCopy());
  43. reply.setError(PacketError.Condition.not_authorized);
  44. // Send the error directly since a route does not exist at this
  45. // point.
  46. session.process(reply);
  47. return reply;
  48. }
  49. if (authToken.isAnonymous()) {
  50. // User used ANONYMOUS SASL so initialize the session as an
  51. // anonymous login
  52. session.setAnonymousAuth();
  53. } else {
  54. String username = authToken.getUsername().toLowerCase();
  55. // If a session already exists with the requested JID, then check to
  56. // see
  57. // if we should kick it off or refuse the new connection
  58. ClientSession oldSession = routingTable.getClientRoute(new JID(
  59. username, serverName, resource, true));
  60. if (oldSession != null) {
  61. try {
  62. int conflictLimit = sessionManager.getConflictKickLimit();
  63. if (conflictLimit == SessionManager.NEVER_KICK) {
  64. reply.setChildElement(packet.getChildElement()
  65. .createCopy());
  66. reply.setError(PacketError.Condition.conflict);
  67. // Send the error directly since a route does not exist
  68. // at this point.
  69. session.process(reply);
  70. return null;
  71. }
  72.  
  73. int conflictCount = oldSession.incrementConflictCount();
  74. if (conflictCount > conflictLimit) {
  75. // Kick out the old connection that is conflicting with
  76. // the new one
  77. StreamError error = new StreamError(
  78. StreamError.Condition.conflict);
  79. oldSession.deliverRawText(error.toXML());
  80. oldSession.close();
  81. } else {
  82. reply.setChildElement(packet.getChildElement()
  83. .createCopy());
  84. reply.setError(PacketError.Condition.conflict);
  85. // Send the error directly since a route does not exist
  86. // at this point.
  87. session.process(reply);
  88. return null;
  89. }
  90. } catch (Exception e) {
  91. Log.error("Error during login", e);
  92. }
  93. }
  94. // If the connection was not refused due to conflict, log the user
  95. // in
  96. session.setAuthToken(authToken, resource);
  97. }
  98.  
  99. child.addElement("jid").setText(session.getAddress().toString());
  100. // Send the response directly since a route does not exist at this
  101. // point.
  102. session.process(reply);
  103. // After the client has been informed, inform all listeners as well.
  104. SessionEventDispatcher.dispatchEvent(session,
  105. SessionEventDispatcher.EventType.resource_bound);
  106.  
  107. // ADD COMPRESSION START==================================================
  108. session.getConnection().addCompression();
  109.  
  110. session.getConnection().startCompression();
  111. Log.info("[DO COMPRESSION]");
  112. // ADD COMPRESSION END====================================================
  113.  
  114. // ADD PRESENCE START====================================================
  115. String domain = XMPPServer.getInstance().getServerInfo()
  116. .getXMPPDomain();
  117. Presence pp = new Presence();
  118. pp.setFrom(session.getAddress().toString());
  119. // pp.setTo(domain);
  120. XMPPServer.getInstance().getPacketRouter().route(pp);
  121. Log.info("[DO PRESENCE]:"+ pp.toXML());
  122. // ADD PRESENCE END====================================================
  123.  
  124. return null;
  125. }

经过以上优化之后,server与client的协商仅仅剩下3个round trip。步骤例如以下:

2  TLS

RECV:<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

SENT:<proceed xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>

4  SASL

RECV:<auth mechanism="PLAIN" xmlns="urn:ietf:params:xml:ns:xmpp-sasl">cmVhbG09ImphY2tsaW4tcGMiLG5vbmNlPSJMamw2RGt4Y3hGSDZxb2dTRE55Nmw2VkYreVQ2YjROMFlTa1BBSlZqIixxb3A9ImF1dGgiLGNoYXJzZXQ9dXRmLTgsYWxnb3JpdGhtPW1kNS1zZXNz</auth>

SENT:<success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"></success>

6  BIND

RECV:<iq id="SfW08-0" type="set"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><resource>Spark 2.6.3</resource></bind></iq>

SENT:<iq type="result" id="SfW08-0" to="jacklin-pc/96508a6d"><bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"><jid>test001@jacklin-pc/Spark 2.6.3</jid></bind></iq>

client依照例如以下节奏发送报文。并进行操作就能够了。client改动代码。在这里不做描写叙述,请改动者自行尝试

openfire连接登陆优化方案的更多相关文章

  1. Tomcat 配置详解/优化方案

     转自:http://blog.csdn.net/cicada688/article/details/14451541 Service.xml Server.xml配置文件用于对整个容器进行相关的配置 ...

  2. tomcat配置详解/优化方案

    Service.xml Server.xml配置文件用于对整个容器进行相关的配置. <Server>元素:是整个配置文件的根元素.表示整个Catalina容器. 属性:className: ...

  3. (转)Web性能优化方案

    第一章 打开网站慢现状分析 在公司访问部署在IDC机房的VIP网站时会感觉很慢.是什么原因造成的?为了缩短页面的响应时间,改进我们的用户体验,我们需要知道用户的时间花在等待什么东西上. 可以跟踪一下我 ...

  4. Redmine性能优化方案

    近来公司redmine服务器表现很糟糕,在16核,64GRAM的机器上,压测结果竟然只有每秒5~7个请求,部分页面一个都出不来. 以下是我对Redmine性能优化方案: redmine服务器性能问题排 ...

  5. Android App罕见错误和优化方案

    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 1.App如果被定义一个有参数构造函数,那么需要再定义一个无参数的,如果不则会在某些情况下初始化失败 ...

  6. mysql大内存高性能优化方案

    mysql优化是一个相对来说比较重要的事情了,特别像对mysql读写比较多的网站就显得非常重要了,下面我们来介绍mysql大内存高性能优化方案 8G内存下MySQL的优化 按照下面的设置试试看:key ...

  7. Openfire服务器MySQL优化

    Openfire服务器MySQL优化: [root@iZ28g4ctd7tZ ~]# mysql -u root -p XXXXX mysql> show processlist; +----- ...

  8. Adapter优化方案的探索

    概要:使用Adapter的注意事项与优化方案本文的例子都可以在结尾处的示例代码连接中看到并下载,如果喜欢请star,如果觉得有纰漏请提交issue,如果你有更好的点子可以提交pull request. ...

  9. Web性能优化方案

    第一章 打开网站慢现状分析 在公司访问部署在IDC机房的VIP网站时会感觉很慢.是什么原因造成的?为了缩短页面的响应时间,改进我们的用户体验,我们需要知道用户的时间花在等待什么东西上. 可以跟踪一下我 ...

随机推荐

  1. z-index优先级总结

    因为显示器显示的图案是一个二维平面,拥有x轴和y轴来表示位置属性.为了表示三维立体的概念如显示元素的上下层的叠加顺序引入了z-index属性来表示z轴的区别,表示一个元素在叠加顺序上的上下立体关系. ...

  2. C++线性序列容器<vector>简单总结

    C++线性序列容器<vector>简单总结 vector是一个长度可变的数组,使用的时候无须声明上限,随着元素的增加,Vector的长度会自动增加:Vector类提供额外的方法来增加.删除 ...

  3. activiti总结

    1.activiti如何修改登录用户名?在哪个数据库里面添加. 2.activiti的启动和部署在http://activiti.org/userguide/index.html#demo.setup ...

  4. 获取EnterpriseLibrary企业库配置文件中ConnectionStrings(原创)

    在使用企业类库时想取出单独企业配置文件中的连接字符串遍历,并放到自己的数据库处理类中,在查找了很久的资料后都没有找到,于是自己探索着写了一个,共享给大家以做参考: ConfigurationSourc ...

  5. 设计模式:模版模式(Template Pattern)

    android中的Activity框架,View框架中大量的on函数基本上都应用到了Template模式,掌握这一模式对于理解这些框架大有裨益. 模版模式 又叫模板方法模式,在一个方法中定义一个算法的 ...

  6. 跟踪对象属性值的修改, 设置断点(Break on property change)

    代码 //Break on property change (function () { var localValue; Object.defineProperty(targetObject, 'pr ...

  7. C++拾遗(十三)友元和嵌套类

    友元类 使用友元的场合: 1.两个类既不是is-a关系也不是has-a关系,但是两个类之间又需要有联系,且一个类能访问另一个类的私有成员和保护成员. 2.一个类需要用到另外多个类的私有成员. C++p ...

  8. Vigenère Cipher 维吉尼亚加解密算法

    维吉尼亚的加解密有两种方法. 第一种是查表:第一行为明文,第一列为密钥,剩余的为对应的密文 第二种方法是转化计算法:逐个将字符转化为从零开始的数字,对数字进行加密/解密后,再转化为字符. 本文要用c+ ...

  9. 开发ContentProvider的步骤

    开发ContentProvider的步骤如下: 1.编写一个类,该类必须继承自ContentProvider. 2.实现ContentProvider类中的所有抽象方法:insert() delete ...

  10. php生成短网址的思路与方法

    生成短网址的思路以及使用php生成短网址的实现方法. 生成短网址的思路:如果把短网址还原了,你知道是个什么样子的吗?比如:http://www.jbxue.com/sitejs-17300-1.htm ...