資源下載: https://pan.baidu.com/s/1ge3cajh 密碼: 6ytk
安裝XMPP集成環(huán)境
-
將下載好的xampp-osx根據(jù)提示進(jìn)行安裝并打開。
-
點(diǎn)擊Manage Servers,啟動(dòng)所有服務(wù)邻吭,如果MySQL Database無法啟動(dòng)吕粗,可打開終端執(zhí)行如下命令: sudo /Applications/XAMPP/xamppfiles/bin/mysql.server start
-
點(diǎn)擊Go To Application,配置數(shù)據(jù)庫
-
數(shù)據(jù)庫新建成功后垢乙,需要導(dǎo)入數(shù)據(jù)庫格式⊙雕桑可使用下載好的openfire_mysql.sq文件袄简,也可用另外途徑:前往/usr/local武福,找到文件夾openfire,但此時(shí)的openfire文件夾無法打開痘番。選中后右鍵查看簡介,將下面屬性修改為讀與寫平痰。這時(shí)可以打開openfire文件夾汞舱,查找路徑/usr/local/openfire/resources/database下,找到openfire_mysql.sq文件宗雇,將其拷貝到桌面昂芜,然后打開數(shù)據(jù)庫配置頁面。
Openfire服務(wù)器搭建
可以使用它輕易的構(gòu)建高效率的技師通訊服務(wù)器赔蒲,Openfire的安裝和使用也是非常的簡單泌神,并利用Web進(jìn)行后臺(tái)管理良漱。單臺(tái)服務(wù)器可支持上萬并發(fā)用戶。由于是采用開放的XMPP協(xié)議欢际,我們可以使用各種支持XMPP協(xié)議的IM客戶端軟件登陸服務(wù)(在這里我就使用了Spark)母市。
- 下載Openfire,并根據(jù)提示進(jìn)行安裝损趋。
-
安裝完成后患久,打開系統(tǒng)設(shè)置。
-
點(diǎn)擊Open Admin Console,到網(wǎng)站中配置Openfire浑槽。
- 到這一步,基本上openfire就已經(jīng)配置完畢了。
安裝Spark客戶端
-
按照提示安裝客戶端泛源,完成后打開
-
高級(jí)配置
- 然后以管理員身份登錄冲秽,登錄成功后刷新openfire管理界面,可以看到頭像變亮了镊靴,說明環(huán)境配置成功铣卡。接下來就可以擼碼了。
代碼實(shí)現(xiàn)
- 主要實(shí)現(xiàn)這些功能:
- 注冊邑闲、登錄算行、退出登錄;
- 添加好友苫耸、好友請求
- 發(fā)送消息州邢、接收消息
- 消息記錄
- 新建一個(gè)管理類XMPPManager,創(chuàng)建以下對象:
import UIKit
import XMPPFramework
// 枚舉:連接服務(wù)器的目的
enum ConnectServerPurpose : Int{
case connectServerToLogin // 登錄
case connectServerToRegister // 注冊
}
class XMPPManager: NSObject {
deinit {
NotificationCenter.default.removeObserver(self)
}
fileprivate var password : String?
fileprivate var userName : String?
fileprivate var connectServerPurpose : ConnectServerPurpose = .connectServerToLogin
// 通信通道對象
var xmppStream : XMPPStream?
// JID
var xmppJID : XMPPJID?
// 好友花名冊管理對象
var xmppRoster : XMPPRoster?
// 花名冊數(shù)據(jù)存儲(chǔ)對象
var xmppRosterCoreDataStorage : XMPPRosterCoreDataStorage?
// 信息歸檔對象
var xmppMessageArchiving : XMPPMessageArchiving?
// 信息存儲(chǔ)對象
var xmppMessageArchivingCoreDataStorage : XMPPMessageArchivingCoreDataStorage?
var friendsListResultController : NSFetchedResultsController<NSFetchRequestResult>?
var chatRecordsResultController : NSFetchedResultsController<NSFetchRequestResult>?
// 好友請求
var xmppPresence : XMPPPresence?
// 單例
static let manager : XMPPManager = {
let manager = XMPPManager.init()
// 創(chuàng)建通信通道對象
manager.xmppStream = XMPPStream.init()
// 設(shè)置服務(wù)器IP地址
manager.xmppStream?.hostName = kHostName
// 設(shè)置服務(wù)器端口
manager.xmppStream?.hostPort = kHostPort
// 添加代理
manager.xmppStream?.addDelegate(manager, delegateQueue: DispatchQueue.main)
// 花名冊數(shù)據(jù)存儲(chǔ)對象
manager.xmppRosterCoreDataStorage = XMPPRosterCoreDataStorage.sharedInstance()
manager.xmppRoster = XMPPRoster.init(rosterStorage: manager.xmppRosterCoreDataStorage)
manager.xmppRoster?.activate(manager.xmppStream)
manager.xmppRoster?.addDelegate(manager, delegateQueue: DispatchQueue.main)
// 信息存儲(chǔ)對象
manager.xmppMessageArchivingCoreDataStorage = XMPPMessageArchivingCoreDataStorage.sharedInstance()
manager.xmppMessageArchiving = XMPPMessageArchiving.init(messageArchivingStorage: manager.xmppMessageArchivingCoreDataStorage, dispatchQueue: DispatchQueue.main)
// 激活通信通道對象
manager.xmppMessageArchiving?.activate(manager.xmppStream)
return manager
}()
// 連接服務(wù)器
func connectToServer(withUserName userName: String) {
// 創(chuàng)建XMPPJID對象
self.xmppJID = XMPPJID.init(user: userName, domain: kDomin, resource: kResource)
// 設(shè)置通信通道對象的JID
self.xmppStream?.myJID = self.xmppJID
// 發(fā)送請求
if self.xmppStream?.isConnected() == true || self.xmppStream?.isConnecting() == true {
// 先退出登錄狀態(tài)
self.exitLogin()
}
// 連接服務(wù)器
do {
try self.xmppStream?.connect(withTimeout: -1)
}catch let error as NSError{
print("連接服務(wù)器失敗: " + error.description)
}
}
}
// MARK:- 登錄方法
extension XMPPManager {
// 登錄方法
func login(widthUserName userName: String?, andPassword password: String?) {
guard let userName = userName, let password = password else {
print("用戶名和密碼不能為空")
return
}
// 記錄連接服務(wù)器的目的是登錄
connectServerPurpose = .connectServerToLogin
// 記錄登錄信息
self.password = password
self.userName = userName
connectToServer(withUserName: userName)
}
// 退出登錄
func exitLogin() {
// 先發(fā)送下線狀態(tài)
let presence = XMPPPresence.init(type: "unavailable")
self.xmppStream?.send(presence)
// 斷開連接
self.xmppStream?.disconnect()
}
}
// MARK:- 注冊方法
extension XMPPManager {
func register(widthUserName userName: String?, andPassword password: String?) {
guard let userName = userName, let password = password else {
print("用戶名和密碼不能為空")
return
}
// 記錄連接服務(wù)器的目的是注冊
connectServerPurpose = .connectServerToRegister
// 記錄密碼
self.password = password
connectToServer(withUserName: userName)
}
}
// MARK:- XMPPStreamDelegate
extension XMPPManager : XMPPStreamDelegate {
// 連接成功
func xmppStreamDidConnect(_ sender: XMPPStream!) {
print("連接成功")
switch connectServerPurpose {
case .connectServerToLogin:
// 驗(yàn)證密碼
do {
try self.xmppStream?.authenticate(withPassword: self.password)
} catch let error as NSError {
print("登錄時(shí)驗(yàn)證密碼失斖首印: " + error.description)
}
break
case .connectServerToRegister:
do {
try self.xmppStream?.register(withPassword: self.password)
} catch let error as NSError {
print("注冊時(shí)驗(yàn)證密碼失斄刻省: " + error.description)
}
break
}
}
// 連接超時(shí)
func xmppStreamConnectDidTimeout(_ sender: XMPPStream!) {
print("連接超時(shí)")
}
// 登錄成功
func xmppStreamDidAuthenticate(_ sender: XMPPStream!) {
print("登錄成功 ", #line, #function)
// 發(fā)送上線狀態(tài)
let presence = XMPPPresence.init(type: "available")
XMPPManager.manager.xmppStream?.send(presence)
}
// 已經(jīng)斷開連接
func xmppStreamDidDisconnect(_ sender: XMPPStream!, withError error: Error!) {
print("++++++++++")
}
}
// MARK:- 好友
extension XMPPManager {
// 添加好友
func addNewFriends(userName: String?) {
guard let name = userName else { return }
let friendJID = XMPPJID.init(string: "\(name)@\(kDomin)")
self.xmppRoster?.subscribePresence(toUser: friendJID)
}
// 獲取好友列表
func getFriendsList() -> Array<Any>? {
guard let context = self.xmppRosterCoreDataStorage?.mainThreadManagedObjectContext else {
return nil
}
let request = NSFetchRequest<NSFetchRequestResult>.init(entityName: "XMPPUserCoreDataStorageObject")
let userInfo = String.init(format: "%@@%@", self.userName ?? "", kDomin)
// 謂詞
let predicate = NSPredicate.init(format: "streamBareJidStr = %@", userInfo)
request.predicate = predicate
// 排序
let sort = NSSortDescriptor.init(key: "displayName", ascending: true)
request.sortDescriptors = [sort]
friendsListResultController = NSFetchedResultsController.init(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
friendsListResultController?.delegate = self
do {
try friendsListResultController?.performFetch()
} catch let error as NSError {
print("獲取好友聊天記錄失敗: " + error.description)
}
return friendsListResultController?.fetchedObjects
}
// 獲取與某個(gè)好友的聊天記錄
func getChatRecords(withFriendName friendName: String) ->Array<Any>?{
guard let context = self.xmppMessageArchivingCoreDataStorage?.mainThreadManagedObjectContext else { return nil}
let request = NSFetchRequest<NSFetchRequestResult>.init(entityName: "XMPPMessageArchiving_Message_CoreDataObject")
let userInfo = String.init(format: "%@@%@", self.userName ?? "", kDomin)
let friendsInfo = String.init(format: "%@@%@", friendName, kDomin)
// 謂詞
let predicate = NSPredicate.init(format: "streamBareJidStr=%@ AND bareJidStr=%@", userInfo, friendsInfo)
request.predicate = predicate
// 排序
let sort = NSSortDescriptor.init(key: "timestamp", ascending: true)
request.sortDescriptors = [sort]
chatRecordsResultController = NSFetchedResultsController.init(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
chatRecordsResultController?.delegate = self
do {
try chatRecordsResultController?.performFetch()
} catch let error as NSError {
print("獲取好友聊天記錄失斚油省: " + error.description)
}
guard let fetchedObjects = chatRecordsResultController?.fetchedObjects else { return nil }
var messageArray = Array<Any>()
for obj in fetchedObjects {
if let _ = (obj as? XMPPMessageArchiving_Message_CoreDataObject)?.body {
messageArray.append(obj)
}
}
return messageArray
}
}
// MARK:- XMPPRosterDelegate
extension XMPPManager : XMPPRosterDelegate {
func xmppRoster(_ sender: XMPPRoster!,
didReceiveRosterItem item: DDXMLElement!) {
print(#line, #function)
}
func xmppRosterDidBeginPopulating(_ sender: XMPPRoster!,
withVersion version: String!) {
print(#line, #function)
}
func xmppRosterDidEndPopulating(_ sender: XMPPRoster!) {
print(#line, #function)
}
// 收到好友請求
func xmppRoster(_ sender: XMPPRoster!,
didReceivePresenceSubscriptionRequest presence: XMPPPresence!) {
self.xmppPresence = presence
// 彈框
let alert = UIAlertView.init(title: "好友請求", message: "\(presence.from().user)請求添加你為好友", delegate: self, cancelButtonTitle:
"拒絕", otherButtonTitles: "同意")
alert.show()
}
}
// MARK:- 好友請求彈框
extension XMPPManager : UIAlertViewDelegate {
func alertView(_ alertView: UIAlertView,
clickedButtonAt buttonIndex: Int) {
switch buttonIndex {
case 0:
// 拒絕
let jid = XMPPJID.init(string: self.xmppPresence?.from().user)
self.xmppRoster?.rejectPresenceSubscriptionRequest(from: jid)
break
case 1:
// 同意
let jid = XMPPJID.init(string: self.xmppPresence?.from().user)
self.xmppRoster?.acceptPresenceSubscriptionRequest(from: jid, andAddToRoster: true)
break
default: break
}
}
}
// MARK:- NSFetchedResultsControllerDelegate
extension XMPPManager : NSFetchedResultsControllerDelegate {
func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>,
didChange anObject: Any,
at indexPath: IndexPath?,
for type: NSFetchedResultsChangeType,
newIndexPath: IndexPath?) {
if anObject is XMPPUserCoreDataStorageObject {
// 好友列表數(shù)據(jù)庫發(fā)生變化
NotificationCenter.default.post(name: NSNotification.Name(rawValue: notificationName_friendsListDidChange), object: nil)
}else if anObject is XMPPMessageArchiving_Message_CoreDataObject{
// 聊天記錄數(shù)據(jù)庫發(fā)生變化
NotificationCenter.default.post(name: NSNotification.Name(rawValue: notificationName_chatRecordsDidChange), object: nil)
}
}
}