XMPP 基於XML的點對點通訊協議數組
IM: 即時通訊服務器
XMPP: 基於XML的點對點通訊協議app
1. ejabber: 這個軟件是用來開啓本地服務器使用, 本地服務器地址爲: 127.0.0.1, 全部的電腦都同樣, 開啓服務器的軟件有不少框架
2. Audium: 就是一個聊天工具ide
用局域網聊天工具
1.在同一個WIFI下佈局
2.在同一個本地服務器建立帳號atom
3.登錄的時候, 設置好服務器地址, 爲電腦的IP地址spa
XAMPP: 集成了阿帕奇 FTP和My SQL的一個軟件3d
類的擴展有三種方式
1.協議: 須要本身制定協議, 設置代理, 遵照協議, 通常用來傳值(回調)
2.延展: 爲類添加私有方法和私有變量, 通常用於已知類(.h和.m都存在的類)
3.類目: 爲類添加方法和屬性(只有setter和getter, 沒有實例變量), 必須實現內部聲明的方法, 通常用於系統類的擴展
XMPP的安裝
XMPP安裝須要的軟件
安裝ejabberd
選擇ejabberd.app進行安裝
用戶名和密碼填寫完要記住,以後會用到
安裝成功後,在Finder->應用程序->ejabberd-14.07->bin->start點擊start.exec運行,會跳出一個網頁
在網頁上能夠添加帳號和密碼
將 軟件添加到Finder->應用程序中,點擊打開app,能夠用ejabberd中註冊的帳號和密碼登錄
詳細代碼:
在工程中添加框架和MI類
storyboard基本佈局:
ViewController.m #import "ViewController.h" //引入頭文件 #import "XMPPFramework.h" #import "XMPPManager.h" #import "ContactViewController.h" #define JID(USER_NAME) [NSString stringWithFormat:@"%@@bogon", USER_NAME] @interface ViewController ()<XMPPStreamDelegate, UIAlertViewDelegate, XMPPRosterDelegate> @property (weak, nonatomic) IBOutlet UITextField *userNameTF; @property (weak, nonatomic) IBOutlet UITextField *passwordTF; //花名冊 @property (nonatomic, strong) XMPPRoster *roster; //花名冊數組 @property (nonatomic, strong) NSMutableArray *rosterArr; - (IBAction)loginClick:(id)sender; //建立XMPPStream類的屬性 @property (nonatomic, strong) XMPPStream *stream; @end @implementation ViewController //點擊頁面代理方法 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event { //結束編輯狀態 [self.view endEditing:YES]; } //重寫XMPPStream的getter方法 - (XMPPStream *)stream { if (_stream == nil) { self.stream = [[XMPPStream alloc] init]; _stream.hostName = @"127.0.0.1"; _stream.hostPort = 5222; [_stream addDelegate:self delegateQueue:dispatch_get_main_queue()]; //激活花名冊 [self.roster activate:_stream]; } return _stream; } //重寫XMPPRoster花名冊方法 - (XMPPRoster *)roster { if (!_roster) { self.roster = [[XMPPRoster alloc] initWithRosterStorage:[XMPPRosterCoreDataStorage sharedInstance] dispatchQueue:dispatch_get_main_queue()]; //配置參數 [_roster addDelegate:self delegateQueue:dispatch_get_main_queue()]; } return _roster; } - (void)viewDidLoad { [super viewDidLoad]; //記錄stream [XMPPManager sharedManager].stream = self.stream; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } //登錄按鈕 - (IBAction)loginClick:(id)sender { //登錄 //1.斷開鏈接 [self.stream disconnect]; //2.設置JID:設置字符串宏 self.stream.myJID = [XMPPJID jidWithString: JID(self.userNameTF.text)]; //3.鏈接服務器 [self.stream connectWithTimeout:-1 error:nil]; } #pragma mark-XMPPStreamDelegate //鏈接服務器成功 - (void)xmppStreamDidConnect:(XMPPStream *)sender { //鏈接服務器成功之後登錄 [self.stream authenticateWithPassword:self.passwordTF.text error:nil]; } //鏈接服務器超時 - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender { NSLog(@"鏈接超時"); } //登錄成功 - (void)xmppStreamDidAuthenticate:(XMPPStream *)sender { //發送登錄成功狀態 XMPPPresence *presence = [XMPPPresence presence]; [self.stream sendElement:presence]; //記錄當前登錄的JID [[NSUserDefaults standardUserDefaults] setObject:[NSString stringWithFormat:@"%@@bogon", self.userNameTF.text] forKey:@"MyJID"]; [[[UIAlertView alloc] initWithTitle:@"登錄成功" message:@"" delegate:self cancelButtonTitle:@"好的" otherButtonTitles: nil] show]; } //登陸失敗 - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error { [[[UIAlertView alloc] initWithTitle:@"登錄失敗" message:@"" delegate:self cancelButtonTitle:@"好的" otherButtonTitles: nil] show]; } //接收到消息的代理方法 - (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message { //若是body的長度爲空 if (message.body.length == 0) { } else { NSLog(@"message: %@", message); } } #pragma mark - UIAlertViewDelegate - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { NSLog(@"%ld", buttonIndex); if (buttonIndex == 0) { //觸發sugue [self performSegueWithIdentifier:@"modelToContact" sender:nil]; } else { //清空密碼輸入框 self.passwordTF.text = @""; } } #pragma mark-XMPPRosterDelegate - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender { NSLog(@"開始獲取花名冊"); self.rosterArr = [@[] mutableCopy]; } - (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item { NSLog(@"獲取花名冊中"); [self.rosterArr addObject:item]; } - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender { NSLog(@"結束獲取"); NSLog(@"rosterArr: %@", self.rosterArr); } //屬性傳值 - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"modelToContact"]) { ContactViewController *contact = segue.destinationViewController; contact.temArr = self.rosterArr; } } @end
ContactViewController.m #import "ContactViewController.h" #import "DDXMLElement.h" #import "ChatViewController.h" @interface ContactViewController ()<UITableViewDataSource, UITableViewDelegate> - (IBAction)back:(id)sender; @property (weak, nonatomic) IBOutlet UITableView *tableView; @end @implementation ContactViewController - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark-UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.temArr.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; DDXMLElement *friendItem = self.temArr[indexPath.row]; //找到 JID 字符串 NSString *jidString = [[friendItem attributeForName:@"jid"] stringValue]; cell.textLabel.text = jidString; return cell; } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self performSegueWithIdentifier:@"modelToChat" sender:indexPath]; } #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([segue.identifier isEqualToString:@"modelToChat"]) { ChatViewController *chatVC = segue.destinationViewController; NSIndexPath *index = (NSIndexPath *)sender; chatVC.friendJID = [[self.temArr[index.row] attributeForName:@"jid"] stringValue]; } } - (IBAction)back:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } @end
ContactViewController.h #import <UIKit/UIKit.h> @interface ContactViewController : UIViewController @property (nonatomic, strong) NSMutableArray *temArr; @end
ChatViewController.h #import <UIKit/UIKit.h> @interface ChatViewController : UIViewController @property (nonatomic, copy)NSString *friendJID; @end
ChatViewController.m #import "ChatViewController.h" #import "XMPPFramework.h" #import "XMPPManager.h" @interface ChatViewController ()<UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate, XMPPStreamDelegate> - (IBAction)back:(id)sender; @property (weak, nonatomic) IBOutlet UITableView *tableView; @property (weak, nonatomic) IBOutlet UITextField *textField; @end @implementation ChatViewController - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } #pragma mark-UITableViewDataSource - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return 10; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell" forIndexPath:indexPath]; cell.textLabel.text = @"123"; return cell; } #pragma mark-XMPPStreamDelegate - (void)xmppStreamDidConnect:(XMPPStream *)sender { NSLog(@"鏈接服務器成功"); } - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender { NSLog(@"鏈接服務器超時"); } - (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message { NSLog(@"發送成功"); } - (void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error { NSLog(@"消息發送失敗%@", error); } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ - (IBAction)back:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark-UITextFieldDelegate //點擊return按鈕事件 - (BOOL)textFieldShouldReturn:(UITextField *)textField { //建立消息體 XMPPMessage *message = [XMPPMessage messageWithType:@"text" to:[XMPPJID jidWithString:self.friendJID]]; //添加消息內容 [message addBody:textField.text]; //發送消息 [[XMPPManager sharedManager].stream sendElement:message]; //收回鍵盤 [textField resignFirstResponder]; return YES; } @end
XMPPManager.h #import <Foundation/Foundation.h> @interface XMPPManager : NSObject + (XMPPManager *)sharedManager; @property (nonatomic, strong) id stream; @end
XMPPManager.m #import "XMPPManager.h" @implementation XMPPManager + (XMPPManager *)sharedManager { static XMPPManager *manager = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ manager = [self new]; }); return manager; } @end
NSString+JID.h #import <Foundation/Foundation.h> #import <UIKit/UIKit.h> @interface UITextField (JID) @property (nonatomic, copy) NSString *jid; @end
NSString+JID.m #import "NSString+JID.h" @implementation UITextField (JID) - (void)setJid:(UITextField *)jid { } - (NSString *)jid { return [NSString stringWithFormat:@"%@@bogon", self.text]; } @end
RegisterViewController.m #import "RegisterViewController.h" //引入 XMPP 框架的頭文件 #import "XMPPFramework.h" @interface RegisterViewController ()<XMPPStreamDelegate> @property (weak, nonatomic) IBOutlet UITextField *userNameTF; @property (weak, nonatomic) IBOutlet UITextField *passwordTF; - (IBAction)registerClick:(id)sender; - (IBAction)goBack:(id)sender; //建立一個 XMPPStream類的屬性 @property (nonatomic, strong) XMPPStream *stream; @end @implementation RegisterViewController - (void)viewDidLoad { [super viewDidLoad]; //初始化 XMPPStream self.stream = [[XMPPStream alloc] init]; //設置服務器地址 _stream.hostName = @"127.0.0.1"; //設置服務器的端口號 _stream.hostPort = 5222; //設置XMPPStream代理 [_stream addDelegate:self delegateQueue:dispatch_get_main_queue()]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } /* #pragma mark - Navigation // In a storyboard-based application, you will often want to do a little preparation before navigation - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Get the new view controller using [segue destinationViewController]. // Pass the selected object to the new view controller. } */ - (IBAction)registerClick:(id)sender { //設置 JID //JID是用戶在聊天服務器中的惟一標識 //JID 有兩部分構成: 用戶名@域名 self.stream.myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@bogon", self.userNameTF.text]]; //註冊 //1. 鏈接服務器 //先斷開鏈接 [self.stream disconnect]; //-1 表明無限大, 直到鏈接上服務器 [self.stream connectWithTimeout:-1 error:nil]; } - (IBAction)goBack:(id)sender { [self dismissViewControllerAnimated:YES completion:nil]; } #pragma mark-XMPPStreamDelegate //鏈接服務器超時 - (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender { NSLog(@"%s", __FUNCTION__); } //鏈接服務器成功 - (void)xmppStreamDidConnect:(XMPPStream *)sender { NSLog(@"%s", __FUNCTION__); //2.鏈接服務器成功之後進行註冊 [self.stream registerWithPassword:self.passwordTF.text error:nil]; } //註冊成功 - (void)xmppStreamDidRegister:(XMPPStream *)sender { [[[UIAlertView alloc] initWithTitle:@"恭喜你" message:@"註冊成功" delegate:self cancelButtonTitle:nil otherButtonTitles:@"前往登錄", nil] show]; } //註冊失敗 - (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error { } @end