openfire即时聊天软件搭建

  • 将下载好的xampp-osx根据提示进行安装并打开。
     
    F4648516-D77F-4DF4-9031-F7BED0F8C13F.png
  •  

     

    点击Manage Servers,启动所有服务,如果MySQL Database无法启动,可打开终端执行如下命令: sudo /Applications/XAMPP/xamppfiles/bin/mysql.server start
     
    1B424F11-55A2-4F2C-B188-89059822D4CD.png
  •  

     

    点击Go To Application,配置数据库
     
    A
  •  

     

  •  

    数据库新建成功后,需要导入数据库格式。可使用下载好的openfire_mysql.sq文件,也可用另外途径:前往/usr/local,找到文件夹openfire,但此时的openfire文件夹无法打开。选中后右键查看简介,将下面属性修改为读与写。这时可以打开openfire文件夹,查找路径/usr/local/openfire/resources/database下,找到openfire_mysql.sq文件,将其拷贝到桌面,然后打开数据库配置页面。
     
    C51E4113-4627-4076-A8AB-0C5A4A092378.png

Openfire服务器搭建

可以使用它轻易的构建高效率的技师通讯服务器,Openfire的安装和使用也是非常的简单,并利用Web进行后台管理。单台服务器可支持上万并发用户。由于是采用开放的XMPP协议,我们可以使用各种支持XMPP协议的IM客户端软件登陆服务(在这里我就使用了Spark)。

  • 下载Openfire,并根据提示进行安装。
  •  

     

    安装完成后,打开系统设置。
     
    2337F0EA-21E2-4864-8322-976E3D084138.png
     
    3C705089-8D87-44E8-BDCA-AB87D0F18484.png
  •  

     

    点击Open Admin Console,到网站中配置Openfire。
     
    7FDF4D15-E4E8-4AA9-AC5C-2D448EACB2AB.png
     
     
     
    6477BDE5-63A9-483A-A333-AFED5A64C13C.png
     
    D31A34D9-945C-40C6-BF02-0AE5ACCBC123.png
     
    833FB7BA-32F5-403C-9F78-B21389322536.png
     
    0602DDA6-5F09-4254-82CF-9E8E3404D753.png
     
    7B7802D1-FFB2-4D77-A5ED-6462D71E1577.png
  • 到这一步,基本上openfire就已经配置完毕了。

安装Spark客户端

  • 按照提示安装客户端,完成后打开


     
    E24D0A1D-BC30-4901-8845-D84B40F25EBA.png
  •  

     

    高级配置
     
    5D1E4204-7E6A-47EB-9219-65DFDA664C7A.png
  • 然后以管理员身份登录,登录成功后刷新openfire管理界面,可以看到头像变亮了,说明环境配置成功。接下来就可以撸码了。

代码实现

  • 主要实现这些功能:
    • 注册、登录、退出登录;
    • 添加好友、好友请求
    • 发送消息、接收消息
    • 消息记录
  • 新建一个管理类XMPPManager,创建以下对象:
import UIKit
import XMPPFramework

