淺談基於XMPP的簡單即時通信

    前一陣子本身閒着沒事,利用localhost作了一個簡單的即時通信,固然,只是實現了基本的文字聊天.但也算是有所小成.今天與你們分享一下,也算是鞏固一下.數組

    首先,咱們須要導入XMPP框架文件,能夠在我博客文件中進行下載.服務器

    以後,咱們須要導入兩個庫,分別是libxml2.dylib,libresolv.dylib.app

   首先須要在XMPPConfig.h中將#define  kHostName      @"localhost"//個人本地服務器域名爲localhost,對應設置成本身服務器的框架

  #define kDomin @"localhost"  //這個也是如此dom

    接下來咱們須要建立一個單例XMPP管理類,用於登錄和註冊,以及好友申請驗證ide

    

#import <Foundation/Foundation.h>測試

#import "XMPPFramework.h" //導入頭文件this

@interface XMPPManager : NSObject<XMPPStreamDelegate, XMPPRosterDelegate> //簽定兩個協議atom

//xmpp單例管理者spa

+ (XMPPManager *)shareXMPPManager;

//通信管道,全部通信 都是經過xmppStream管道來完成

@property (nonatomic, strong) XMPPStream *stream;

//好友roster

@property (nonatomic, strong) XMPPRoster *roster;

//信息存儲類

@property (nonatomic, strong) XMPPMessageArchiving *messageArchiving;

@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;

 

//d登錄方法

- (void)loginWithUsreName:(NSString *)userName password:(NSString *)password;

 

//註冊方法

- (void)registWithUserName:(NSString *)userName password:(NSString *)password;

接下來是.m的實現

#import "XMPPManager.h"

typedef NS_ENUM(NSInteger, ConnectToServerPurpose){

    ConnectToServerPurposeLogin, //登錄

    ConnectToServerPurposeRegist,//註冊

};   /定義一個枚舉,用於判斷登錄註冊

 

@interface XMPPManager ()<UIAlertViewDelegate>  //這個是alertview的代理協議,

@property (nonatomic) ConnectToServerPurpose connectPurpose; //下面的都是私有屬性

//登錄用的密碼

@property (nonatomic, strong) NSString *loginPassword;

//註冊用的用戶名

@property (nonatomic, strong) NSString *registPassword;

//xmpp用戶表示ID::JID

@property (nonatomic, strong) XMPPJID *subscrptionRequestJID;

 

@end

 

@implementation XMPPManager

 

 

+(XMPPManager *)shareXMPPManager 

{

    static XMPPManager *manager = nil;

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        manager = [[XMPPManager alloc] init];

    }); //GCD模式聲明單例

    

    return manager;

    

}

//對xmpp進行初始化

- (instancetype)init{

    self = [super init];

    if (self) {

        self.stream = [[XMPPStream alloc] init];

        //openfire服務器地址

        self.stream.hostName = kHostName;

        //openfire端口號

        self.stream.hostPort = kHostPort;

        //代理對象

        //爲xmppSream添加代理

        [self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

        //好友列表

        XMPPRosterCoreDataStorage *roseter = [XMPPRosterCoreDataStorage sharedInstance];

        self.roster = [[XMPPRoster alloc]initWithRosterStorage:roseter dispatchQueue:dispatch_get_main_queue()];

        [self.roster activate:self.stream];

        [self.roster addDelegate:self delegateQueue:dispatch_get_main_queue()];

        //消息列表

        XMPPMessageArchivingCoreDataStorage *messageArchivingCoreDataStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];

        self.messageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageArchivingCoreDataStorage dispatchQueue:dispatch_get_main_queue()];

        [self.messageArchiving activate:self.stream];

        self.managedObjectContext = [messageArchivingCoreDataStorage mainThreadManagedObjectContext];

    }

    return self;

}

 

//登錄方法

 

- (void)loginWithUsreName:(NSString *)userName password:(NSString *)password

{

    NSLog(@"登錄密碼和帳號");

    self.connectPurpose = ConnectToServerPurposeLogin;

    self.loginPassword = password;

    [self connectToSeverWithUserName:userName];

    

    

    

    

}

//註冊方法

- (void)registWithUserName:(NSString *)userName password:(NSString *)password

{

    self.connectPurpose = ConnectToServerPurposeRegist;

    self.registPassword = password;

    //獲取JID

    [self connectToSeverWithUserName:userName];

 

}

- (void)connectToSeverWithUserName:(NSString *)userName

{

    NSLog(@"username = %@", userName);

        self.stream.myJID =  [XMPPJID jidWithUser:userName domain:kDomin resource:kResource];

    //[self.stream setHostName:kHostName];

    [self connectToSever];

}

 

 

