消息的发送与接收
消息类的抽象
一个XMPP的基本Message消息形式如下:
<message from='aa@gmail.com/handpay-pc1ECC80A8' to='bb@jabber.org' type='chat' id='purpled65d5457'> <body>hello</body> </message>
从上面的例子中可以看到message
报文的主要成员有from
属性、to
属性、
type
属性、id
属性和子元素body
。对于这些特征可以抽象出Message
类:
在完成了对Message
类的定义以后,就可以着手实现接收与发送消息的逻辑。
发送消息
发送消息的实现逻辑非常简单,XMPPConnection
的write()
方法可以直接把文本写入
输入流:
abstract class XMPPConnection(override val serviceName: String, override val port: Int, override val proxyInfo: ProxyInfo) extends Connection(serviceName: String, port: Int, proxyInfo: ProxyInfo) with MessageProcesser with AuthInfo with Logging { /* ... */ def write(str: String) { if (null != str) ioStream.packetWriter ! str } /* ... */
用户在使用本工具库发送消息时,可以通过直接调用Message
实例的toXML
方法生成要
发出的报文:
val elem = new Message("jadexmppIBIDM-0", "aa@gmail.com", "bb@gmail.com", "hello") conn.write(elem.toXML.toString)
消息类的需要属性id
表示唯一的标识,type
标识消息是普通消息还是群聊:
override def addAttributeToXML(node: Elem): Elem = { super.addAttributeToXML(node) % Attribute(None, "id", newTextAttr(id), Attribute(None, "type", newTextAttr("chat"), Null)) }
<message>
标签的内部就是消息子元素:
def nodeXML(childElementXML: NodeBuffer): Elem = <message>{ childElementXML }</message>
而消息子元素为<active>
标签加上<body>
标签,消息的内容就在<body>
标签内:
override def childElementXML: NodeBuffer = { val nb = super.childElementXML nb += <active xmlns="http://jabber.org/protocol/chatstates"/> nb += <body>{msgStr}</body> nb }
接收消息
为了处理收到的消息,需要定制专门的处理器,在这里定义了MessageHandler
类。
由于本项目是作为一个工具库交给用户使用的,所以不可能事先知道用户在他们的项目
中是如何使用收到的XMPP消息的(是作为聊天工具显示在屏幕上还是合为远程控制执行
命令)。
为了方便地在MessangeHandler
中调用最终用户定义的逻辑,可以声明一个名为
MsgRecvHandler
的接口,它包含一个公有的方法onMessageReceive(msg)
。用户可以
通过实现这个方法来写入自己的业务逻辑。MessageHandler
内部有有一个
MsgRecvHandler
类型的列表,如图\ref{fig:ch08.MsgRecvHandler.png}。
MessageHandler
类的process()
方法会从收到的XML文档中提取出from
、to
、id
、body
等信息,构建出Message
实例作为参数。然后逐一调用每个MsgRecevhandler
成员的onMessageReceive()
方法,触发所有用户的业务逻辑。处理过程如图
\ref{fig:ch08.msghdl.png}。