XMPP 基于XML的点对点通信协议

XMPP 基于XML的点对点通信协议

 

IM: 即时通信

XMPP: 基于XML的点对点通信协议

1. ejabber: 这个软件是用来开启本地服务器使用, 本地服务器地址为: 127.0.0.1, 所有的电脑都一样, 开启服务器的软件有很多

2. Audium: 就是一个聊天工具

用局域网聊天

1.在同一个WIFI

2.在同一个本地服务器创建账号

3.登陆的时候, 设置好服务器地址, 为电脑的IP地址

XAMPP: 集成了阿帕奇 FTPMy SQL的一个软件

类的扩展有三种方式

1.协议: 需要自己制定协议, 设置代理, 遵守协议, 一般用来传值(回调)

2.延展: 为类添加私有方法和私有变量, 一般用于已知类(.h.m都存在的类)

3.类目: 为类添加方法和属性(只有settergetter, 没有实例变量), 必须实现内部声明的方法, 一般用于系统类的扩展

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

 

原文地址:https://www.cnblogs.com/OrangesChen/p/5135129.html