- (void)connectToSever{

    //首先判斷,是否存在連接

    //如有鏈接,先斷開,而後從新創建連接

    if ([self.stream isConnected]) {

        //斷開

        NSLog(@"斷開");

        [self disconnectWithServer];

    }

    NSError *error = nil;

    [self.stream connectWithTimeout:30.0f error:&error];   //請求三十秒

    if (error != nil) {

        NSLog(@"請求失敗 %s | error = %@", __FUNCTION__, error);

    }

    

}

 

- (void)disconnectWithServer

{

    //在線狀態

    XMPPPresence *presence = [XMPPPresence presenceWithType:@"unavailable"];

    [self.stream sendElement:presence];

    [self.stream disconnect];

}

#pragma mark -- xmppStreamDelegate

//鏈接服務器成功

- (void)xmppStreamDidConnect:(XMPPStream *)sender{

    NSLog(@"%s__%d__|", __FUNCTION__, __LINE__);

    NSLog(@"鏈接成功");

    switch (self.connectPurpose) {

        case ConnectToServerPurposeLogin://登錄

            [sender authenticateWithPassword:self.loginPassword error:nil];

            break;

        case ConnectToServerPurposeRegist://註冊

            [sender registerWithPassword:self.registPassword error:nil];

            break;

        default:

            break;

    }

}

//驗證密碼成功

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender

{

    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"isLogin"] == NO) {

        return;

    }

    NSLog(@"驗證密碼成功");

    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];

    [self.stream sendElement:presence];

}

//驗證密碼失敗

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error

{   NSLog(@"驗證密碼失敗");

    NSLog(@"error = %@", error);

    if ([[NSUserDefaults standardUserDefaults] boolForKey:@"isLogin"] == NO) {

        return;

    }

    NSLog(@"%s__%d__|", __FUNCTION__, __LINE__);

}

 

- (void)xmppStreamConnectDidTimeout:(XMPPStream *)sender

{

    NSLog(@"connect Time out%s__%d__|", __FUNCTION__, __LINE__);

    NSLog(@"請求超時");

}

 

#pragma mark --xmppRosterDelegate

- (void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence

{

     //好友申請,from發送好友的jid

    self.subscrptionRequestJID = presence.from;

    UIAlertView *alertview = [[UIAlertView alloc] initWithTitle:@"添加好友申請" message:presence.from.user delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"肯定", nil];

    [alertview show];

    

}

 

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex

{

    switch (buttonIndex) {

        case 0:

            [self.roster rejectPresenceSubscriptionRequestFrom:self.subscrptionRequestJID];

            break;

        case 1:

            [self.roster acceptPresenceSubscriptionRequestFrom:self.subscrptionRequestJID andAddToRoster:YES];

        default:

            break;

    }

    self.subscrptionRequestJID = nil;

}

@end

 

接下來是註冊頁面的邏輯方法書寫

  UI界面是直接用可視化storyboard拖拽的,就是一個輸入用戶名,一個密碼框,加上一個點擊註冊的按鈕

#import "RegistViewController.h"

#import "XMPPManager.h"

@interface RegistViewController ()<XMPPStreamDelegate> //簽定協議

@property (weak, nonatomic) IBOutlet UITextField *userName;

@property (weak, nonatomic) IBOutlet UITextField *password;

 

@end

 

@implementation RegistViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    [[XMPPManager shareXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(resihn)];//添加手勢輕拍回收鍵盤

    [self.view addGestureRecognizer:tap];

 

    

}

- (void)resihn

{

    [self.password resignFirstResponder];

    [self.userName resignFirstResponder];

}

- (IBAction)clickRegistButton:(UIButton *)sender {

    

    [[XMPPManager shareXMPPManager] registWithUserName:self.userName.text password:self.password.text];  //調用單例的註冊方法

    

    

}

 

- (void)xmppStreamDidRegister:(XMPPStream *)sender

{

    NSLog(@"%s__%d__|", __FUNCTION__, __LINE__);

    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isLogin"];  //本地簡單數據存儲,利用bool值表示登錄或沒有登錄.可使得下次運行程序時判斷直接登錄與否

    NSLog(@"註冊成功");

    NSUserDefaults *user = [NSUserDefaults standardUserDefaults];   //利用本地數據化存儲保存帳號密碼

    [user setObject:self.userName.text forKey:@"userName"];

    [user setObject:self.password.text forKey:@"password"];

    XMPPPresence *presence = [[XMPPPresence alloc] initWithType:@"available"];

    [[XMPPManager shareXMPPManager].stream sendElement:presence];

    [self.navigationController dismissViewControllerAnimated:YES completion:^{

        

        

    }];

}

 

- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error

{

    NSLog(@"註冊失敗");

}

 

接下來是登錄頁面的邏輯書寫.

#import "LoginViewController.h"

#import "XMPPManager.h"

@interface LoginViewController ()<XMPPStreamDelegate>

@property (weak, nonatomic) IBOutlet UITextField *userName;

@property (weak, nonatomic) IBOutlet UITextField *password;

 

@end

 

@implementation LoginViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    // Do any additional setup after loading the view.

    NSLog(@"asdas");

    [[XMPPManager shareXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(resihn)];

    [self.view addGestureRecognizer:tap];

}

- (void)resihn

{

    [self.password resignFirstResponder];

    [self.userName resignFirstResponder];

}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

- (IBAction)clickLoginButton:(UIButton *)sender {

    

    [[XMPPManager shareXMPPManager] loginWithUsreName:self.userName.text password:self.password.text];  //調用單例的登錄方法

    NSLog(@"zoule");

}

#pragma mark -- xmppStramDelegate

//登錄成功

- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender

{

    NSLog(@"登錄成功--%s__%d__|", __FUNCTION__, __LINE__);

    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"isLogin"];

    [[NSUserDefaults standardUserDefaults] setObject:self.userName.text forKey:@"userName"];

    [[NSUserDefaults standardUserDefaults] setObject:self.password.text forKey:@"password"];  //登錄成功時也是保存帳號密碼

    [[NSUserDefaults standardUserDefaults] synchronize];

    XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];

    [[XMPPManager shareXMPPManager].stream sendElement:presence];

    //回到好友列表

    [self.navigationController dismissViewControllerAnimated:YES completion:^{

        

        

    }];

}

 

