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:messageArchingCoreStoragedispatchQueue: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.userpreferredStyle: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:selfaction:@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,[XMPPManagerdefaultXMPPManager].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

XMPP即时通讯(代码实现)的更多相关文章

  1. XMPP即时通讯资料记录

    几天开始研究XMPP即时通讯的技术,来实现移动应用的计时聊天功能.记录下参考的博客地址,还挺详细的. http://blog.csdn.net/fhbystudy/article/details/16 ...

  2. XMPP即时通讯

    XMPP:XMPP是基于XML的点对点通讯协议,The Extensible Messaging and Presence Protocol(可扩展通讯和表示协议). XMPP可用于服务类实时通讯,表 ...

  3. iOS开发之XMPP即时通讯简单实现

    首先搭载服务器和数据库 搭载服务器我用的是openfire,数据库用的是mysql 这里推荐两个链接 配置mysql,用的是mysql workbench http://justsee.iteye.c ...

  4. XMPP即时通讯基础知识

    XMPP参考 一.定义 XMPP 是一种很类似于http协议的一种数据传输协议,它的过程就如同“解包装--〉包装”的过程,用户只需要明白它接受的类型,并理解它返回的类型,就可以很好的利用xmpp来进行 ...

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

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

  6. iOS中 XMPP即时通讯实现的主要步骤

    这里只是列出实现的只要步骤,不是全部代码. 首先导入XMPPFramework,及相关配置,完成后开始. 创建一个XMPPHelper  类来管理要进行的操作. XMPPHelper.h文件如下 ty ...

  7. XMPP即时通讯协议使用(前传)——协议详解

    XMPP详解 XMPP(eXtensible Messaging and Presence Protocol,可扩展消息处理和现场协议)是一种在两个地点间传递小型结构化数据的协议.在此基础上,XMPP ...

  8. XMPP即时通讯协议使用(七)——利用Strophe实现WebIM及strophe.plugins插件使用

    Strophe简介与Openfire配置 Strophe.js是为XMPP写的一个js类库.因为http协议本身不能实现持久连接,所以strophe利用BOSH模拟实现持久连接. 官方文档: http ...

  9. XMPP即时通讯协议使用(六)——开发Openfire聊天记录插件

    转载地址:http://www.cnblogs.com/hoojo/archive/2013/03/29/openfire_plugin_chatlogs_plugin_.html 开发环境: Sys ...

随机推荐

  1. 转载 深入理解JavaScript中的this关键字

    转载原地址: http://www.cnblogs.com/rainman/archive/2009/05/03/1448392.html 深入理解JavaScript中的this关键字   1. 一 ...

  2. 转载Jquery中的三种$()

    1.$()可以是$(expresion),即css选择器.Xpath或html元素,也就是通过上述表达式来匹配目标元素. 比如:$("a")构造的这个对象,是用CSS选择器构建了一 ...

  3. URL- 含义及组成

    URL (uniform resource locator) : 互联网的每个网页都有自己唯一的统一资源定位器,由3部分组成:通信协议,主机名,资源名. HTTP(hypertext transfer ...

  4. Web- HTML网页颜色大全

    按色相的搭配分类 列举一些最为代表常见的颜色值,让你能快速的找到自己想要的代码 红色 #FFFFCC#CCFFFF#FFCCCC #99CCCC#FFCC99#FFCCCC #FF9999#99669 ...

  5. AVAudioRecorder、AVAudioPlayer录音及播放

    #pragma mark - 设置录制的音频文件的位置 - (NSString *)audioRecordingPath{ NSString *str_date=[TimeTransform Date ...

  6. java大数总结【转】

    java大数(2013长春网络赛)--hdu4762总结一下:1.java提交类要写Main.2.读取大数. Scanner read=new Scanner(System.in); BigInteg ...

  7. nginx 调优

    般来说nginx配置文件中对优化比较有作用的为以下几项:worker_processes 8;1 nginx进程数,建议按照cpu数目来指定,一般为它的倍数.worker_cpu_affinity 0 ...

  8. TQ210裸机编程(3)——按键(查询法)

    首先查看TQ210的底板原理图 这次编程只操作KEY1和KEY2,在TQ210核心板原理图中搜索XEINT0 可以看出KEY1和KEY2分别接在S5PV210的GPH0_0和GPH0_1引脚. 这次编 ...

  9. MySQL和PostgreSQL 导入数据对照

    在虚拟机上測评了下MySQL 和 PostgreSQL 的各种LOAD FILE方式以及时间. 由于是虚拟机上的測评,所以时间仅仅做參考,不要太较真, 看看就好了.MySQL 工具:    1. 自带 ...

  10. HDU 4259 - Double Dealing(求循环节)

    首先将扑克牌进行一次置换,然后分解出所有的循环节,所有循环节的扑克牌个数的最小公倍数即为答案 #include <stdio.h> #include <string.h> #i ...