前一阵子本身闲着没事,利用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;
}
这是我所写的简单的聊天程序,而且也测试成功.