一.基本框架结构:

StroyBoard的基本页面搭建:

二.个功能代码块的是实现

LoginViewController.m文件(登录页面的基本配置)

#import "LoginViewController.h"
#import "XMPPManager.h"@interface LoginViewController ()<XMPPStreamDelegate>
@property (weak, nonatomic) IBOutlet UITextField *userNameTes;
@property (weak, nonatomic) IBOutlet UITextField *passwordTes;@end@implementation LoginViewController- (void)viewDidLoad {[super viewDidLoad];//添加代理方法
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}#pragma mark ---实现XMPPStreamDelegate中的代理方法//认证登录成功的代理方法
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{NSLog(@"登录成功");//登陆成功进入好友聊天页面
    [self dismissViewControllerAnimated:YES  completion:nil];}
//认证登录失败的代理方法
-(void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error
{NSLog(@"登录失败%@",error);
}- (IBAction)loginWithUserNameAndPassword:(id)sender {//登录
    [[XMPPManager sharedXMPPManager] loginWithUserName:self.userNameTes.text passWord:self.passwordTes.text];}@end

RegisterViewController.m文(注册页面的基本配置)

#import "RegisterViewController.h"
#import "XMPPManager.h"@interface RegisterViewController () <XMPPStreamDelegate>
@property (weak, nonatomic) IBOutlet UITextField *userNameTes;
@property (weak, nonatomic) IBOutlet UITextField *passwordTes;@end@implementation RegisterViewController- (void)viewDidLoad {[super viewDidLoad];//给stream 设置代理监测注册是否成功
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];
}#pragma mark ---实现XMPPStreamDelegate中的代理方法//注册成功
-(void)xmppStreamDidRegister:(XMPPStream *)sender
{NSLog(@"注册成功");//注册成功 进入登陆页面
    [self.navigationController popViewControllerAnimated:YES];
}
//注册失败
-(void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error
{NSLog(@"注册失败");
}- (IBAction)registerWithUserNameAndPassword:(id)sender {//注册[[XMPPManager sharedXMPPManager] registerWithUserName:self.userNameTes.text passWord:self.passwordTes.text];}@end

XMPPManager.h文件 (主要功能实现文件)

#import <Foundation/Foundation.h>
#import "XMPPFramework.h"@interface XMPPManager : NSObject<XMPPStreamDelegate>//声明一个'通道'属性 为了其他类中能够访问到
@property(nonatomic,strong)XMPPStream *stream;//与好友有关的操作
@property(nonatomic,strong)XMPPRoster *roster;//与聊天信息有关的  聊天信息托管上下文
@property(nonatomic,strong)NSManagedObjectContext *context;//信息归档  聊天信息归档对象
@property(nonatomic,strong)XMPPMessageArchiving *messageAchiving;#pragma mark ---获取一个单例对象
+(XMPPManager *)sharedXMPPManager;#pragma mark ---登录
-(void)loginWithUserName:(NSString *)userName passWord:(NSString *)passWord;#pragma mark ---注册
-(void)registerWithUserName:(NSString *)userName passWord:(NSString *)passWord;#pragma mark ---删除好友
-(void)removeFriendName:(NSString *)name;@end

XMPPManager.m文件(主要功能实现文件)

#import "XMPPManager.h"

//枚举
typedef NS_ENUM(NSUInteger,connectToSeverType){connectToSeverLogin,connectToSeverRegister};//枚举
//typedef enum : NSUInteger {
//    <#MyEnumValueA#>,
//    <#MyEnumValueB#>,
//    <#MyEnumValueC#>,
//} <#MyEnum#>;@interface XMPPManager ()//登录用户名
@property(nonatomic,strong)NSString *LoginuserName;
//登录密码
@property(nonatomic,strong)NSString *loginPassWord;
//注册名
@property(nonatomic,strong)NSString *registerUserName;
//注册密码
@property(nonatomic,strong)NSString *registerPassWord;
//用来表示访问服务器的目的(登录或注册)
@property(nonatomic,assign)connectToSeverType connectPurpose;@end@implementation XMPPManager#pragma mark ---获取一个单例对象
+(XMPPManager *)sharedXMPPManager
{static XMPPManager *manager = nil;static dispatch_once_t onceToken;dispatch_once(&onceToken, ^{manager = [[XMPPManager alloc] init];});return manager;}//重写XMPPManager的init方法,是为了创建 通道(xmppStream)
//init方法
- (instancetype)init
{self = [super init];if (self) {//初始化通道self.stream = [[XMPPStream alloc] init];//指定其连接的服务器IP地址self.stream.hostName = kHostName;//指定服务器端口  默认为5222//端口号 是制定当前连接的是什么服务self.stream.hostPort = kHostPort;//给stream设置代理,来检测链接状态和认证状态
        [self.stream addDelegate:self delegateQueue:dispatch_get_main_queue()];//初始化管理好友对象roster(与好友相关的操作)XMPPRosterCoreDataStorage *storage = [XMPPRosterCoreDataStorage sharedInstance];self.roster = [[XMPPRoster alloc] initWithRosterStorage:storage dispatchQueue:dispatch_get_main_queue()];//在管道上激活对象
        [self.roster activate:self.stream];//初始化messageAchiving对象XMPPMessageArchivingCoreDataStorage *messageStroge = [XMPPMessageArchivingCoreDataStorage sharedInstance];self.messageAchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:messageStroge dispatchQueue:dispatch_get_main_queue()];//激活
        [self.messageAchiving activate:self.stream];//托管上下文对象self.context = messageStroge.mainThreadManagedObjectContext;}return self;
}#pragma mark ---登录
-(void)loginWithUserName:(NSString *)userName passWord:(NSString *)passWord
{self.loginPassWord = userName;self.LoginuserName = userName;//此时将枚举属性的值设为connectToSeverLoginself.connectPurpose = connectToSeverLogin;//链接
    [self linkConnect];}//链接的方法
-(void)linkConnect
{//在真正链接之前,需要告诉服务器你是谁,//XMPPJID在Xmpp中身份的唯一标识XMPPJID *myJID = nil;switch (self.connectPurpose) {case connectToSeverLogin:{myJID = [XMPPJID jidWithUser:self.LoginuserName domain:kDomin resource:kResource];break;}case connectToSeverRegister:{myJID = [XMPPJID jidWithUser:self.registerUserName domain:kDomin resource:kResource];break;}default:break;}
//    XMPPJID *myJID = [XMPPJID jidWithUser:self.LoginuserName domain:kDomin resource:kResource];//将自己的JID设置到Stream通道上self.stream.myJID = myJID;//开始链接if ([self.stream isConnected] || [self.stream isConnecting]) {//先断开
        [self disconnectToSever];}//连接到服务器NSError *error = nil;[self.stream connectWithTimeout:30.0 error:&error];if (error) {NSLog(@"error:%@",error);}}//断开链接
-(void)disconnectToSever{//设置下线状态元素XMPPPresence *pressence = [XMPPPresence presenceWithType:@"unavailable"];//将stream上的状态改为下线
    [self.stream sendElement:pressence];//断开链接
    [self.stream disconnect];}#pragma mark ---注册
-(void)registerWithUserName:(NSString *)userName passWord:(NSString *)passWord
{self.registerPassWord = passWord;self.registerUserName = userName;//将枚举值设为注册self.connectPurpose = connectToSeverRegister;//链接
    [self linkConnect];
}#pragma mark ---XMPPStreamDelegate的方法
//链接成功
-(void)xmppStreamDidConnect:(XMPPStream *)sender
{NSLog(@"连接成功");//进行认证switch (self.connectPurpose) {case connectToSeverLogin://如果链接的目的是登录,这里需要一个登录的passWord
            [self.stream authenticateWithPassword:self.loginPassWord error:nil];break;case connectToSeverRegister://如果链接的目的是注册,这里需要一个注册的passWord
            [self.stream registerWithPassword:self.registerPassWord error:nil];break;default:break;}}
//链接失败
-(void)xmppStreamDidDisconnect:(XMPPStream *)sender withError:(NSError *)error
{NSLog(@"连接失败%@",error);
}
//链接超时
-(void)xmppStreamConnectDidTimeout:(XMPPStream *)sender
{NSLog(@"链接超时");}
//认证成功
-(void)xmppStreamDidAuthenticate:(XMPPStream *)sender
{NSLog(@"认证成功");//设置上线状态XMPPPresence *presence = [XMPPPresence presenceWithType:@"available"];[self.stream sendElement:presence];}#pragma mark ---删除好友
-(void)removeFriendName:(NSString *)name
{//根据username和域名,创建一个jidXMPPJID *friendJid = [XMPPJID jidWithString:[NSString stringWithFormat:@"%@@%@",name,kDomin ]];//取消对好友的监听
    [self.roster unsubscribePresenceFromUser:friendJid];//将好友移除
    [self.roster removeUser:friendJid];}@end

RosterTableViewController.m文件(好友界面功能的实现)

#import "RosterTableViewController.h"
#import "XMPPManager.h"
#import "ChatTableViewController.h"@interface RosterTableViewController ()<XMPPRosterDelegate,UIAlertViewDelegate,XMPPStreamDelegate>//所有好友
@property(nonatomic,strong)NSMutableArray *allRoster;//请求者的jid
@property(nonatomic,strong)XMPPJID *requestJid;@end//存放所有的好友
@implementation RosterTableViewController- (void)viewDidLoad {[super viewDidLoad];//对数组进行初始化self.allRoster = [NSMutableArray array];//通过代理协议,获取好友
    [[XMPPManager sharedXMPPManager].roster addDelegate:self delegateQueue:dispatch_get_main_queue()];//监听好友的状态  通过代理协议的形式
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];}#pragma mark ---实现XMPPRosterDelegate协议中的方法
//开始获取好友
-(void)xmppRosterDidBeginPopulating:(XMPPRoster *)sender
{NSLog(@"开始获取好友");}
//正在获取好友  有几个好友这个放法就会执行几次
-(void)xmppRoster:(XMPPRoster *)sender didReceiveRosterItem:(DDXMLElement *)item
{NSLog(@"%@",item);//to 订阅了别人//from 别人订阅了你//both 互为好友NSString *subscription = [[item attributeForName:@"subscription"] stringValue];if ([subscription isEqualToString:@"from"] || [subscription isEqualToString:@"both"]) {//获取好友的jid字符串NSString *jidStr = [[item attributeForName:@"jid" ] stringValue];//获取好友的jidXMPPJID *frienfJid = [XMPPJID jidWithString:jidStr];//如果之前加过了,表示好友已经存在if ([_allRoster containsObject:frienfJid ]) {return;}//将好友对象(frienfJid) 添加到数组中
        [self.allRoster addObject:frienfJid];//插入行//插入到最后一行NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.allRoster.count - 1 inSection:0];[self.tableView insertRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];}}//结束获取好友
-(void)xmppRosterDidEndPopulating:(XMPPRoster *)sender
{NSLog(@"好友获取结束");
}//获取好友请求
-(void)xmppRoster:(XMPPRoster *)sender didReceivePresenceSubscriptionRequest:(XMPPPresence *)presence
{NSLog(@"获取好友请求!");self.requestJid =  presence.from;UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"好友请求" message:_requestJid.user delegate:self cancelButtonTitle:@"Cancel" otherButtonTitles:@"OK", nil];[alert show];}-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{switch (buttonIndex) {case 0://点击拒绝按钮
          [[XMPPManager sharedXMPPManager].roster rejectPresenceSubscriptionRequestFrom:self.requestJid];break;case 1://接受请求按钮
            [[XMPPManager sharedXMPPManager].roster acceptPresenceSubscriptionRequestFrom:self.requestJid andAddToRoster:NO];break;default:break;}self.requestJid = nil;
}#pragma mark --实现XMPPStreamDelegate中的方法
//监听好友的状态
-(void)xmppStream:(XMPPStream *)sender didReceivePresence:(XMPPPresence *)presence
{//监测好友的状态NSString *presenceType =  presence.type;if ([presenceType isEqualToString:@"available"]) {NSLog(@"好友在线状态!");}  else if ([presenceType isEqualToString:@"unavailable"])NSLog(@"好友处于下线状态!");}#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {// Return the number of sections.return 1;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {// Return the number of rows in the section.return self.allRoster.count;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"roster" forIndexPath:indexPath];XMPPJID *friendJid = _allRoster[indexPath.row];cell.textLabel.text = friendJid.user;return cell;
}// Override to support conditional editing of the table view.
- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath {// Return NO if you do not want the specified item to be editable.return YES;
}// Override to support editing the table view.
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath {if (editingStyle == UITableViewCellEditingStyleDelete) {//删除数据源XMPPJID *jid = _allRoster[indexPath.row];[[XMPPManager sharedXMPPManager]removeFriendName:jid.user];[self.allRoster removeObjectAtIndex:indexPath.row];[tableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationFade];} else if (editingStyle == UITableViewCellEditingStyleInsert) {// Create a new instance of the appropriate class, insert it into the array, and add a new row to the table view
    }
}#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 {//获取点击的cellUITableViewCell *cell = (UITableViewCell *)sender;NSIndexPath *indexPah =  [self.tableView indexPathForCell:cell];ChatTableViewController *chatVC =  (ChatTableViewController *)segue.destinationViewController;chatVC.chatToJid = _allRoster[indexPah.row];}@end

ChatTableViewController.h文件(聊天界面功能的实现)

#import <UIKit/UIKit.h>
#import "XMPPManager.h"@interface ChatTableViewController : UITableViewController@property(nonatomic,strong)XMPPJID *chatToJid;@end

ChatTableViewController.m文件(聊天界面功能的实现)

#import "ChatTableViewController.h"
#import "XMPPManager.h"@interface ChatTableViewController ()<XMPPStreamDelegate>//信息数组
@property(nonatomic,strong)NSMutableArray *allMessages;
- (IBAction)sendMessage:(UIBarButtonItem *)sender;@end@implementation ChatTableViewController- (void)viewDidLoad {[super viewDidLoad];//初始化数组self.allMessages = [NSMutableArray array];//设置代理
    [[XMPPManager sharedXMPPManager].stream addDelegate:self delegateQueue:dispatch_get_main_queue()];//显示历史记录
    [self parseMesage];}#pragma mark --实现协议中的方法
//已经接受好友发送的信息
-(void)xmppStream:(XMPPStream *)sender didReceiveMessage:(XMPPMessage *)message
{NSLog(@"已经接受信息:%@",message);[self parseMesage];
}-(void)parseMesage
{//查询请求NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];NSEntityDescription *entity = [NSEntityDescription entityForName:@"XMPPMessageArchiving_Message_CoreDataObject" inManagedObjectContext:[XMPPManager sharedXMPPManager].context];[fetchRequest setEntity:entity];// Specify criteria for filtering which objects to fetch//谓词  查询条件NSPredicate *predicate = [NSPredicate predicateWithFormat:@"bareJidStr == %@ AND streamBareJidStr == %@", _chatToJid.bare,[XMPPManager sharedXMPPManager].stream.myJID.bare];[fetchRequest setPredicate:predicate];// Specify how the fetched objects should be sorted//排序NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timestamp"ascending:YES];[fetchRequest setSortDescriptors:[NSArray arrayWithObjects:sortDescriptor, nil]];NSError *error = nil;NSArray *fetchedObjects = [[XMPPManager sharedXMPPManager].context executeFetchRequest:fetchRequest error:&error];if (fetchedObjects == nil) {NSLog(@"没有聊天记录!");} else {//清空之前的聊天记录
        [self.allMessages removeAllObjects];[self.allMessages addObjectsFromArray:fetchedObjects];[self.tableView reloadData];if (_allMessages.count == 0) {return;}NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.allMessages.count -1 inSection:0];[self.tableView selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];}
}
#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
#warning Incomplete implementation, return the number of sectionsreturn 1;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
#warning Incomplete implementation, return the number of rowsreturn _allMessages.count;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"chat" forIndexPath:indexPath];XMPPMessageArchiving_Message_CoreDataObject *message = _allMessages[indexPath.row];if (message.isOutgoing == YES) {  //自己发的cell.detailTextLabel.text = message.body;cell.textLabel.text = @"";} else {cell.textLabel.text = message.body;cell.detailTextLabel.text = @"";}return cell;
}#pragma mark --给好友发送消息
- (IBAction)sendMessage:(UIBarButtonItem *)sender {//发送给谁消息(Jid)XMPPMessage *message = [XMPPMessage messageWithType:@"chat" to:self.chatToJid];//设置发送内容[message addBody:@"你好!"];//开始发送
    [[XMPPManager sharedXMPPManager].stream sendElement:message];}