//登錄失敗

- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error

{

    NSLog(@"%s__%d__|登錄失敗", __FUNCTION__, __LINE__);

}

 

而後好友列表頁面

#import "RosterTableViewController.h"

#import "XMPPManager.h"

#import "ChatTableViewController.h"

@interface RosterTableViewController ()<XMPPRosterDelegate>

@property (nonatomic, strong) NSMutableArray *rosterArr;

 

@end

 

@implementation RosterTableViewController

 

- (void)viewDidLoad {

    [super viewDidLoad];

    

    // Uncomment the following line to preserve selection between presentations.

    // self.clearsSelectionOnViewWillAppear = NO;

    

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    self.rosterArr = [NSMutableArray array];

    [[XMPPManager shareXMPPManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];

    NSString *password = [userDefaults objectForKey:@"password"];

    NSString *userName = [userDefaults objectForKey:@"userName"];

    [[XMPPManager shareXMPPManager] loginWithUsreName:userName password:password];

    

}

 

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

#pragma mark - Table view data source

 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

#warning Potentially incomplete method implementation.

    // Return the number of sections.

    return 1;

}

- (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender

{

    NSLog(@"wowoowow");

    NSLog(@"%s__%d__|", __FUNCTION__, __LINE__);

    //開始接受好友

}

 

//每一次接受好友都會執行一次

- (void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item

{

    NSLog(@"%s__%d__|item = %@", __FUNCTION__, __LINE__, item);

    NSString *JIDstr = [[item attributeForName:@"jid"] stringValue];

    XMPPJID *jid = [XMPPJID jidWithString:JIDstr];

    if ([self.rosterArr containsObject:jid]) {

        return;

    }

    [self.rosterArr addObject:jid];

    [self.tableView reloadData];

 

    NSIndexPath *indexpath = [NSIndexPath indexPathForRow:self.rosterArr.count - 1 inSection:0];

    [self.tableView scrollToRowAtIndexPath:indexpath atScrollPosition:UITableViewScrollPositionBottom animated:YES];

 

}

 

//接受結束

- (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender

{

    NSLog(@"%s__%d__|接受結束", __FUNCTION__, __LINE__);

}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

#warning Incomplete method implementation.

    // Return the number of rows in the section.

    return self.rosterArr.count;

}

 

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Roster" forIndexPath:indexPath];

    XMPPJID *jid = [self.rosterArr objectAtIndex:indexPath.row];

    cell.textLabel.text = jid.user;

    

    // Configure the cell...

    

    return cell;

}

 

 

 

 

 

 

 

#pragma mark - 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.

    ChatTableViewController *chatVC = [segue destinationViewController];

    UITableViewCell *cell = sender;

    NSInteger i = [self.tableView indexPathForCell:cell].row;

    XMPPJID *jid = [self.rosterArr objectAtIndex:i];

    chatVC.chatJIDStr = jid.bare;

    //chatVC.chatJID = jid;

}

 

