zoukankan      html  css  js  c++  java
  • XMPP即时通讯(代码实现)

    1.配置XMPP(XMPPConfig.m)

    2.配置XMPPFramework框架

    3.创建单例类(XMPPManager.h/XMPPManager.m)管理器

    XMPPManager.m:

     

    #import "XMPPManager.h"

    #import "AppDelegate.h"

    //连接服务器的目的

    typedef NS_ENUM(NSInteger, ConnectToServerPopurpose)

    {

        ConnectToServerPopurposeLogin, //登录

        ConnectToServerPopurposeRegist //注册

    };

    @interface XMPPManager ()<XMPPStreamDelegate,XMPPRosterDelegate>

    @property (nonatomic, assign) ConnectToServerPopurpose serverPurpose; //连接服务器的目的

    @property (nonatomic, copy) NSString *loginPassword; //登录密码

    @property (nonatomic, copy) NSString *registerPassword; //注册密码

    - (void)connectServer; //连接服务器

    - (void)disConnectWithServer; //断开服务器

    @end

    @implementation XMPPManager

     

    static XMPPManager *manager = nil;

    + (XMPPManager *)defaultXMPPManager {

        //gcd once 程序执行期间只执行一次

        static dispatch_once_t onceToken;

        dispatch_once(&onceToken, ^{

            manager = [[XMPPManager alloc] init];

        });

        return manager;

    }

    //重写init方法

    - (instancetype)init

    {

        self = [super init];

        if (self) {

            //创建通信通道用于和服务器进行连接和沟通

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

            //设置服务器

            self.stream.hostName = kHostName;

            //设置端口号

            self.stream.hostPort = kHostPort;

            //添加代理

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

            

            //创建好友列表仓库

            XMPPRosterCoreDataStorage *rosterCorDataStorage = [XMPPRosterCoreDataStorage sharedInstance];

            //创建花名册对象

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

            //将花名册对象添加到stream活动

            [self.roster activate:self.stream];

            //添加代理

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

            

            //创建信息归档对象

            XMPPMessageArchivingCoreDataStorage *messageArchingCoreStorage = [XMPPMessageArchivingCoreDataStorage sharedInstance];

            //创建信息归档对象

            self.messageArching = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageArchingCoreStorage dispatchQueue:dispatch_get_main_queue()];

            //添加活动到通信管道

            [self.messageArching activate:self.stream];

            

            //获取数据管理器

            self.managerContext = messageArchingCoreStorage.mainThreadManagedObjectContext;

        }

        return self;

    }

     

    #pragma mark - XMPPRosterDelegate

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

        //获取请求对象的JID

        XMPPJID *requestJID = presence.from;

        //创建提示框

        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"添加好友提醒" message:requestJID.user preferredStyle:UIAlertControllerStyleAlert];

        //创建事件

        UIAlertAction *addAction = [UIAlertAction actionWithTitle:@"添加" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {

            //接受好友请求

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

        }];

        UIAlertAction *rejectAction = [UIAlertAction actionWithTitle:@"拒绝" style:UIAlertActionStyleDefault handler:^(UIAlertAction *action) {

            //拒绝好友请求

            [self.roster rejectPresenceSubscriptionRequestFrom:requestJID];

        }];

        //添加事件

        [alertVC addAction:addAction];

        [alertVC addAction:rejectAction];

        //弹出提示框

        //获取AppDelegate

        AppDelegate *appDelegate  = [UIApplication sharedApplication].delegate;

        //获取根视图控制器

        UIViewController *rootVC = appDelegate.window.rootViewController;

        [rootVC presentViewController:alertVC animated:YES completion:nil];

    }

     

    //连接服务器

    - (void)connectServer {

        if ([self.stream isConnected] || [self.stream isConnecting]) {

            //断开连接

            [self disConnectWithServer];

        }

        //建立新的连接(30秒超时)

        NSError *error = nil;

        [self.stream connectWithTimeout:30 error:&error];

        if (error) {

            NSLog(@"connect fail");

        }

    }

     

    //登录

    - (void)loginWithUserName:(NSString *)username password:(NSString *)pw {

        self.loginPassword = pw; //记录登录密码

        self.serverPurpose = ConnectToServerPopurposeLogin; //登录标识

        //获取jid 唯一标识

        XMPPJID *myJID = [XMPPJID jidWithUser:username domain:kDomin resource:kResource];

        self.stream.myJID = myJID; //设置JID

        //连接服务器

        [self connectServer];

    }

    //注册

    - (void)registWithUserName:(NSString *)username passeord:(NSString *)pw {

        self.registerPassword = pw; //记录注册密码

        self.serverPurpose = ConnectToServerPopurposeRegist; //注册标识

        //获取JID唯一标识

        XMPPJID *myJID = [XMPPJID jidWithUser:username domain:kDomin resource:kResource];

        self.stream.myJID = myJID; //设置JID

        //连接服务器

        [self connectServer];

    }

     

    //添加好友

    - (void)addFriend:(NSString *)name {

        //创建JID

        XMPPJID *myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@",name,kHostName]];

        //添加好友

        [self.roster subscribePresenceToUser:myJID];

    }

    //删除好友

    - (void)deleteFriend:(NSString *)name {

        //获取JID

        XMPPJID *myJID = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@",name,kHostName]];

        [self.roster removeUser:myJID];

    }

     

    //断开服务器连接

    - (void)disConnectWithServer {

        //断开服务器

        [self.stream disconnect];

    }

     

    #pragma mark - XMPPStreamDelegate

    //成功连接服务器

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

        NSLog(@"connect success");

        switch (self.serverPurpose) {

            case ConnectToServerPopurposeLogin:

                //登录

            {

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

            }

                break;

            case ConnectToServerPopurposeRegist:

                //注册

            {

                [self.stream registerWithPassword:self.registerPassword error:nil];

            }

                break;

            default:

                break;

        }

    }

    //连接超时

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

        NSLog(@"connect time out");

    }

    @end

    用户登录:

    #import "LoginViewController.h"

    #import "XMPPManager.h"

    #import "RosterTableViewController.h"

    BOOL isClickButton = YES;

    @interface LoginViewController ()<XMPPStreamDelegate>

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

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

     

    @end

     

    @implementation LoginViewController

     

    - (void)viewDidLoad {

        [super viewDidLoad];

        //添加代理

        [[XMPPManager defaultXMPPManager].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.

    }

    */

    #pragma mark - handleAction

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

        isClickButton = YES; //标识点击了登录button

        [[XMPPManager defaultXMPPManager] loginWithUserName:self.userNameTF.text password:self.passwordTF.text];

    }

     

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

        

    }

     

    #pragma mark - XMPPStreamDelegate

    //登录成功

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

        //上线--更改状态

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

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

     

        if (isClickButton) {

                   //提示

            UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"欢迎回来" preferredStyle:(UIAlertControllerStyleAlert)];

            //添加事件

            UIAlertAction *action = [UIAlertAction actionWithTitle:@"" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {

                //获取从storyBoard中获取联系人列表界面

                RosterTableViewController *rosterVC = [self.storyboard instantiateViewControllerWithIdentifier:@"contact"];

                //传值

                rosterVC.userName = self.userNameTF.text;

                rosterVC.paassWord = self.passwordTF.text;

                //push

                [self.navigationController pushViewController:rosterVC animated:YES];

            }];

            [alertVC addAction:action];

            //弹出提示

            [self presentViewController:alertVC animated:YES completion:nil];

           //更改BOOL

            isClickButton = NO;

        }

    }

     

    //登录失败

    - (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(NSXMLElement *)error {

        //提示

        UIAlertController *alertVC = [UIAlertController alertControllerWithTitle:@"温馨提示" message:@"账号或密码错误请核对" preferredStyle:(UIAlertControllerStyleAlert)];

        //添加事件

        UIAlertAction *action = [UIAlertAction actionWithTitle:@"" style:(UIAlertActionStyleDefault) handler:^(UIAlertAction *action) {

            

        }];

        [alertVC addAction:action];

        //弹出提示

        [self presentViewController:alertVC animated:YES completion:nil];

     

    }

    @end

    用户注册:

     

    #import "RegisterViewController.h"

    #import "XMPPManager.h"

     

    @interface RegisterViewController ()<XMPPStreamDelegate>

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

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

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

     

    @end

     

    @implementation RegisterViewController

     

    - (void)viewDidLoad {

        [super viewDidLoad];

        //添加代理

        [[XMPPManager defaultXMPPManager].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.

    }

    */

    #pragma mark - handleAction

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

        //注册

        [[XMPPManager defaultXMPPManager] registWithUserName:self.userNameTF.text passeord:self.passwordTF.text];

    }

     

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

        

    }

     

    #pragma mark - XMPPStreamDelegate

    //注册成功

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

        NSLog(@"register success");

    }

    //注册失败

    - (void)xmppStream:(XMPPStream *)sender didNotRegister:(NSXMLElement *)error {

        NSLog(@"register fail");

    }

    @end

    联系人列表

     

    #import "RosterTableViewController.h"

    #import "RosterCell.h"

    #import "ChatTableViewController.h"

    #import "XMPPManager.h"

     

    @interface RosterTableViewController ()<XMPPRosterDelegate>

    @property (nonatomic, strong) NSMutableArray *contacts; //联系人数组

    @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.contacts = [NSMutableArray array]; //创建数组

        //添加代理

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

        //再次登录

        [[XMPPManager defaultXMPPManager] loginWithUserName:self.userName password:self.paassWord];

    }

     

    - (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning];

        // Dispose of any resources that can be recreated.

    }

     

    #pragma mark - Table view data source

     

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

        return 1;

    }

     

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

     

        return self.contacts.count;

    }

     

     

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

        RosterCell *cell = [tableView dequeueReusableCellWithIdentifier:@"roster" forIndexPath:indexPath];

        //获取对应的联系人JID

        XMPPJID *jid = self.contacts[indexPath.row];

        cell.textLabel.text = jid.user;

        return cell;

    }

     

    #pragma mark - XMPPRosterDelegate

    //开始检索好友

    - (void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender {

        NSLog(@"begin search friends");

    }

    //检索好友,每执行一次获取一个好友信息

    - (void)xmppRoster:(XMPPRoster *)sender didRecieveRosterItem:(NSXMLElement *)item {

        NSLog(@"%@",[[item attributeForName:@"jid"] stringValue]);

        //获取JIDStr

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

        //获取JID

        XMPPJID *myJID = [XMPPJID jidWithString:jidStr resource:kResource];

        //防止重复添加好友

        for (XMPPJID *JID in self.contacts) {

            if ([JID.user isEqualToString:myJID.user]) {

                return;

            }

        }

        //放入数组

        [self.contacts addObject:myJID];

        //刷新界面

        [self.tableView insertRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:self.contacts.count - 1 inSection:0]] withRowAnimation:UITableViewRowAnimationLeft];

    }

    //结束检索好友

    - (void)xmppRosterDidEndPopulating:(XMPPRoster *)sender {

        NSLog(@"end search friends");

    }

     

     

    #pragma mark - Navigation

     

    - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {

        //界面传值

        //获取下一个试图控制器

        ChatTableViewController *chatVC = segue.destinationViewController;

        //获取cell

        UITableViewCell *cell = sender;

        //获取下标

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

        //获取对应的对象

        XMPPJID *JID = self.contacts[index];

        chatVC.friendJID = JID;

    }

    @end

    聊天控制器

     

    #import "ChatTableViewController.h"

    #import "ChatCell.h"

     

    @interface ChatTableViewController ()<XMPPStreamDelegate>

    @property (nonatomic, strong) NSMutableArray *messageArray; //用来存储信息

    @end

     

    @implementation ChatTableViewController

     

    - (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 = [[UIBarButtonItem alloc] initWithTitle:@"发送" style:(UIBarButtonItemStyleDone) target:self action:@selector(sendMessage)];

        //添加代理

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

        //创建数组

        self.messageArray = [NSMutableArray array];

        //获取本地聊天信息

        [self reloadMessage];

        

    }

     

    //发送新的消息

    - (void)sendMessage

    {

        //创建新的信息

        XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.friendJID];

        //添加信息体

        [message addBody:@"比如说"];

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

    }

    - (void)didReceiveMemoryWarning {

        [super didReceiveMemoryWarning];

        // Dispose of any resources that can be recreated.

    }

     

    #pragma mark - Table view data source

     

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

        return 1;

    }

     

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

        return self.messageArray.count;

    }

     

     

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

        ChatCell *cell = [tableView dequeueReusableCellWithIdentifier:@"chat" forIndexPath:indexPath];

        //获取对应数组元素

        XMPPMessage *message = self.messageArray[indexPath.row];

        if ([message isKindOfClass:[XMPPMessage class]]) {

         //将收到的信息放到左边,发送的放到右边

        if ([message.from.user isEqualToString:self.friendJID.user]) {

            cell.textLabel.text = message.body;

            cell.detailTextLabel.text = @"";

        }else {

            cell.textLabel.text = @"";

            cell.detailTextLabel.text = message.body;

        }

        }else {

            //数组中的对象时XMPPMessageArchiving_Mesage_CoreDataObject类型

            XMPPMessageArchiving_Message_CoreDataObject *mes = (XMPPMessageArchiving_Message_CoreDataObject *)message;

            //Outgoing发送,用来判断消息是接受的 还是发送的

            if (![mes isOutgoing]) {

                cell.textLabel.text = mes.message.body;

                cell.detailTextLabel.text = @"";

            }else {

                cell.textLabel.text = @"";

                cell.detailTextLabel.text = mes.message.body;

            }

        }

        return cell;

    }

     

     

    //读取本地信息

    - (void)reloadMessage

    {

        //获取数据管理器

        NSManagedObjectContext *managerContext = [XMPPManager defaultXMPPManager].managerContext;

        //请求对象

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

        //实体描述对象

        //XMPPMessageArchiving_Message_CoreDataObject是持久化信息对应的实体类

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

        [fetchRequest setEntity:entity];

        // 查询条件

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

        [fetchRequest setPredicate:predicate];

        // 排序

        //按照时间排序

        NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp"

                                                                       ascending:YES];

        [fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];

        NSError *error = nil;

        //执行查询,获取符合条件的对象

        NSArray *fetchedObjects = [managerContext executeFetchRequest:fetchRequest error:&error];

        if (fetchedObjects == nil) {

            NSLog(@"your content is null for search");

        }

        //将查询到的本地聊天信息存放到数组中

        [self.messageArray addObjectsFromArray:fetchedObjects];

        //刷新数据

        [self.tableView reloadData];

    }

     

    //展示信息

    - (void)showMessageWithMessage:(XMPPMessage *)message {

        //将信息放入数组

        [self.messageArray addObject:message];

        //刷新数据

        NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.messageArray.count - 1 inSection:0];

        [self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationRight];

        

        //滑动tableView到对应的cell

        [self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:(UITableViewScrollPositionBottom) animated:YES];

    }

     

    #pragma mark - XMPPSteamDelegate

     

    //接收信息

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

        NSLog(@"%@",message.body);

        //只获取当前好友的聊天信息

        if ([message.from.user isEqualToString:self.friendJID.user]) {

            //展示信息

            [self showMessageWithMessage:message];

        }

    }

    //发送信息

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

        [self showMessageWithMessage:message];

    }

    /*

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

    }

    */

     

    @end

  • 相关阅读:
    ZOJ 1403 解密
    HDU 1021 斐波那契
    Wannafly挑战赛26题解
    NOI2019省选模拟赛 第三场
    NOI2019省选模拟赛 第五场
    洛谷P5280 [ZJOI2019]线段树(线段树)
    NOI2019省选模拟赛 第六场
    Wannafly挑战赛29题解
    李超线段树学习笔记
    洛谷P4069 [SDOI2016]游戏(李超线段树)
  • 原文地址:https://www.cnblogs.com/dongdongyuan/p/4936784.html
Copyright © 2011-2022 走看看