// 枚举:连接服务器的目的
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?
  // 花名册数据存储对象
  var xmppRosterCoreDataStorage : XMPPRosterCoreDataStorage?
  // 信息归档对象
  var xmppMessageArchiving : XMPPMessageArchiving?
  // 信息存储对象
  var xmppMessageArchivingCoreDataStorage : XMPPMessageArchivingCoreDataStorage?
  
  var friendsListResultController : NSFetchedResultsController<NSFetchRequestResult>?
  var chatRecordsResultController : NSFetchedResultsController<NSFetchRequestResult>?
  // 好友请求
  var xmppPresence : XMPPPresence?
  
  // 单例
  static let manager : XMPPManager = {
      let manager = XMPPManager.init()
      // 创建通信通道对象
      manager.xmppStream = XMPPStream.init()
      // 设置服务器IP地址
      manager.xmppStream?.hostName = kHostName
      // 设置服务器端口
      manager.xmppStream?.hostPort = kHostPort
      // 添加代理
      manager.xmppStream?.addDelegate(manager, delegateQueue: DispatchQueue.main)
      
      // 花名册数据存储对象
      manager.xmppRosterCoreDataStorage = XMPPRosterCoreDataStorage.sharedInstance()
      manager.xmppRoster = XMPPRoster.init(rosterStorage: manager.xmppRosterCoreDataStorage)
      manager.xmppRoster?.activate(manager.xmppStream)
      manager.xmppRoster?.addDelegate(manager, delegateQueue: DispatchQueue.main)
      
      // 信息存储对象
      manager.xmppMessageArchivingCoreDataStorage = XMPPMessageArchivingCoreDataStorage.sharedInstance()
      manager.xmppMessageArchiving = XMPPMessageArchiving.init(messageArchivingStorage: manager.xmppMessageArchivingCoreDataStorage, dispatchQueue: DispatchQueue.main)
      // 激活通信通道对象
      manager.xmppMessageArchiving?.activate(manager.xmppStream)

      return manager
  }()
  
  // 连接服务器
  func connectToServer(withUserName userName: String) {
      // 创建XMPPJID对象
      self.xmppJID = XMPPJID.init(user: userName, domain: kDomin, resource: kResource)
      // 设置通信通道对象的JID
      self.xmppStream?.myJID = self.xmppJID
      // 发送请求
      if self.xmppStream?.isConnected() == true || self.xmppStream?.isConnecting() == true {
          // 先退出登录状态
         self.exitLogin()
      }
      // 连接服务器
      do {
          try self.xmppStream?.connect(withTimeout: -1)
      }catch let error as NSError{
          print("连接服务器失败: " + error.description)
      }
  }
}

// MARK:- 登录方法
extension XMPPManager {
  // 登录方法
  func login(widthUserName userName: String?, andPassword password: String?) {
      guard let userName = userName, let password = password else {
          print("用户名和密码不能为空")
          return
      }
      // 记录连接服务器的目的是登录
      connectServerPurpose = .connectServerToLogin
      // 记录登录信息
      self.password = password
      self.userName = userName
      connectToServer(withUserName: userName)
  }
  
  // 退出登录
  func exitLogin() {
      // 先发送下线状态
      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
      }
      // 记录连接服务器的目的是注册
      connectServerPurpose = .connectServerToRegister
      // 记录密码
      self.password = password
      connectToServer(withUserName: userName)
  }
}

// MARK:- XMPPStreamDelegate
extension XMPPManager : XMPPStreamDelegate {
  // 连接成功
  func xmppStreamDidConnect(_ sender: XMPPStream!) {
      print("连接成功")
      switch connectServerPurpose {
          case .connectServerToLogin:
              // 验证密码
              do {
                  try self.xmppStream?.authenticate(withPassword: self.password)
              } catch let error as NSError {
                  print("登录时验证密码失败: " + error.description)
              }
              break
          case .connectServerToRegister:
              do {
                  try self.xmppStream?.register(withPassword: self.password)
              } catch let error as NSError {
                  print("注册时验证密码失败: " + error.description)
              }
              break
      }
  }
  
  // 连接超时
  func xmppStreamConnectDidTimeout(_ sender: XMPPStream!) {
      print("连接超时")
  }
  
  // 登录成功
  func xmppStreamDidAuthenticate(_ sender: XMPPStream!) {
      print("登录成功  ", #line, #function)
      // 发送上线状态
      let presence = XMPPPresence.init(type: "available")
      XMPPManager.manager.xmppStream?.send(presence)
  }
  
  // 已经断开连接
  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
  }
  
  // 获取与某个好友的聊天记录
  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 {
          // 好友列表数据库发生变化
          NotificationCenter.default.post(name: NSNotification.Name(rawValue: notificationName_friendsListDidChange), object: nil)
      }else if anObject is XMPPMessageArchiving_Message_CoreDataObject{
          // 聊天记录数据库发生变化
          NotificationCenter.default.post(name: NSNotification.Name(rawValue: notificationName_chatRecordsDidChange), object: nil)
      }
  }
}



XMPP: Openfire + Spark 实现即时通讯 - 简书 (jianshu.com)  

 

 

 

 

 

 

https://cimoc.cn/2021/11/18/rocketchat-install/

ocketChat —— 自建聊天服务器

 

posted @ 2021-06-26 12:36  woaibaobei  阅读(213)  评论(0编辑  收藏  举报