1、各Openfire服务器得设置不同的域名,即 ofProperty表xmpp.domain属性,如设置 192.168.1.46等。
2、Dialback提供一种弱身份验证的方式,要使用这种方式可以将 ofproperty表中“xmpp.server.tls.enabled” 设置为false,并将“xmpp.server.dialback.enabled”设置为true。
3、建立到对方的路由:
LocalOutgoingServerSession类 authenticateDomain方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 OutgoingServerSession session;
3 ......
4 session = createOutgoingSession(domain, hostname, port);
5 if (session != null) {
6 // Add the validated domain as an authenticated domain
7 session.addAuthenticatedDomain(domain);
8 // Add the new hostname to the list of names that the server may have
9 session.addHostname(hostname);
10 // Notify the SessionManager that a new session has been created
11 sessionManager.outgoingServerSessionCreated((LocalOutgoingServerSession) session);
12 return true;
13 }
14 ......
15 }
3.1 Socket探测对方5269端口是否开放。
LocalOutgoingServerSession类 createOutgoingSession方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 int port) {
3 boolean useTLS = JiveGlobals.getBooleanProperty("xmpp.server.tls.enabled", true);
4 RemoteServerConfiguration configuration = RemoteServerManager.getConfiguration(hostname);
5 if (configuration != null) {
6 // TODO Use the specific TLS configuration for this remote server
7 //useTLS = configuration.isTLSEnabled();
8 }
9
10 // Connect to remote server using XMPP 1.0 (TLS + SASL EXTERNAL or TLS + server dialback or server dialback)
11 SocketConnection connection = null;
12 String realHostname = null;
13 int realPort = port;
14 Socket socket = new Socket();
15 try {
16 // Get the real hostname to connect to using DNS lookup of the specified hostname
17 DNSUtil.HostAddress address = DNSUtil.resolveXMPPServerDomain(hostname, port);
18 realHostname = address.getHost();
19 realPort = address.getPort();
20 Log.debug("LocalOutgoingServerSession: OS - Trying to connect to " + hostname + ":" + port +
21 "(DNS lookup: " + realHostname + ":" + realPort + ")");
22 // Establish a TCP connection to the Receiving Server
23 socket.connect(new InetSocketAddress(realHostname, realPort),
24 RemoteServerManager.getSocketTimeout());
25 Log.debug("LocalOutgoingServerSession: OS - Plain connection to " + hostname + ":" + port + " successful");
26 }
27 catch (Exception e) {
28 Log.error("Error trying to connect to remote server: " + hostname +
29 "(DNS lookup: " + realHostname + ":" + realPort + ")", e);
30 return null;
31 }
3.2 尝试server dialback(XMPP 1.0),建立信任通道。
3.2.1 LocalOutgoingServerSession类 createOutgoingSession方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 else if (ServerDialback.isEnabled() && features.element("dialback") != null) {
3 Log.debug("LocalOutgoingServerSession: OS - About to try connecting using server dialback XMPP 1.0 with: " + hostname);
4 ServerDialback method = new ServerDialback(connection, domain);
5 OutgoingServerSocketReader newSocketReader = new OutgoingServerSocketReader(reader);
6 if (method.authenticateDomain(newSocketReader, domain, hostname, id)) {
7 Log.debug("LocalOutgoingServerSession: OS - SERVER DIALBACK XMPP 1.0 with " + hostname + " was successful");
8 StreamID streamID = new BasicStreamIDFactory().createStreamID(id);
9 LocalOutgoingServerSession session = new LocalOutgoingServerSession(domain, connection, newSocketReader, streamID);
10 connection.init(session);
11 // Set the hostname as the address of the session
12 session.setAddress(new JID(null, hostname, null));
13 return session;
14 }
15 else {
16 Log.debug("LocalOutgoingServerSession: OS - Error, SERVER DIALBACK with " + hostname + " failed");
17 }
18 }
19
3.2.2 ServerDialback类 authenticateDomain方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 String hostname, String id) {
3 String key = AuthFactory.createDigest(id, getSecretkey());
4 Log.debug("ServerDialback: OS - Sent dialback key to host: " + hostname + " id: " + id + " from domain: " +
5 domain);
6
7 synchronized (socketReader) {
8 // Send a dialback key to the Receiving Server
9 StringBuilder sb = new StringBuilder();
10 sb.append("<db:result");
11 sb.append(" from=\"").append(domain).append("\"");
12 sb.append(" to=\"").append(hostname).append("\">");
13 sb.append(key);
14 sb.append("</db:result>");
15 connection.deliverRawText(sb.toString());
16
17 // Process the answer from the Receiving Server
18 try {
19 Element doc = socketReader.getElement(RemoteServerManager.getSocketTimeout(),
20 TimeUnit.MILLISECONDS);
21 if (doc == null) {
22 Log.debug("ServerDialback: OS - Time out waiting for answer in validation from: " + hostname +
23 " id: " +
24 id +
25 " for domain: " +
26 domain);
27 return false;
28 }
29 else if ("db".equals(doc.getNamespacePrefix()) && "result".equals(doc.getName())) {
30 boolean success = "valid".equals(doc.attributeValue("type"));
31 Log.debug("ServerDialback: OS - Validation " + (success ? "GRANTED" : "FAILED") + " from: " +
32 hostname +
33 " id: " +
34 id +
35 " for domain: " +
36 domain);
37 return success;
38 }
39 else {
40 Log.debug("ServerDialback: OS - Unexpected answer in validation from: " + hostname + " id: " +
41 id +
42 " for domain: " +
43 domain +
44 " answer:" +
45 doc.asXML());
46 return false;
47 }
48 }
49 catch (InterruptedException e) {
50 Log.debug("ServerDialback: OS - Validation FAILED from: " + hostname +
51 " id: " +
52 id +
53 " for domain: " +
54 domain, e);
55 return false;
56 }
57 }
58 }
4、加入路由表的serversCache缓存。如 session.addHostname(hostname);
LocalOutgoingServerSession类 addHostname方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 synchronized (hostnames) {
3 hostnames.add(hostname);
4 }
5 // Add a new route for this new session
6 XMPPServer.getInstance().getRoutingTable().addServerRoute(new JID(null, hostname, null, true), this);
7 }
RoutingTableImpl类 addServerRoute(JID route, LocalOutgoingServerSession destination)方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 String address = route.getDomain();
3 localRoutingTable.addRoute(address, destination);
4 Lock lock = CacheFactory.getLock(address, serversCache);
5 try {
6 lock.lock();
7 serversCache.put(address, server.getNodeID().toByteArray());
8 }
9 finally {
10 lock.unlock();
11 }
12 }
5、包再次路由,发往对方。
5.1 OutgoingSessionPromise类 sendPacket(Packet packet)方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 // Create a connection to the remote server from the domain where the packet has been sent
3 boolean created;
4 // Make sure that only one cluster node is creating the outgoing connection
5 // TODO: Evaluate why removing the oss part causes nasty s2s and lockup issues.
6 Lock lock = CacheFactory.getLock(domain+"oss", serversCache);
7 try {
8 lock.lock();
9 created = LocalOutgoingServerSession
10 .authenticateDomain(packet.getFrom().getDomain(), packet.getTo().getDomain());
11 } finally {
12 lock.unlock();
13 }
14 if (created) {
15 if (!routingTable.hasServerRoute(packet.getTo())) {
16 throw new Exception("Route created but not found!!!");
17 }
18 // A connection to the remote server was created so get the route and send the packet
19 routingTable.routePacket(packet.getTo(), packet, false);
20 }
21 else {
22 throw new Exception("Failed to create connection to remote server");
23 }
24 }
5.2 RoutingTableImpl类 routePacket(JID jid, Packet packet, boolean fromServer)方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 byte[] nodeID = serversCache.get(jid.getDomain());
3 if (nodeID != null) {
4 if (server.getNodeID().equals(nodeID)) {
5 // This is a route to a remote server connected from this node
6 try {
7 localRoutingTable.getRoute(jid.getDomain()).process(packet);
8 routed = true;
9 } catch (UnauthorizedException e) {
10 Log.error(e);
11 }
12 }
13 else {
14 // This is a route to a remote server connected from other node
15 if (remotePacketRouter != null) {
16 routed = remotePacketRouter.routePacket(nodeID, jid, packet);
17 }
18 }
19 }
20 else {
21 // Return a promise of a remote session. This object will queue packets pending
22 // to be sent to remote servers
23 OutgoingSessionPromise.getInstance().process(packet);
24 routed = true;
25 }
5.3 通过SocketConnection类的deliver(Packet packet)发出:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 if (isClosed()) {
3 backupDeliverer.deliver(packet);
4 }
5 else {
6 boolean errorDelivering = false;
7 boolean allowedToWrite = false;
8 try {
9 requestWriting();
10 allowedToWrite = true;
11 xmlSerializer.write(packet.getElement());
12 if (flashClient) {
13 writer.write('\0');
14 }
15 xmlSerializer.flush();
16 }
17 catch (Exception e) {
18 Log.debug("Error delivering packet" + "\n" + this.toString(), e);
19 errorDelivering = true;
20 }
21 finally {
22 if (allowedToWrite) {
23 releaseWriting();
24 }
25 }
26 if (errorDelivering) {
27 close();
28 // Retry sending the packet again. Most probably if the packet is a
29 // Message it will be stored offline
30 backupDeliverer.deliver(packet);
31 }
32 else {
33 session.incrementServerPacketCount();
34 }
35 }
36 }
6、对方Openfire服务器在ServerSocketReader类的 packetReceived(Packet packet) 接收,并响应回包。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
2 if (packet.getTo() == null || packet.getFrom() == null) {
3 Log.debug("Closing IncomingServerSession due to packet with no TO or FROM: " +
4 packet.toXML());
5 // Send a stream error saying that the packet includes no TO or FROM
6 StreamError error = new StreamError(StreamError.Condition.improper_addressing);
7 connection.deliverRawText(error.toXML());
8 // Close the underlying connection
9 connection.close();
10 open = false;
11 throw new PacketRejectedException("Packet with no TO or FROM attributes");
12 }
13 else if (!((LocalIncomingServerSession) session).isValidDomain(packet.getFrom().getDomain())) {
14 Log.debug("Closing IncomingServerSession due to packet with invalid domain: " +
15 packet.toXML());
16 // Send a stream error saying that the packet includes an invalid FROM
17 StreamError error = new StreamError(StreamError.Condition.invalid_from);
18 connection.deliverRawText(error.toXML());
19 // Close the underlying connection
20 connection.close();
21 open = false;
22 throw new PacketRejectedException("Packet with no TO or FROM attributes");
23 }
24 }