浅谈基于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;

}

 

 

这是我所写的简单的聊天程序,而且也测试成功.

相关文章
相关标签/搜索