前一陣子本身閒着沒事,利用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;
}
這是我所寫的簡單的聊天程序,而且也測試成功.