//信息发送成功
-(void)xmppStream:(XMPPStream *)sender didSendMessage:(XMPPMessage *)message
{NSLog(@"信息发送成功:%@",message);[self parseMesage];}
//信息发送失败
-(void)xmppStream:(XMPPStream *)sender didFailToSendMessage:(XMPPMessage *)message error:(NSError *)error
{NSLog(@"信息发送失败:%@",error);}@end

转载于:https://www.cnblogs.com/erdeng/p/4951143.html

项目开发--------XMPP即时通讯相关推荐

  1. Android-IM从零开始开发一个即时通讯项目

    Android-IM从零开始开发一个即时通讯项目 https://www.jianshu.com/p/dca480006691 关于聊天室项目 聊天室项目,也被称为即时通讯(IM). 其原理是服务器是 ...

  2. 基于Android开发的即时通讯聊天app

    基于Android开发的即时通讯聊天app 前言 即时通讯(Instant Messaging,简称IM)在互联网中应用十分广泛,它可以和很多的领域结合,发挥十分重要的作用.比如金融行业的支付宝.各大 ...

  3. [go学习笔记.第十六章.TCP编程] 3.项目-海量用户即时通讯系统-redis介入,用户登录,注册

    1.实现功能-完成用户登录 在redis手动添加测试用户,并画出示意图以及说明注意事项(后续通过程序注册用户) 如:输入用户名和密码,如果在redis中存在并正确,则登录,否则退出系统,并给出相应提示 ...

  4. Android基于环信开发的即时通讯APP

    Android基于环信开发的即时通讯APP,源代码在Github上,GitHub地址为:https://github.com/PowerDos/BLChat ,希望能够帮到你们,下面是系统简介. 简介 ...

  5. winform服务器消息推送,winform项目——仿QQ即时通讯程序12:服务端程序补充及优化...

    原标题:winform项目--仿QQ即时通讯程序12:服务端程序补充及优化 上一篇文章大概完成了服务端程序,今天继续做项目的时候发现还有一些功能没有做,还有几处地方不够完善.不做好就会影响客户端程序的 ...

  6. talent-aio1.0.2 发布,让天下没有难开发的即时通讯

    web开发领域,springmvc之外有jfinal:TCP长连接领域,netty之外还有更易用的talent-aio 更好用.更接开发人员地气的TCP长连接框架,talent-aio 1.0.2正式 ...

  7. xmpp即时通讯的笔记(摘抄)

    xmpp的使用: 即时通讯 instant messaging(IM) :  -->实时收发信息! 即时通讯相关软件: **QQ,MSN,GoogleTalk,AIM,Jabber(XMPP别名 ...

  8. 视频开发(即时通讯平台)

    当前,全球环境瞬息万变,快捷的信息沟通已经成为人们正常生活的必须,而视频通信的正是借助这一市场需求完成了由"信息化代表"到"代表信息化"的完美蜕变.市场细化应用 ...

  9. winform项目_winform项目——仿QQ即时通讯程序01:原理及项目分析

    即时通讯程序,腾讯QQ可以说是一家独大,虽然市场上仍然有类似QQ的即时通讯程序,但是基本上面向的对象都是特定人群.那么,现在做一个即时通讯的软件还有意义吗?在我看来,意义非常大.作为一个学习编程的人, ...

最新文章

  1. 我被编程语言PUA了!用互联网黑话写代码,每天都在“赋能”变量
  2. java端模拟http的get、post请求(转)
  3. vscode快捷替换json格式
  4. 鼠标右键 移动选定的文件夹到指定位置_iRightMouse:一款免费Mac鼠标右键增强神器...
  5. rust怎么关阳光指令_我家也有庭院多好,伸缩阳光房装上,能休闲能当车库,还不算违建...
  6. 使用Image.GetThumbnailImage 方法返回缩略图
  7. react 版权问题_react兼容ie
  8. php tp框架文档,Thinkphp 框架基础之入口文件功能、定义与用法分析
  9. java 怎么入门_学习java怎么入门
  10. 免费下载百度文库文档、免注册、免登录、免财富值 - 帮手网-云下载
  11. ManualResetEvent
  12. 达梦单机数据库服务器磁盘空间占满问题
  13. 天翼网关最新超级密码2020_5G 下体验阿里云盘后,我决定继续用天翼和度盘
  14. Latex排版学习笔记(1)——希腊字母表及其在latex中的表示
  15. 野蛮人传教士问题(上)
  16. TMS320F28335入门(七)eCAP学习
  17. NVIDIA Jetson TK1学习与开发——简介(针对嵌入式系统应用释放 GPU 的潜能)
  18. 苏州大学转专业计算机2019汇总,关于2019-2020学年第一学期普通本科生转专业名单的公示...
  19. 服务器普通硬盘,服务器硬盘和普通硬盘区别
  20. ettercap局域网arp欺骗,轻松窃密

热门文章

  1. tensorflow dataset.shuffle dataset.batch dataset.repeat 理解 注意点
  2. python怎么实现输入多行文字_介绍一个Python 包,几行代码可实现 OCR 文本识别!...
  3. mysql无法存储文字_mysql存储不了中文字符串怎么办
  4. 在eclipse导入SSH项目
  5. C#xml创建修改读取删除帮助类XmlHelper.cs
  6. Three.js中实现点击按钮添加删除旋转立方体
  7. Windows下使用Java API操作HDFS的常用方法
  8. Node搭建静态资源服务器时后缀名与响应头映射关系的Json文件
  9. MongoDb在Windows上的下载安装以及可视化工具的下载与使用
  10. C#中调用Windows系统服务exe程序的工具类与重启服务的流程