首页了解用户唯一标识有什么组成(JID):
JID 一般由三部分构成:用户名,域名和资源名,例如 test@example.com/Anthony
如果没有设置主机名,则使用 JID 的域名作为主机名
端口号是可选的,默认是 5222
下面动手开始,来创建一个单例,用于全权负责管理与XMPP交互,
@interface HYBXMPPHelper : NSObject <XMPPStreamDelegate, XMPPRosterDelegate>
/**
* Singleton shared method
*/
+ (HYBXMPPHelper *)shared;
- (BOOL)connect;
- (BOOL)disconnect;
// 上线
- (void)goOnline;
// 下线
- (void)goOffline;
- (void)destroyResources;
@end
还得声明这几个属性,在匿名扩展中添加,无须对外公开:
@property (nonatomic, strong) XMPPStream *xmppStream;
@property (nonatomic, strong) XMPPReconnect *xmppReconnect;
// 花名册相关
@property (nonatomic, strong) XMPPRoster *xmppRoster;
@property (nonatomic, strong) XMPPRosterCoreDataStorage *xmppRosterStorage;
下面来实现单例方法:
+ (HYBXMPPHelper *)shared {
static HYBXMPPHelper *sg_xmppSharedObject = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
if (!sg_xmppSharedObject) {
sg_xmppSharedObject = [[self alloc] init];
[sg_xmppSharedObject setupXmppStream];
}
});
return sg_xmppSharedObject;
}
- (void)setupXmppStream {
NSAssert(_xmppStream == nil, @"-setupXmppStream method called multiple times");
_jid = [[NSUserDefaults standardUserDefaults] objectForKey:kUserJIDKey];
_password = [[NSUserDefaults standardUserDefaults] objectForKey:kUserPasswordKey];
_xmppStream = [[XMPPStream alloc] init];
#if !TARGET_IPHONE_SIMULATOR
// 设置此行为YES,表示允许socket在后台运行
// 在模拟器上是不支持在后台运行的
_xmppStream.enableBackgroundingOnSocket = YES;
#endif
// XMPPReconnect模块会监控意外断开连接并自动重连
_xmppReconnect = [[XMPPReconnect alloc] init];
// 配置花名册并配置本地花名册储存
_xmppRosterStorage = [[XMPPRosterCoreDataStorage alloc] init];
_xmppRoster = [[XMPPRoster alloc] initWithRosterStorage:_xmppRosterStorage];
_xmppRoster.autoAcceptKnownPresenceSubscriptionRequests = YES;
_xmppRoster.autoFetchRoster = YES;
// 配置vCard存储支持,vCard模块结合vCardTempModule可下载用户Avatar
_xmppvCardStorage = [[XMPPvCardCoreDataStorage alloc] init];
_xmppvCardTempModule = [[XMPPvCardTempModule alloc] initWithvCardStorage:_xmppvCardStorage];
_xmppvCardAvatarModule = [[XMPPvCardAvatarModule alloc] initWithvCardTempModule:_xmppvCardTempModule];
// XMPP特性模块配置,用于处理复杂的哈希协议等
_xmppCapailitiesStorage = [[XMPPCapabilitiesCoreDataStorage alloc] init];
_xmppCapabilities = [[XMPPCapabilities alloc] initWithCapabilitiesStorage:_xmppCapailitiesStorage];
_xmppCapabilities.autoFetchHashedCapabilities = YES;
_xmppCapabilities.autoFetchNonHashedCapabilities = NO;
// 激活XMPP stream
[_xmppReconnect activate:_xmppStream];
[_xmppRoster activate:_xmppStream];
[_xmppvCardTempModule activate:_xmppStream];
[_xmppvCardAvatarModule activate:_xmppStream];
[_xmppCapabilities activate:_xmppStream];
// 消息相关
_xmppMessageStorage = [[XMPPMessageArchivingCoreDataStorage alloc] init];
_xmppMessageArchiving = [[XMPPMessageArchiving alloc] initWithMessageArchivingStorage:_xmppMessageStorage];
[_xmppMessageArchiving setClientSideMessageArchivingOnly:YES];
[_xmppMessageArchiving activate:_xmppStream];
// 添加代理
[_xmppStream addDelegate:self delegateQueue:dispatch_get_main_queue()];
[_xmppRoster addDelegate:self delegateQueue:dispatch_get_main_queue()];
[_xmppMessageArchiving addDelegate:self delegateQueue:dispatch_get_main_queue()];
}
在应用将要终止时,释放资源等:
- (void)destroyResources {
[_xmppStream removeDelegate:self];
[_xmppRoster removeDelegate:self];
[_xmppReconnect deactivate];
[_xmppRoster deactivate];
[_xmppvCardTempModule deactivate];
[_xmppvCardAvatarModule deactivate];
[_xmppCapabilities deactivate];
[_xmppMessageArchiving deactivate];
[_xmppStream disconnect];
_xmppStream = nil;
_xmppRoster = nil;
_xmppReconnect = nil;
_xmppRosterStorage = nil;
_xmppvCardAvatarModule = nil;
_xmppvCardStorage = nil;
_xmppvCardTempModule = nil;
_xmppCapabilities = nil;
_xmppCapailitiesStorage = nil;
_xmppMessageStorage = nil;
_xmppMessageArchiving = nil;
}
调用只在appdelegate中调用:
- (void)applicationWillTerminate:(UIApplication *)application {
[[HYBXMPPHelper shared] destroyResources];
}
接下来就是需要实现代理方法。
先对外公开一个注册API,然后实现:
- (void)registerWithJid:(NSString *)jidString
password:(NSString *)password
completion:(HYBCompletionBlock)completion {
_isLogining = NO;
if (_isRegistering) {
return;
}
if (jidString == nil || password == nil) {
if (completion) {
completion(NO, @"用户名或者密码不能为空");
}
return;
}
_jid = jidString;
_password = password;
self.completionBlock = completion;
_isRegistering = YES;
if (![_xmppStream isConnected]) {
if (![self connect]) {
_isRegistering = NO;
if (completion) {
completion(NO, @"连接服务器失败");
}
}
return;
}
[self registerWithJid:jidString];
}
- (void)registerWithJid:(NSString *)jidString {
if (![jidString hasSuffix:kServer]) {
jidString = [NSString stringWithFormat:@"%@@%@", jidString, kServer];
}
[_xmppStream setMyJID:[XMPPJID jidWithString:jidString]];
// 设置服务器
[_xmppStream setHostName:kServer];
NSError *error = nil;
_isRegistering = YES;
if (![_xmppStream registerWithPassword:_password error:&error]) {
NSLog(@"注册账号失败:%@", [error description]);
_isRegistering = NO;
if (self.completionBlock) {
self.completionBlock(NO, [error description]);
}
}
}
如果没有连接服务器,就需要先连接服务器:
- (BOOL)connect {
if (![_xmppStream isDisconnected]) {
return NO;
}
if (_jid == nil || _password == nil) {
return NO;
}
[_xmppStream setMyJID:[XMPPJID jidWithString:_jid]];
[_xmppStream setHostName:kServer];
// 连接
NSError *error = nil;
if (![_xmppStream connectWithTimeout:XMPPStreamTimeoutNone error:&error]) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Error connecting"
message:@"See console for error details."
delegate:nil
cancelButtonTitle:@"Ok"
otherButtonTitles:nil];
[alertView show];
NSLog(@"Error connecting: %@", [error description]);
return NO;
}
return YES;
}
然后就是注册成功与失败的代理了:
// @begin
// 注册相关
- (void)xmppStreamDidRegister:(XMPPStream *)sender {
NSLog(@"注册成功");
if (self.completionBlock && _isRegistering) {
self.completionBlock(YES, nil);
}
_isRegistering = NO;
}
- (void)xmppStream:(XMPPStream *)sender didNotRegister:(DDXMLElement *)error {
/*
<iq xmlns="jabber:client" type="error" to="127.0.0.1/347f0596">
<query xmlns="jabber:iq:register">
<username>test</username>
<password>test</password>
</query>
<error code="409" type="cancel">
<conflict xmlns="urn:ietf:params:xml:ns:xmpp-stanzas"></conflict>
</error>
</iq>
*/
NSLog(@"注册失败: %@", error);
DDXMLElement *query = [error elementForName:@"query"];
DDXMLElement *username = [query elementForName:@"username"];
DDXMLElement *password = [query elementForName:@"password"];
DDXMLElement *errorNode = [error elementForName:@"error"];
int code = [errorNode attributeIntValueForName:@"code"];
NSLog(@"%@ %@", [username stringValue], [password stringValue]);
if (self.completionBlock && _isRegistering && !_isLogining) {
self.completionBlock(NO, [self errorMessageWithErrorCode:code]);
}
_isRegistering = NO;
}
// @end
下面看看登录:
1、先对外公开一个登录API:
- (void)loginWithJid:(NSString *)jidString
password:(NSString *)password
completion:(HYBCompletionBlock)completion {
_isRegistering = NO;
if (_xmppStream.isAuthenticated) {// 已经登录了,就无须再登录
if (completion) {
completion(YES, nil);
}
return;
}
if (_isLogining) {
return;
}
if (jidString == nil || password == nil) {
if (completion) {
completion(NO, @"用户名或者密码不能为空");
}
return;
}
_jid = jidString;
_password = password;
self.completionBlock = completion;
_isLogining = YES;
if (![_xmppStream isConnected]) {
if (![self connect]) {
_isLogining = NO;
if (completion) {
completion(NO, @"连接服务器失败");
}
}
return;
}
[self loginWithJid:jidString];
}
- (void)loginWithJid:(NSString *)jidString {
if (![jidString hasSuffix:kServer]) {
jidString = [NSString stringWithFormat:@"%@@%@", jidString, kServer];
}
[_xmppStream setMyJID:[XMPPJID jidWithString:jidString]];
// 设置服务器
[_xmppStream setHostName:kServer];
NSError *error = nil;
_isLogining = YES;
if (![_xmppStream authenticateWithPassword:_password error:&error]) {
NSLog(@"登录失败:%@", [error description]);
_isLogining = NO;
if (self.completionBlock) {
self.completionBlock(NO, [error description]);
}
}
}
然后会在代理中回调:
// @begin
// 登录相关
- (void)xmppStreamDidAuthenticate:(XMPPStream *)sender {
if (_isRegistering) {
[self registerWithJid:_jid];
return;
}
// 登录成功
if (self.completionBlock && _isLogining) {
self.completionBlock(YES, nil);
}
NSLog(@"密码校验成功,用户将要上线");
_isLogining = NO;
[self goOnline];
}
- (void)xmppStream:(XMPPStream *)sender didNotAuthenticate:(DDXMLElement *)error {
if (_isRegistering) {
[self registerWithJid:_jid];
return;
}
if (_isLogining) {
[self loginWithJid:_jid];
return;
}
_isLogining = NO;
NSLog(@"didNotAuthenticate :密码校验失败,登录不成功,原因是:%@", [error XMLString]);
}
// @end
当登录成功后,发送 我上线的 通知给我的好友们:
- (void)goOnline {
// 获取现场节点,默认type = "available"
XMPPPresence *presence = [XMPPPresence presence];
NSString *domain = [_xmppStream.myJID domain];
// Google set their presence priority to 24, so we do the same to be compatible.
if ([domain isEqualToString:@"gmail.com"]
|| [domain isEqualToString:@"gtalk.com"]
|| [domain isEqualToString:@"talk.google.com"]) {
NSXMLElement *priority = [NSXMLElement elementWithName:@"priority"
stringValue:@"24"];
[presence addChild:priority];
}
// 发送下线信息
[_xmppStream sendElement:presence];
}
到此就完成了登录、注册功能了!!!