即時通信(Instant Messaging)是目前Internet上最爲流行的通信方式,各類各樣的即時通信軟件也層出不窮,服務提供商也提供了愈來愈豐富的通信服務功能。html
即時通信有多種實現方式,例如:XMPP、環信、融雲等。
git
XMPP(XML Messages Presence Protocol(可擴展消息處理現場協議))是基於可擴展標記語言(XML)的協議,可用於即時通訊(IM)。github
XMPP協議是由JSF開源社區組織開發的,不屬於任何的機構和我的,從根本上保證了其開放性。
安全
互聯網工程工做組將XMPP於2002年正式列爲承認的實時通信及Presence技術。
服務器
XMPP 協議雖然是基於Client/Server 架構,可是XMPP協議自己並無這樣的限制。網絡的架構和電子郵件十分類似,但沒有結合任何特定的網絡架構,適用範圍很是普遍。網絡
基於XML 創建起來的應用具備良好的語義完整性和擴展性。
架構
XMPP 除了可用在即時通訊的應用程序,還能用在網絡管理、內容供稿、協同工具、檔案共享、遊戲、遠端系統監控等。dom
XMPP在Client-to-Server通訊和Server-to-Server通訊中都使用TLS (Transport Layer Security)協議做爲通訊通道的加密方法,保證通訊的安全。分佈式
XMPP環境的搭建的步驟詳見XMPP環境的搭建工具
將XMPP協議第三方文件拖入工程,下載地址:https://github.com/AlonerOwl/XMPPFramework
1> 界面搭建
界面採用Storyboard,效果圖以下:
2> 初始化XMPP通道
//初始化通道 self.xmppStream = [[XMPPStream alloc] init]; //openfire服務器IP地址 self.xmppStream.hostName = kHostName; //openfire服務器端口 默認5222 self.xmppStream.hostPort = kHostPort; //添加代理 [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue() ];
3> 鏈接服務器代碼所需方法
4> 登陸代碼所需方法
5> 註冊代碼所需方法
6> 所有代碼(方法的使用詳見代碼註釋)
XMPPManager.h
#import <Foundation/Foundation.h> #import "XMPPFramework.h" @interface XMPPManager : NSObject /// 聲明XMPP通道屬性 @property (nonatomic, strong) XMPPStream *xmppStream; + (XMPPManager *)shareXMPPManager; // 登陸方法 - (void)loginWithUsername:(NSString *)username password:(NSString *)password; // 註冊方法 - (void)registerWithUsername:(NSString *)username password:(NSString *)password; @end
XMPPManager.m
#import "XMPPManager.h" static XMPPManager *xmppManager = nil; // 枚舉記錄狀態 typedef NS_ENUM(NSUInteger, ConnectToServerPurpose) { ConnectToServerPurposeLogin, ConnectToServerPurposeRegister }; // 遵循協議 @interface XMPPManager () <XMPPStreamDelegate> /// 用戶名 @property (nonatomic, copy) NSString *username; /// 密碼 @property (nonatomic, copy) NSString *password; /// 記錄當前的狀態,判斷是登陸仍是註冊 @property (nonatomic) ConnectToServerPurpose connectToServer; @end @implementation XMPPManager + (XMPPManager *)shareXMPPManager { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ xmppManager = [XMPPManager new]; }); return xmppManager; } #pragma mark - 1.建立通道 - (instancetype)init { self = [super init]; if (self) { //初始化對象 self.xmppStream = [XMPPStream new]; // 設置服務器 [self settingUpServer]; } return self; } // 設置服務器 - (void)settingUpServer { // 設置opfire服務器 self.xmppStream.hostName = kHostName; // 設置服務器端口號 self.xmppStream.hostPort = kHostPort; // 添加代理 [self.xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; } #pragma mark - 登陸的方法 - (void)loginWithUsername:(NSString *)username password:(NSString *)password { self.username = username; self.password = password; // 記錄登陸狀態 self.connectToServer = ConnectToServerPurposeLogin; // 鏈接服務器 [self linkServer]; } #pragma mark - 註冊的方法 - (void)registerWithUsername:(NSString *)username password:(NSString *)password { self.username = username; self.password = password; // 記錄註冊狀態 self.connectToServer = ConnectToServerPurposeRegister; // 鏈接服務器 [self linkServer]; } #pragma mark - 鏈接服務器 - (void)linkServer { // 要鏈接服務器,要有用戶身份認證 // 身份證 // 參數1:用戶名 // 參數2:域名 // 參數3:資源 XMPPJID *jid = [XMPPJID jidWithUser:self.username domain:kDomin resource:kResource]; self.xmppStream.myJID = jid; // 若是當前聊天工具處於鏈接狀態或已經鏈接,此時,你須要去切斷鏈接【此邏輯不惟一】 if ([self.xmppStream isConnected] || [self.xmppStream isConnecting]) { // 斷開鏈接 [self disConnectToServer]; } // 設置鏈接超時 NSError *error = nil; [self.xmppStream connectWithTimeout:30 error:&error]; if (error) { NSLog(@"鏈接超時"); } } #pragma mark - 斷開鏈接(註銷) - (void)disConnectToServer { // 當前用戶不可用(下線了) XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"]; // 告訴通道用戶不可用(下線了) [self.xmppStream sendElement:presence]; // 使用通道失去鏈接 [self.xmppStream disconnect]; } #pragma mark - XMPPStreamDelegate協議方法 #pragma mark 鏈接超時 - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender { NSLog(@"鏈接超時"); } #pragma mark 鏈接成功 - (void)xmppStreamDidConnect:(XMPPStream *)sender { NSLog(@"鏈接成功"); // 認證密碼 // [self.xmppStream authenticateWithPassword:self.password error:nil]; // 區分登陸和註冊進行密碼驗證 switch (self.connectToServer) { case ConnectToServerPurposeLogin: // 登陸認證 [self.xmppStream authenticateWithPassword:self.password error:nil]; break; case ConnectToServerPurposeRegister: // 註冊的認證 [self.xmppStream registerWithPassword:self.password error:nil]; break; default: break; } } #pragma mark 斷開鏈接 - (void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error { // 1. 本身動手斷開 // 2. 被動斷開 if (error) { NSLog(@"斷開鏈接,error == %@", error); } } #pragma mark 認證失敗 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error { NSLog(@"認證失敗:error = %@", error); } #pragma mark 認證成功 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender { // 當前用戶可用(上線了) XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"]; // 告訴通道用戶可用(上線了) [self.xmppStream sendElement:presence]; NSLog(@"認證成功"); } @end
LoginViewController.m
#import "LoginViewController.h" #import "XMPPManager.h" @interface LoginViewController () /// 用戶輸入框 @property (weak, nonatomic) IBOutlet UITextField *usernameTextField; /// 密碼輸入框 @property (weak, nonatomic) IBOutlet UITextField *passwordTextField; @end @implementation LoginViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (IBAction)loginAction:(UIButton *)sender { [[XMPPManager shareXMPPManager] loginWithUsername:self.usernameTextField.text password:self.passwordTextField.text]; } @end
RegisterViewController.m
#import "RegisterViewController.h" #import "XMPPManager.h" @interface RegisterViewController () <XMPPStreamDelegate> @property (weak, nonatomic) IBOutlet UITextField *registerUNTextField; @property (weak, nonatomic) IBOutlet UITextField *registerPWTextField; @end @implementation RegisterViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // 添加代理 [[XMPPManager shareXMPPManager].xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } // 按鈕點擊事件 - (IBAction)registerAction:(id)sender { [[XMPPManager shareXMPPManager] registerWithUsername:self.registerUNTextField.text password:self.registerPWTextField.text]; } #pragma mark - XMPPStreamDelegate實現協議方法 // 註冊成功後,dismiss到登陸界面 - (void)xmppStreamDidRegister:(XMPPStream *)sender { NSLog(@"註冊成功"); [self dismissViewControllerAnimated:YES completion:nil]; } // 註冊失敗 - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error { NSLog(@"註冊失敗:%@", error); } @end
未完待續...