Smack 开源库学习总结(二)连接服务器
结合开源代码,深入学习几个重点函数的具体实现功能。
本篇文章主要学习 XMPPTCPConnection中函数conncect()的具体实现的功能。
XMPPTCPConnection connection = new XMPPTCPConnection(config);
connection .connect();
XMPPConnection.java是一个接口,里面定义了获取服务器name、host、及port等等方法。
AbstractXMPPConnection.java抽象类实现了XMPPConnection.java这个接口,XMPPTCPConnection.java和XMPPBOSHConnection.java是继承AbstractXMPPConnection.java,以实现不同的功能。
connect()在AbstractXMPPConnection.java中实现,其只是连接服务器的总入口而已。
public synchronized AbstractXMPPConnection connect() throws SmackException, IOException, XMPPException, InterruptedException {
// Check if not already connected
throwAlreadyConnectedExceptionIfAppropriate();
// Reset the connection state
initState();
saslAuthentication.init();
streamId = null;
try {
// Perform the actual connection to the XMPP service
connectInternal();
// If TLS is required but the server doesn't offer it, disconnect
// from the server and throw an error. First check if we've already negotiated TLS
// and are secure, however (features get parsed a second time after TLS is established).
if (!isSecureConnection() && getConfiguration().getSecurityMode() == SecurityMode.required) {
throw new SecurityRequiredByClientException();
}
} catch (SmackException | IOException | XMPPException | InterruptedException e) {
instantShutdown();
throw e;
}
// Make note of the fact that we're now connected.
connected = true;
callConnectionConnectedListener();
return this;
}
实际连接服务器的动作是由抽象函数connectInternal()处理,在XMPPTCPConnection中实现了connectInternal()具体动作,该函数主要实现
(1) 函数connectUsingConfiguration() 实现 XMPP Server建立Socket连接;
(2) 函数initConnection() 实现初始化数据写入和读取的操作流程。
protected void connectInternal() throws SmackException, IOException, XMPPException, InterruptedException {
closingStreamReceived.init();
// Establishes the TCP connection to the server and does setup the reader and writer. Throws an exception if
// there is an error establishing the connection
connectUsingConfiguration();
// We connected successfully to the servers TCP port
initConnection();
// TLS handled will be successful either if TLS was established, or if it was not mandatory.
tlsHandled.checkIfSuccessOrWaitOrThrow();
// Wait with SASL auth until the SASL mechanisms have been received
saslFeatureReceived.checkIfSuccessOrWaitOrThrow();
}
在initConnection()中初始化数据流读写线程:packetWriter.init() 和 packetReader.init();
private void initConnection() throws IOException, InterruptedException {
compressionHandler = null;
// Set the reader and writer instance variables
initReaderAndWriter();
int availableReaderWriterSemaphorePermits = readerWriterSemaphore.availablePermits();
if (availableReaderWriterSemaphorePermits < 2) {
Object[] logObjects = new Object[] {
this,
availableReaderWriterSemaphorePermits,
};
LOGGER.log(Level.FINE, "Not every reader/writer threads where terminated on connection re-initializtion of {0}. Available permits {1}", logObjects);
}
readerWriterSemaphore.acquire(2);
// Start the writer thread. This will open an XMPP stream to the server
packetWriter.init();
// Start the reader thread. The startup() method will block until we
// get an opening stream packet back from server
packetReader.init();
}
在数据流写入初始化函数packetWriter.init()中,开启线程 writePackets(),并将数据流保存在packetReader.parser 中;
在数据读取初始化函数packetReader.init()中,开启线程parsePackets()对packetReader.parser中的数据进行解析处理;
private void parsePackets() {
try {
initialOpenStreamSend.checkIfSuccessOrWait();
int eventType = parser.getEventType();
while (!done) {
switch (eventType) {
case XmlPullParser.START_TAG:
final String name = parser.getName();
switch (name) {
case Message.ELEMENT:
case IQ.IQ_ELEMENT:
case Presence.ELEMENT:
try {
parseAndProcessStanza(parser);
} finally {
clientHandledStanzasCount = SMUtils.incrementHeight(clientHandledStanzasCount);
}
break;
case "stream":
// We found an opening stream.
if ("jabber:client".equals(parser.getNamespace(null))) {
streamId = parser.getAttributeValue("", "id");
String reportedServerDomain = parser.getAttributeValue("", "from");
assert (config.getXMPPServiceDomain().equals(reportedServerDomain));
}
break;
case "error":
StreamError streamError = PacketParserUtils.parseStreamError(parser);
saslFeatureReceived.reportFailure(new StreamErrorException(streamError));
// Mark the tlsHandled sync point as success, we will use the saslFeatureReceived sync
// point to report the error, which is checked immediately after tlsHandled in
// connectInternal().
tlsHandled.reportSuccess();
throw new StreamErrorException(streamError);
case "features":
parseFeatures(parser);
break;
.......(下面代码省略)
}
}
}
}
}
连接成功后,回调connected(),表明已经和服务器连接成功。