一、整体聊天功能实现的流程
1>与openfire服务器建立连接
2>获取连接对象,建立输入输出流
3>开启输入数据流,线程阻塞等待消息,发出消息
4>关闭输入输出流,关闭连接
二、解析连接服务器的类XmppConnection
首先 XmppConnection调用connect方法连接服务器,这里调用connectUsingConfiguration(config)方法,这里我们看到了私有变量config,这里应该初始化了系统的配置信息,这里在初始化的时候调用了mConfiguration = new ConnectionConfiguration(HOST, PORT);下面接着看connectUsingConfiguration方法;
public void connect() throws XMPPException {
// Establishes the connection, readers and writers
connectUsingConfiguration(config);
// Automatically makes the login if the user was previously connected successfully
// to the server and the connection was terminated abruptly
if (connected && wasAuthenticated) {
// Make the login
if (isAnonymous()) {
// Make the anonymous login
loginAnonymously();
}
else {
login(config.getUsername(), config.getPassword(), config.getResource());
}
notifyReconnection();
}
}
我们看到,这里获取的 this.socket存于XmppConnection中,每个XmppConnection对应的当前设备和服务器的链接,对应的一个socket对象,缓存于当前XmppConnection中,便于使用,这里新建socket连接后做了一个初始化连接的操作,即initConnection(),我们继续看这个操作
private void connectUsingConfiguration(ConnectionConfiguration config) throws XMPPException {
Iterator<HostAddress> it = config.getHostAddresses().iterator();
List<HostAddress> failedAddresses = new LinkedList<HostAddress>();
boolean xmppIOError = false;
while (it.hasNext()) {
exception = null;
HostAddress hostAddress = it.next();
String host = hostAddress.getFQDN();
int port = hostAddress.getPort();
try {
if (config.getSocketFactory() == null) {
this.socket = new Socket(host, port);
}
else {
this.socket = config.getSocketFactory().createSocket(host, port);
}
} catch (UnknownHostException uhe) {
String errorMessage = "Could not connect to " + host + ":" + port + ".";
exception = new XMPPException(errorMessage, new XMPPError(XMPPError.Condition.remote_server_timeout,
errorMessage), uhe);
} catch (IOException ioe) {
String errorMessage = "XMPPError connecting to " + host + ":" + port + ".";
exception = new XMPPException(errorMessage, new XMPPError(XMPPError.Condition.remote_server_error,
errorMessage), ioe);
xmppIOError = true;
}
//此处省略部分代码
socketClosed = false;
initConnection();
}
新建连接的socket后这里一开始便有一个initReaderAndWriter方法,这里初始化字符输入输出流, 这里创建了 packetWriter和packetReader ,用于向服务器发送和接收消息,当然下面对应的有程序异常后对于输入输出流和socket的关闭的操作,我们来看看packetWriter这个向服务器发送消息的类具体做了什么操作
private void initConnection() throws XMPPException {
boolean isFirstInitialization = packetReader == null || packetWriter == null;
compressionHandler = null;
serverAckdCompression = false;
initReaderAndWriter();
try {
if (isFirstInitialization) {
packetWriter = new PacketWriter(this);
packetReader = new PacketReader(this);
// If debugging is enabled, we should start the thread that will listen for
if (config.isDebuggerEnabled()) {
addPacketListener(debugger.getReaderListener(), null);
if (debugger.getWriterListener() != null) {
addPacketSendingListener(debugger.getWriterListener(), null);
}
}
}
else {
packetWriter.init();
packetReader.init();
}
packetWriter.startup();
packetReader.startup();
connected = true;
if (isFirstInitialization) {
// Notify listeners that a new connection has been established
for (ConnectionCreationListener listener : getConnectionCreationListeners()) {
listener.connectionCreated(this);
}
}
}
catch (XMPPException ex) {
// An exception occurred in setting up the connection. Make sure we shut down the
// readers and writers and close the socket.
if (packetWriter != null) {
try {
packetWriter.shutdown();
}
catch (Throwable ignore) { /* ignore */ }
packetWriter = null;
}
if (packetReader != null) {
try {
packetReader.shutdown();
}
catch (Throwable ignore) { /* ignore */ }
packetReader = null;
}
if (reader != null) {
try {
reader.close();
}
catch (Throwable ignore) { /* ignore */ }
reader = null;
}
if (writer != null) {
try {
writer.close();
}
catch (Throwable ignore) { /* ignore */}
writer = null;
}
if (socket != null) {
try {
socket.close();
}
catch (Exception e) { /* ignore */ }
socket = null;
}
this.setWasAuthenticated(authenticated);
chatManager = null;
authenticated = false;
connected = false;
throw ex; // Everything stoppped. Now throw the exception.
}
}
三、PacketWriter(发送消息)
这里新建了一个消息队列,用于存放用户将要发送的消息,存放于这个消息列队中,然后初始化获取用户将要发送消息的接收过程init(),这里开启了一个线程writerThread调用writePackets方法,这个writerThread线程的开启在PacketWriter的startUp方法中,下面我们看下writePackets做了什么操作
protected PacketWriter(XMPPConnection connection) {
this.queue = new ArrayBlockingQueue<Packet>(500, true);
this.connection = connection;
init();
}
protected void init() {
this.writer = connection.writer;
done = false;
writerThread = new Thread() {
public void run() {
writePackets(this);
}
};
writerThread.setName("Smack Packet Writer (" + connection.connectionCounterValue + ")");
writerThread.setDaemon(true);
}
这里一开便调用了openStream,这里添加字符串去打开xmpp协议指定的输入输出流,以便于后面能发送数据,然后这里有一个while循环,循环的状态基本为阻塞状态,nextPacket去用户那里获取即将要发送的消息我们等会再看,如果拿到的消息不为空,则 writer.write(packet.toXML());直接传输到服务器,中间有一个 queue.clear();表示消息发送完成后清楚消息队列 writer.write(“”)结束消息的标志;完成后关闭输入输出流,接下来我们看下nextPacket方法具体的操作
void openStream() throws IOException {
StringBuilder stream = new StringBuilder();
stream.append("<stream:stream");
stream.append(" to=\"").append(connection.getServiceName()).append("\"");
stream.append(" xmlns=\"jabber:client\"");
stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\"");
stream.append(" version=\"1.0\">");
writer.write(stream.toString());
writer.flush();
}
private void writePackets(Thread thisThread) {
try {
// Open the stream.
openStream();
// Write out packets from the queue.
while (!done && (writerThread == thisThread)) {
Packet packet = nextPacket();
if (packet != null) {
writer.write(packet.toXML());
if (queue.isEmpty()) {
writer.flush();
}
}
}
// Flush out the rest of the queue. If the queue is extremely large, it's possible
// we won't have time to entirely flush it before the socket is forced closed
// by the shutdown process.
try {
while (!queue.isEmpty()) {
Packet packet = queue.remove();
writer.write(packet.toXML());
}
writer.flush();
}
catch (Exception e) {
e.printStackTrace();
}
// Delete the queue contents (hopefully nothing is left).
queue.clear();
// Close the stream.
try {
writer.write("</stream:stream>");
writer.flush();
}
catch (Exception e) {
// Do nothing
}
finally {
try {
writer.close();
}
catch (Exception e) {
// Do nothing
}
}
}
catch (IOException ioe) {
// The exception can be ignored if the the connection is 'done'
// or if the it was caused because the socket got closed
if (!(done || connection.isSocketClosed())) {
done = true;
// packetReader could be set to null by an concurrent disconnect() call.
// Therefore Prevent NPE exceptions by checking packetReader.
if (connection.packetReader != null) {
connection.notifyConnectionError(ioe);
}
}
}
}
这里有一个循环,从消息队列中获取用户即将发送的消息,如果有,则从消息队列中取出来,没有则synchronized 同步中调用 queue.wait(),让当前的writerThread处于等待状态,等待用户将要发送的消息存入消息队列,这里我们看完了packetWriter,接下来我们看下packetReader
private Packet nextPacket() {
Packet packet = null;
// Wait until there's a packet or we're done.
while (!done && (packet = queue.poll()) == null) {
try {
synchronized (queue) {
queue.wait();
}
}
catch (InterruptedException ie) {
// Do nothing
}
}
return packet;
}
三、PacketReader(接收消息)
同样这边的PacketReader构造方法中调用了初始化方法init,我们来看看,这里的主要两个方法parsePackets和resetParser,parsePackets则是为接收到的消息做解析的准备,暂时先放下,这里同样也是开启了一个线程 readerThread,resetParser则是初始化XML解析器和输入输出流parser.setInput(connection.reader),为我们parsePackets解析接收到的数据做铺垫,下面我们看下parsePackets方法,由于比较多,这里显示部分代码
protected void init() {
done = false;
connectionID = null;
readerThread = new Thread() {
public void run() {
parsePackets(this);
}
};
readerThread.setName("Smack Packet Reader (" + connection.connectionCounterValue + ")");
readerThread.setDaemon(true);
// Create an executor to deliver incoming packets to listeners. We'll use a single
// thread with an unbounded queue.
listenerExecutor = Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(runnable,
"Smack Listener Processor (" + connection.connectionCounterValue + ")");
thread.setDaemon(true);
return thread;
}
});
resetParser();
}
private void resetParser() {
try {
parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(connection.reader);
}
catch (XmlPullParserException xppe) {
xppe.printStackTrace();
}
}
这里采用的pull解析xml数据,由于把这里的xml数据主要有三个节点,message、iq、presence,分别代表的是消息、请求响应、出席,我们先来看message节点的处理,这里调用的是PacketParserUtils.parseMessage,里面同样也是XmlPullParser解析器解析数据,拿到数据后将数据封装为Message对象后然后返回,获取id、to、from、type等数据,然后我们看看processPacket(packet)方法,具体做了哪些操作
public static Packet parseMessage(XmlPullParser parser) throws Exception {
Message message = new Message();
String id = parser.getAttributeValue("", "id");
message.setPacketID(id == null ? Packet.ID_NOT_AVAILABLE : id);
message.setTo(parser.getAttributeValue("", "to"));
message.setFrom(parser.getAttributeValue("", "from"));
message.setType(Message.Type.fromString(parser.getAttributeValue("", "type")));
String language = getLanguageAttribute(parser);
return message;
}
private void parsePackets(Thread thread) {
try {
int eventType = parser.getEventType();
do {
if (eventType == XmlPullParser.START_TAG) {
int parserDepth = parser.getDepth();
ParsingExceptionCallback callback = connection.getParsingExceptionCallback();
if (parser.getName().equals("message")) {
Packet packet;
try {
packet = PacketParserUtils.parseMessage(parser);
} catch (Exception e) {
String content = PacketParserUtils.parseContentDepth(parser, parserDepth);
UnparsablePacket message = new UnparsablePacket(content, e);
if (callback != null) {
callback.handleUnparsablePacket(message);
}
continue;
}
processPacket(packet);
}
else if (parser.getName().equals("iq")) {
IQ iq;
try {
iq = PacketParserUtils.parseIQ(parser, connection);
} catch (Exception e) {
String content = PacketParserUtils.parseContentDepth(parser, parserDepth);
UnparsablePacket message = new UnparsablePacket(content, e);
if (callback != null) {
callback.handleUnparsablePacket(message);
}
continue;
}
processPacket(iq);
}
else if (parser.getName().equals("presence")) {
Presence presence;
try {
presence = PacketParserUtils.parsePresence(parser);
} catch (Exception e) {
String content = PacketParserUtils.parseContentDepth(parser, parserDepth);
UnparsablePacket message = new UnparsablePacket(content, e);
if (callback != null) {
callback.handleUnparsablePacket(message);
}
continue;
}
processPacket(presence);
}
}
这里出现listenerExecutor,存储了监听的消息池,然后调用了ListenerNotification通知了所有的消息监听器接收消息,并且自己去判断消息的类型的来源,而 collector.processPacket(packet)则是将当前接收到的消息存储到消息队列中,发送和接收的消息处于同一消息队列
private void processPacket(Packet packet) {
if (packet == null) {
return;
}
// Loop through all collectors and notify the appropriate ones.
for (PacketCollector collector: connection.getPacketCollectors()) {
collector.processPacket(packet);
}
// Deliver the incoming packet to listeners.
listenerExecutor.submit(new ListenerNotification(packet));
}
private class ListenerNotification implements Runnable {
private Packet packet;
public ListenerNotification(Packet packet) {
this.packet = packet;
}
public void run() {
for (ListenerWrapper listenerWrapper : connection.recvListeners.values()) {
listenerWrapper.notifyListener(packet);
}
}
}
protected void processPacket(Packet packet) {
if (packet == null) {
return;
}
if (packetFilter == null || packetFilter.accept(packet)) {
while (!resultQueue.offer(packet)) {
// Since we know the queue is full, this poll should never actually block.
resultQueue.poll();
}
}
}