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

转载于:https://www.cnblogs.com/guaishushu/p/4979019.html

XMPP即时通讯(代码实现)相关推荐

  1. Java聊天室程序源码 Java即时通讯代码 Java局域网聊天系统 Java即时通讯 Java聊天系统

    Java聊天室程序源码 Java即时通讯代码 Java局域网聊天系统  Java即时通讯 Java聊天系统 public Swingtest002() {// 设置标题setTitle("请 ...

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

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

  3. XMPP - 即时通讯技术

    XMPP-即时通讯技术简介 (IM -- Instant Messaging)支持用户在线实时交谈.如果要发送一条信息,用户需要打开一个小窗口,以便让用户及其朋友在其中输入信息并让交谈双方都看到交谈的 ...

  4. 项目开发--------XMPP即时通讯

    一.基本框架结构: StroyBoard的基本页面搭建: 二.个功能代码块的是实现 LoginViewController.m文件(登录页面的基本配置) #import "LoginView ...

  5. java xmpp即时通讯_基于XMPP协议即时通讯工具开发总结

    一.概要 转眼毕业了,毕业设计的课题是"基于XMPP协议的通讯工具",开发平台式android,实现了基本的离线消息,文字聊天,表情聊天,文件传输,语音聊天的功能. 本文主要介绍开 ...

  6. 利用Swoft实现PHP+websocket直播,即时通讯代码

    PHP.swoft 框架部分 用swoft框架做webSocket服务器 linux 环境 一.推荐用 composer 安装 swoft 框架. 这里只做概述不讲详细安装步骤 composer cr ...

  7. Mac下使用XMPP即时通讯【2】:安装XMPP和Spark

    转载自:http://www.cnblogs.com/maxinliang/p/3582924.html 一.下载并安装openfire 1.下载最新的openfire安装文件 官方下载站点:http ...

  8. 即时通讯项目 java版本qq (含服务器和客户端)源码_即时通讯安卓-QQ互联网和即时通讯云,如何实现即时通讯,这是Android还是Java...

    Android是系统平台. 应用程序所做的是应用程序的开发和完成 也就是说,通信是网络通信,但在手机环境中,网络的情况更复杂,所以我们必须做好结构 安卓即时通讯. 怎么做?安卓版TT即时通讯排行. A ...

  9. Android smack+ejabberd+spack 实现即时通讯(二)客户端连接

    这个是写客户端连接服务端的内容  我们这里使用的封装好的jar包 samck来完成xmpp即时通讯 //如有不了解xmpp请看 http://blog.csdn.net/lnb333666/artic ...

  10. Openfire XMPP Smack RTC IM 即时通讯 聊天 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

最新文章

  1. PostgreSQL 10.1 手册_部分 II. SQL 语言_第 12 章 全文搜索_12.9. GIN 和 GiST 索引类型
  2. RHEL6搭建本地yum源
  3. C#通过WMI的wind32 的API函数实现msinfo32的本地和远程计算机的系统摘要信息查看功能...
  4. 清除绊脚石(不定期更新)
  5. 日志服务(Log service)4月控制台更新指南
  6. oracle0raD,在Radhat 5 上安装Oracle 10g(转)
  7. ios开发之.pch文件的使用
  8. 间接寻址级别不同_被遗忘的利息税,国债与存款的利率区别,同大额存单的4点大不同...
  9. MIX08,迎来Silverlight2的新时代
  10. 为什么要简化代码书写
  11. Linux下安装gcc报错的情况解决方案
  12. NLP基础笔记1——中文分词技术
  13. 算24点的一般方法及例题
  14. GICv3软件overview手册之虚拟化
  15. 【word2vec】算法原理 公式推导
  16. 麒麟子Javascript游戏编程零基础教程一:序言
  17. CST(Crypto Systems Toolkit) 7.1学习笔记-chapter6
  18. 无线IPPBX系统的设计与实现
  19. 数据库间数据迁移常见工具和方法
  20. 敏捷 橄榄球运动_澳大利亚橄榄球迷的研究声称南非裁判的偏见被证明是错误的

热门文章

  1. [RK3399][Android7.1] 调试笔记 --- 播放音乐没有声音
  2. 高强度加密vep文件提取MP4方法
  3. 逻辑回归:详细建模流程与例子代码
  4. 学术会议/期刊论文撤稿信的模板
  5. 揭秘黑客攻击内幕和20个黑客相关术语
  6. 对于高级搜索部分的要求
  7. SCSI 设备热插拔
  8. python高级语言特长_高级程序设计语言的特点是()
  9. 如何修复win7蓝牙服务器,快速解决win7系统蓝牙驱动的修复方法
  10. ip 纯真数据库查找