而後是聊天頁面的書寫

 

#import "ChatTableViewController.h"

#import "XMPPManager.h"

@interface ChatTableViewController ()<XMPPStreamDelegate>

//消息數組

@property (nonatomic, strong) NSMutableArray *messageArr;

 

@end

 

@implementation ChatTableViewController

//發送消息

- (IBAction)sendMessageButton:(UIBarButtonItem *)sender {

    XMPPJID *jid = [XMPPJID jidWithString:self.chatJIDStr resource:kResource];

    XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:jid];

    [message addBody:@"喬寄峯是傻逼"];

    [[XMPPManager shareXMPPManager].stream sendElement:message];

    

    

}

#pragma mark -- streamDelegate

//成功接收消息代理方法

- (void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message

{

    

    NSLog(@"%s__%d__|message = %@", __FUNCTION__, __LINE__, message);

    [self reloadMessage];

    

}

//成功發送消息

- (void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message

{

    NSLog(@"%s__%d__|message = %@", __FUNCTION__, __LINE__, message);

    [self reloadMessage];

    

}

- (void)viewDidLoad {

    [super viewDidLoad];

    

    // Uncomment the following line to preserve selection between presentations.

    // self.clearsSelectionOnViewWillAppear = NO;

    

    // Uncomment the following line to display an Edit button in the navigation bar for this view controller.

    // self.navigationItem.rightBarButtonItem = self.editButtonItem;

    self.messageArr = [NSMutableArray array];

    [[XMPPManager shareXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];

    [self reloadMessage];

}

//刷新消息

- (void)reloadMessage

{

    NSManagedObjectContext *managedObjectContext = [XMPPManager shareXMPPManager].managedObjectContext;

    NSFetchRequest *request = [[NSFetchRequest alloc] init];

    

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:managedObjectContext];

    [request setEntity:entity];

    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"streamBareJidStr == %@ AND bareJidStr == %@", [XMPPManager shareXMPPManager].stream.myJID.bare, self.chatJIDStr];

    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"timestamp" ascending:YES];

    [request setSortDescriptors:@[sort]];

    [request setPredicate:predicate];

    NSArray *result = [managedObjectContext executeFetchRequest:request error:nil];

    if (request != nil) {

        if (self.messageArr.count != 0) {

            [self.messageArr removeAllObjects];

                    }

    }

    [self.messageArr addObjectsFromArray:result];

    [self.tableView reloadData];

 

    

}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

 

#pragma mark - Table view data source

 

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {

#warning Potentially incomplete method implementation.

    // Return the number of sections.

    return 1;

}

 

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {

#warning Incomplete method implementation.

    // Return the number of rows in the section.

    return self.messageArr.count;

}

 

 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Chat" forIndexPath:indexPath];

    

    // Configure the cell...

    XMPPMessageArchiving_Message_CoreDataObject *message = [self.messageArr objectAtIndex:indexPath.row];

    if (message.isOutgoing) {

        cell.detailTextLabel.text = message.body;

        cell.textLabel.text = @"";

    }else{

        cell.detailTextLabel.text = @"";

        cell.textLabel.text = message.body;

    }

    

    

    return cell;

 

}

 

至於AppDelegate的書寫,就看本身的邏輯方式了,我是先判斷前一次是否登錄,若是沒有登錄,則前往登錄頁面,而且在登錄頁面若是沒有帳號,就點擊前往註冊.若是前一次登錄了,bool值爲yes,則直接前往好友裂變頁面

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

//    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

//    self.window.backgroundColor = [UIColor whiteColor];

    [self.window makeKeyAndVisible];

    NSUserDefaults *userdefault = [NSUserDefaults standardUserDefaults];

//    //[userdefault setBool:NO forKey:@"isLogin"];

//    //[userdefault synchronize];

    if ([userdefault boolForKey:@"isLogin"] == NO) {

        UIStoryboard *loginStory = [UIStoryboard storyboardWithName:@"LoginAndRegist" bundle:nil];

        //用VC獲得loginStrotyBoard

        

        UIViewController *VC = [loginStory instantiateInitialViewController];

        //self.window.rootViewController = VC;

        

        [self.window.rootViewController presentViewController:VC animated:YES completion:nil];

   }

 

    return YES;

}

 

 

這是我所寫的簡單的聊天程序,而且也測試成功.

相關文章
相關標籤/搜索