/** * Create a new session and connect to jabber server host denoted by * <code>route</code> or <code>to</code>. * * @param to * domain of the server to connect to. * @param route * optional hostname of the server to connect to (might be null). * * @throws UnknownHostException * @throws IOException */ public Session(String to, String route) throws UnknownHostException, IOException { this.to = to; int port = DEFAULT_XMPPPORT; this.sock = new Socket(); this.setLastActive(); try { this.db = DocumentBuilderFactory.newInstance().newDocumentBuilder(); } catch (Exception e) { } // first, try connecting throught the 'route' attribute. if (route != null && !route.equals("")) { JHBServlet.dbg( "Trying to use 'route' attribute to open a socket...", 3); if (route.startsWith("xmpp:")) { route = route.substring("xmpp:".length()); } int i; // has 'route' the optional port? if ((i = route.lastIndexOf(":")) != -1) { try { int p = Integer.parseInt(route.substring(i + 1)); if (p >= 0 && p <= 65535) { port = p; JHBServlet.dbg( "...route attribute holds a valid port (" + port + ").", 3); } } catch (NumberFormatException nfe) { } route = route.substring(0, i); } JHBServlet.dbg("Trying to open a socket to '" + route + "', using port " + port + ".", 3); try { this.sock.connect(new InetSocketAddress(route, port), SOCKET_TIMEOUT); } catch (Exception e) { JHBServlet.dbg( "Failed to open a socket using the 'route' attribute", 3); /* * none of the exceptions possible should be reason to throw * anything. If the socket isn't opened, we'll get a retry using * the 'to' attribute anyway. */ } } // If no socket has been opened, try connecting trough the 'to' // attribute. if (this.sock == null || !this.sock.isConnected()) { JHBServlet.dbg("Trying to use 'to' attribute to open a socket...", 3); host = DNSUtil.resolveXMPPServerDomain(to, DEFAULT_XMPPPORT); try { JHBServlet.dbg("Trying to open a socket to '" + host.getHost() + "', using port " + host.getPort() + ".", 3); this.sock.connect(new InetSocketAddress(host.getHost(), host .getPort()), SOCKET_TIMEOUT); } catch (UnknownHostException uhe) { JHBServlet.dbg( "Failed to open a socket using the 'to' attribute", 3); throw uhe; } catch (IOException ioe) { JHBServlet.dbg( "Failed to open a socket using the 'to' attribute", 3); throw ioe; } } // at this point, we either have a socket, or an exception has already // been thrown. try { if (this.sock.isConnected()) JHBServlet.dbg("Succesfully connected to " + to, 2); this.sock.setSoTimeout(SOCKET_TIMEOUT); // instantiate <stream> this.osw = new OutputStreamWriter(this.sock.getOutputStream(), "UTF-8"); this.osw.write("<stream:stream to='" + this.to + "'" + " xmlns='jabber:client' " + " xmlns:stream='http://etherx.jabber.org/streams'" + " version='1.0'" + ">"); this.osw.flush(); // create unique session id while (sessions.get(this.sid = createSessionID(24)) != null) ; JHBServlet.dbg("creating session with id " + this.sid, 2); // register session sessions.put(this.sid, this); // create list of responses responses = new TreeMap(); this.br = new BufferedReader(new InputStreamReader(this.sock .getInputStream(), "UTF-8")); this.streamPattern = Pattern.compile( ".*<stream:stream[^>]*id=['|\"]([^'|^\"]+)['|\"][^>]*>.*", Pattern.DOTALL); this.stream10Pattern = Pattern .compile( ".*<stream:stream[^>]*id=['|\"]([^'|^\"]+)['|\"][^>]*>.*(<stream.*)$", Pattern.DOTALL); this.stream10Test = Pattern.compile( ".*<stream:stream[^>]*version=['|\"]1.0['|\"][^>]*>.*", Pattern.DOTALL); this.setStatus(SESS_ACTIVE); } catch (IOException ioe) { throw ioe; } }
整个构造方法的作用是创建一个新的 session 并且连接到 jabber 服务器上去。 Create a new session and connect to jabber server host denoted by <code> route </code> or <code> to </code> .
这是 Session 类的构造方法,带两个 String 类型的参数 to 和 route ,分别都表示与之建立会话的 Server ,而 route 是备用可选的: optional hostname of the server to connect to (might be null) 。
this . sock = new Socket();
this .setLastActive();
创建一个 Socket 对象,并且在此时 setLastActive ,因为他认为创建 Socket 对象算是 Active 动作吧。
// first, try connecting throught the 'route' attribute. if (route != null && !route.equals("")) { JHBServlet.dbg( "Trying to use 'route' attribute to open a socket...", 3); if (route.startsWith("xmpp:")) { route = route.substring("xmpp:".length()); } int i; // has 'route' the optional port? if ((i = route.lastIndexOf(":")) != -1) { try { int p = Integer.parseInt(route.substring(i + 1)); if (p >= 0 && p <= 65535) { port = p; JHBServlet.dbg( "...route attribute holds a valid port (" + port + ").", 3); } } catch (NumberFormatException nfe) { } route = route.substring(0, i); } JHBServlet.dbg("Trying to open a socket to '" + route + "', using port " + port + ".", 3); try { this.sock.connect(new InetSocketAddress(route, port), SOCKET_TIMEOUT); } catch (Exception e) { JHBServlet.dbg( "Failed to open a socket using the 'route' attribute", 3); /* * none of the exceptions possible should be reason to throw * anything. If the socket isn't opened, we'll get a retry using * the 'to' attribute anyway. */ } }
接下来,试着用 route 来连接,结合下面的程序可以看出 route 的构成是这样的 xmpp:domain:port ,所以最终 socket 连接也是这样连的:
this.sock.connect(new InetSocketAddress(route, port), SOCKET_TIMEOUT); // If no socket has been opened, try connecting trough the 'to' // attribute. if (this.sock == null || !this.sock.isConnected()) { JHBServlet.dbg("Trying to use 'to' attribute to open a socket...", 3); host = DNSUtil.resolveXMPPServerDomain(to, DEFAULT_XMPPPORT); try { JHBServlet.dbg("Trying to open a socket to '" + host.getHost() + "', using port " + host.getPort() + ".", 3); this.sock.connect(new InetSocketAddress(host.getHost(), host .getPort()), SOCKET_TIMEOUT); } catch (UnknownHostException uhe) { JHBServlet.dbg( "Failed to open a socket using the 'to' attribute", 3); throw uhe; } catch (IOException ioe) { JHBServlet.dbg( "Failed to open a socket using the 'to' attribute", 3); throw ioe; } }
接下来用 to 来连接,而对 to 的解析采用了封装的 resolveXMPPServerDomain 方法,得到的 DNSUtil.HostAddress 对象
// at this point, we either have a socket, or an exception // has already been thrown.
到此我们或者有了 socket 或者是一个已经抛出的 exception
if (this.sock.isConnected()) JHBServlet.dbg("Succesfully connected to " + to, 2); this.sock.setSoTimeout(SOCKET_TIMEOUT); // instantiate <stream> this.osw = new OutputStreamWriter(this.sock.getOutputStream(), "UTF-8"); this.osw.write("<stream:stream to='" + this.to + "'" + " xmlns='jabber:client' " + " xmlns:stream='http://etherx.jabber.org/streams'" + " version='1.0'" + ">"); this.osw.flush();
首先向 OutputStreamWriter 流中写数据。
// create unique session id while (sessions.get(this.sid = createSessionID(24)) != null);
接着创建一个独特的 session id ,这里的 sessions 是一个 HashTable ,如果对应 key 所得不为 null ,即表重复,循取。
// register session sessions.put(this.sid, this);
取得 unique sid 之后,记得存入。接下去是一些 Pattern 对象的建立,最后:
this.setStatus(SESS_ACTIVE);
表明到此处 session 是处于活动状态的。 Sid 型如: terminating session WOfs8pYb5UEpttRISjjS1Uxc