基于XMPP实现即时通讯(二、登陆、上下线、接收消息)

关于XMPP 本地环境搭建、登录、好友列表,好友状态、发送接收多媒体消息、增删好友、群聊等功能点,大家可以跟我一起来学习,最后章节会将整个完整Demo放到我的github。
因为很早就开始用swift来做项目,所以本文以swift进行示例。Xcode版本为7.3.1。

一、了解XMPP常用对象

XMPPStream:xmpp基础服务类
XMPPRoster:好友列表类
XMPPRosterCoreDataStorage:好友列表(用户账号)在core data中的操作类
XMPPvCardCoreDataStorage:好友名片(昵称,签名,性别,年龄等信息)在core data中的操作类
XMPPvCardTemp:好友名片实体类,从数据库里取出来的都是它
xmppvCardAvatarModule:好友头像
XMPPReconnect:如果失去链接,自动重连
XMPPRoom:提供多用户聊天支持
XMPPPubSub:发布订阅

二、引入头文件

根据基于XMPP实现即时通讯(一、环境搭建)的集成方式,cocapods引入的XMPP为OC版本,所以要在工程的桥接文件中引入基础服务类:#import “XMPP.h"

三、登陆

登陆之前我们得先在openfire上建个用户(也可以直接用管理员帐号),然后再进行以下步骤:初始化核心类、建立socket链接、链接成功后通过密码进行身份校验、更新上线状态。

1. 新建用户

用管理员帐号登录openfire,在用户/组中新建用户。(实际业务流程中,此建立过程也是后台服务器实现,只是需要客户端传递相关注册信息)

2. 初始化基础类

可以新建一个XMPPManager类,以下方法均在此类实现。(用来管理所有XMPP核心业务服务):

1
2
3
4
5
func xmppInit()
{
_xmppStream = XMPPStream()
_xmppStream?.addDelegate(self, delegateQueue: dispatch_get_main_queue())
}

3. 建立socket链接

3.1 先在文件头申明常量:
因为是在个人MAC搭建Openfire服务器调试,所以此hostname为个人MAC共享访问地址,在mac的系统偏好设置->共享里可以查看到完整地址,在实际调试过程中,真机登录必须用此host,但聊天可能为localhost,也可能为此host,模拟器也存在同样的情况,当消息发送不成功时,可以尝试修改这些host

1
let vHostName = "JamieiMac.local"

3.2 建立socket链接:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
func xmppConnect(userId: String, password: String) {

let userName = "\(userId)@\(vHostName)"
let jid = XMPPJID.jidWithString(userName)
_xmppStream.myJID = jid
_xmppStream.hostName = vHostName
_pwd = password

do {
try _xmppStream.connectWithTimeout(10)
} catch let error {
DPrintln("发送链接请求失败 \(error),请检查网络或服务器配置")
}
}

// MARK: -------------------------------- 链接回调 -------------------------------
func xmppStream(sender: XMPPStream!, socketDidConnect socket: GCDAsyncSocket!) {
DPrintln("建立socket链接")
}

func xmppStreamDidConnect(sender: XMPPStream!) {
DPrintln("链接成功")

//进行身份校验
self.authenticateUser()
}

func xmppStreamConnectDidTimeout(sender: XMPPStream!){
DPrintln("链接超时")

Tools.shared.showAlertViewAndDismissDefault(nil, message: "链接超时,请重试")
}

3.3 校验身份

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func authenticateUser() {

if _xmppStream.isAuthenticated(){
return
}
do {
try _xmppStream.authenticateWithPassword(_pwd)
} catch let error {
Tools.dissmissLoadingInWindow()
DPrintln("发送验证请求失败 \(error)")
_xmppStream.disconnect()
}
}

// MARK: -------------------------------- 身份验证回调 -------------------------------
func xmppStreamDidAuthenticate(sender: XMPPStream!){
DPrintln("身份验证成功")
// Tools.shared.showAlertViewAndDismissDefault(nil, message: "登录成功!")
self.onLine()
Tools.dissmissLoadingInWindow()
NSNotificationCenter.defaultCenter().postNotificationName("loginSuccess", object: nil, userInfo: nil)

}

func xmppStream(sender: XMPPStream!, didNotAuthenticate error: DDXMLElement!) {

DPrintln("身份验证失败")
Tools.dissmissLoadingInWindow()

NSNotificationCenter.defaultCenter().postNotificationName("loginError", object: nil, userInfo: nil)
_xmppStream.disconnect()

}

四、上下线

用户默认状态有:

1
2
3
4
available 上线
away 离开
do not disturb 忙碌
unavailable 下线

默认身份验证成功后,用户还是灰色(下线状态)的,因此通常登陆成功后直接发送上线状态。上线成功后在openfire的用户摘要中可看到在线状态变绿。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func onLine(){
DPrintln("发送上线状态")

let presence = XMPPPresence(type: "available")
_xmppStream.sendElement(presence)
}

//用户主动下线(同时断开链接)
func outLine(){
DPrintln("发送下线状态")

let presence = XMPPPresence(type: "unavailable")
_xmppStream.sendElement(presence)

//断开链接
_xmppStream.disconnect()
}

五、接收信息

  1. 当接收到< message /> 标签时,XMPP会回调didReceiveMessage的方法。根据 XMPP 协议,消息体的内容存储在标签 < body /> 内

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
      func xmppStream(sender: XMPPStream!, didReceiveMessage message: XMPPMessage!) {
    DPrintln("收到消息 \(message)")

    //stringValue默认解析body内字符
    let messageString = message.stringValue()
    if !NSString.isNilOrEmpty(messageString){
    let messageBody = message.elementForName("body").stringValue
    let fromeUser = (message.attributeForName("from")).stringValue
    UIView.showAlertView(fromeUser(), andMessage: messageBody())
    } else{
    DPrintln("收到其它类型消息/非正常消息")
    }
    }
  2. 测试接收消息: 登录及上线成功后,在openfire控制界面中,打开“会话”-》“工具”,即可向所有用户发送消息,赶紧验证下你的代码吧!


作者 @代码书生
2016 年 06月 16日

尘满面,鬓如霜,Bug多多岂不白忙?重敏捷,保质量,Case重重亦可远航。^.^