XMPP(2)

关于XMPP最权威的讲解:http://www.jabbercn.org/RFC3920(这个才是最权威的,下面文章只是博客中)

什么是OpenFire

Openfire 采用Java开发,开源的实时协作(RTC)服务器基于XMPP(Jabber)协议。

您可以使用它轻易的构建高效率的即时通信服务器。Openfire安装和使用都非常简单,并利用Web进行管理。单台服务器可支持上万并发用户。

由于是采用开放的XMPP协议,您可以使用各种支持XMPP协议的IM客户端软件登陆服务。

XMPP(Jabber)协议

1、 介绍

XMPP是一种基于XML的协议,它继承了在XML环境中灵活的扩展性。因此,基于XMPP的应用具有超强的可扩展性。经过扩展以后的XMPP可以通过发 送扩展的信息来处理用户的需求,以及在XMPP的顶端建立如内容发布系统和基于地址的服务等应用程 序。而且,XMPP包含了针对服务器端的软件协议,使之能与另一个进行通话,这使得开发者更容易建立客户应用程序或给一个配好系统添加功能。

2、 定义:

XMPP(可扩展消息处理现场协议)是基于可扩展标记语言(XML)的协议,它用于即时消息(IM)以及在线现场探测。它在促进服务器之间的准即时操作。这个协议可能最终允许因特网用户向因特网上的其他任何人发送即时消息,即使其操作系统和浏览器不同。

XMPP的前身是Jabber, 一个开源形式组织产生的网络即时通信协议。XMPP目前被IETF国际标准组织完成了标准化工作。标准化的核心结果分为两部分;

1、核心的XML流传输协议

基于XML FreeEIM流传输的即时通讯扩展应用

XMPP的核心XML流传输协议的定义使得XMPP能够在一个比以往网络通信协议更规范的平台上。借助于XML易于解析和阅读的特性,使得XMPP的协议能够非常漂亮。

2、XMPP的即时通讯扩展应用

这部分是根据IETF在这之前对即时通讯的一个抽象定义的,与其他业已得到广泛使用的即时通讯协议,诸如AIM,QQ等有功能完整,完善等先进性。

在IETF 中,把IM协议划分为四种协议,即时信息和出席协议(Instant Messaging and Presence Protocol, IMPP)、出席和即时信息协议(Presence and Instant Messaging Protocol, PRIM)、针对即时信息和出席扩展的会话发起协议(Session Initiation Protocol for Instant Messaging and Presence Leveraging Extensions, SIMPLE),以及可扩展的消息出席协议(XMPP)。最初研发IMPP 也是为了创建一种标准化的协议,但是今天,IMPP 已经发展成为基本协议单元,定义所有即时通信协议应该支持的核心功能集。

3、 XMPP协议的优点

a. XMPP 协议是公开的,由JSF开源社区组织开发的。XMPP 协议并不属于任何的机构和个人,而是属于整个社区,这一点从根本上保证了其开放性。

b. XMPP 协议具有良好的扩展性。在XMPP 中,即时消息和到场信息都是基于XML 的结构化信息,这些信息以XML 节(XML Stanza)的形式在通信实体间交换。XMPP 发挥了XML 结构化数据的通用传输层的作用,它将出席和上下文敏感信息嵌入到XML 结构化数据中,从而使数据以极高的效率传送给最合适的资源。基于XML 建立起来的应用具有良好的语义完整性和扩展性。

c. 分布式的网络架构。XMPP 协议都是基于Client/Server 架构,但是XMPP协议本身并没有这样的限制。网络的架构和电子邮件十分相似,但没有结合任何特定的网络架构,适用范围非常广泛。

d. XMPP 具有很好的弹性。XMPP 除了可用在即时通信的应用程序,还能用在网络管理、内容供稿、协同工具、档案共享、游戏、远端系统监控等。

e. 安全性。XMPP在Client-to-Server通信,和Server-to-Server通信中都使用TLS (Transport Layer Security)协议作为通信通道的加密方法,保证通信的安全。任何XMPP服务器可以独立于公众XMPP网络(例如在企业内部网络中),而使用 SASL及TLS等技术更加增强了通信的安全性。如下图所示:

4、 XMPP协议的组成

主要的XMPP 协议范本及当今应用很广的XMPP 扩展:

l RFC 3920 XMPP(新的RFC6120):核心。定义了XMPP 协议框架下应用的网络架构,引入了XML Stream(XML 流)与XML Stanza(XML 节),并规定XMPP 协议在通信过程中使用的XML 标签。使用XML 标签从根本上说是协议开放性与扩展性的需要。此外,在通信的安全方面,把TLS 安全传输机制与SASL 认证机制引入到内核,与XMPP 进行无缝的连接,为协议的安全性、可靠性奠定了基础。Core 文档还规定了错误的定义及处理、XML 的使用规范、JID(Jabber Identifier,Jabber 标识符)的定义、命名规范等等。所以这是所有基于XMPP 协议的应用都必需支持的文档。

l RFC 3921:用户成功登陆到服务器之后,发布更新自己的在线好友管理、发送即时聊天消息等业务。所有的这些业务都是通过三种基本的XML 节来完成的:IQ Stanza(IQ 节), Presence Stanza(Presence 节), Message Stanza(Message 节)。RFC3921 还对阻塞策略进行了定义,定义是多种阻塞方式。可以说,RFC3921 是RFC3920 的充分补充。两个文档结合起来,就形成了一个基本的即时通信协议平台,在这个平台上可以开发出各种各样的应用。

l XEP-0030 服务搜索。一个强大的用来测定XMPP 网络中的其它实体所支持特性的协议。

l XEP-0115 实体性能。XEP-0030 的一个通过即时出席的定制,可以实时改变交变广告功能。

l XEP-0045 多人聊天。一组定义参与和管理多用户聊天室的协议,类似于Internet 的Relay Chat,具有很高的安全性。

l XEP-0096 文件传输。定义了从一个XMPP 实体到另一个的文件传输。

l XEP-0124 HTTP 绑定。将XMPP 绑定到HTTP 而不是TCP,主要用于不能够持久的维持与服务器TCP 连接的设备。

l XEP-0166 Jingle。规定了多媒体通信协商的整体架构。

l XEP-0167 Jingle Audio Content Description Format。定义了从一个XMPP 实体到另一个的语音传输过程。

l XEP-0176 Jingle ICE(Interactive Connectivity Establishment)Transport。ICE传输机制,文件解决了如何让防火墙或是NAT(Network Address Translation)保护下的实体建立连接的问题。

l XEP-0177 Jingle Raw UDP Transport。纯UDP 传输机制,文件讲述了如何在没有防火墙且在同一网络下建立连接的。

l XEP-0180 Jingle Video Content Description Format。定义了从一个XMPP 实体到另一个的视频传输过程。

l XEP-0181 Jingle DTMF(Dual Tone Multi-Frequency)。

l XEP-0183 Jingle Telepathy Transport Method。

5、 XMPP协议网络架构

XMPP是一个典型的C/S架构,而不是像大多数即时通讯软件一样,使用P2P客户端到客户端的架构,也就是说在大多数情况下,当两个客户端进行通讯时, 他们的消息都是通过服务器传递的(也有例外,例如在两个客户端传输文件时).采用这种架构,主要是为了简化客户端,将大多数工作放在服务器端进行,这样, 客户端的工作就比较简单,而且,当增加功能时,多数是在服务器端进行.XMPP服务的框架结构如下图所示.XMPP中定义了三个角色,XMPP客户 端,XMPP服务器、网关.通信能够在这三者的任意两个之间双向发生.服务器同时承担了客户端信息记录、连接管理和信息的路由功能.网关承担着与异构即时 通信系统的互联互通,异构系统可以包括SMS(短信)、MSN、ICQ等.基本的网络形式是单客户端通过TCP/IP连接到单服务器,然后在之上传输 XML,工作原理是:

(1) 点连接到服务器;

(2)服务器利用本地目录系统中的证书对其认证;

(3) 点指定目标地址,让服务器告知目标状态;

(4) 务器查找、连接并进行相互认证;

(5) 点之间进行交互;

6、 XMPP客户端

XMPP 系统的一个设计标准是必须支持简单的客户端。事实上,XMPP 系统架构对客户端只有很少的几个限制。一个XMPP 客户端必须支持的功能有:

1. 通过 TCP 套接字与XMPP 服务器进行通信;

2. 解析组织好的 XML 信息包;

3. 理解消息数据类型。

XMPP 将复杂性从客户端转移到服务器端。这使得客户端编写变得非常容易,更新系统功能也同样变得容易。XMPP 客户端与服务端通过XML 在TCP 套接字的5222 端口进行通信,而不需要客户端之间直接进行通信。

基本的XMPP 客户端必须实现以下标准协议(XEP-0211):

RFC3920 核心协议Core

RFC3921 即时消息和出席协议Instant Messaging and Presence

XEP-0030 服务发现Service Discovery

XEP-0115 实体能力Entity Capabilities

7、 XMPP服务器

XMPP 服务器遵循两个主要法则:

1、监听客户端连接,并直接与客户端应用程序通信;

2、与其他 XMPP 服务器通信;

XMPP开源服务器一般被设计成模块化,由各个不同的代码包构成,这些代码包分别处理Session管理、用户和服务器之间的通信、服务器之间的通信、 DNS(Domain Name System)转换、存储用户的个人信息和朋友名单、保留用户在下线时收到的信息、用户注册、用户的身份和权限认证、根据用户的要求过滤信息和系统记录 等。另外,服务器可以通过附加服务来进行扩展,如完整的安全策略,允许服务器组件的连接或客户端选择,通向其他消息系统的网关。

基本的XMPP 服务器必须实现以下标准协议

RFC3920 核心协议Core

RFC3921 即时消息和出席协议Instant Messaging and Presence

XEP-0030 服务发现Service Discovery

8、 XMPP网关

XMPP 突出的特点是可以和其他即时通信系统交换信息和用户在线状况。由于协议不同,XMPP 和其他系统交换信息必须通过协议的转换来实现,目前几种主流即时通信协议都没有公开,所以XMPP 服务器本身并没有实现和其他协议的转换,但它的架构允许转换的实现。实现这个特殊功能的服务端在XMPP 架构里叫做网关(gateway)。目前,XMPP 实现了和AIM、ICQ、IRC、MSN Massager、RSS0.9 和Yahoo Massager 的协议转换。由于网关的存在,XMPP 架构事实上兼容所有其他即时通信网络,这无疑大大提高了XMPP 的灵活性和可扩展性。

9、 XMPP地址格式

一个实体在XMPP网络结构中被称为一个接点,它有唯一的标示符jabber identifier(JID),即实体地址,用来表示一个Jabber用户,但是也可以表示其他内容,例如一个聊天室.一个有效的JID包括一系列元素:

(1) 名(domain identifier);

(2) 点(node identifier);

(3) 源(resource identifier).

它的格式是node@domain/resource,node@domain,类似电子邮件的地址格式.resource用来表示接点不同的设备或位置,这 个是可选的,例如a在Server1上注册了一个用户,用户名为doom,那么a的JID就是doom@serverl,在发送消息时,指明 doom@serverl就可以了,resource可以不用指定,但a在登录到这个Server时,fl的JID可能是doom@serverl/exodus(如果a用Exodus软件登录),也可能是[email=doom@serverl/psi]    doom@serverl /psi[/email](如果a用psi软件登录).资源只用来识别属于用户的位置或设备等,一个用户可以同时以多种资源与同一个XMPP服务器连接。

10、 XMPP消息格式

XMPP中定义了3个顶层XML元素: Message、Presence、IQ,下面针对这三种元素进行介绍。

用于在两个jabber用户之间发送信息。Jsm(jabber会话管理器)负责满足所有的消息,不管目标用户的状态如何。如果用户在线jsm立即提交;否则jsm就存储。

To : 标识消息的接收方。

from : 指发送方的名字或标示(id)

Text: 此元素包含了要提交给目标用户的信息。

结构如下所示:

你好,在忙吗

用来表明用户的状态,如:online、away、dnd(请勿打扰)等。当用户离线或改变自己的状态时,就会在stream的上下文中插入一个Presence元素,来表明自身的状态.结构如下所示:

jabber.com/contact’To = ‘yaoman @jabber.com/contact'>

Online

元素可以取下面几种值:

Probe: 用于向接受消息方发送特殊的请求

subscribe: 当接受方状态改变时,自动向发送方发送presence信息。

< IQ >

一种请求/响应机制:从一个实体向另一个发送请求,另外一个实体接受请求,并进行响应.例如,client在stream的上下文中插入一个元素,向Server请求得到自己的好友列表,Server返回一个,里面是请求的结果.

主要的属性是type。包括:

Get :获取当前域值。

Set :设置或替换get查询的值。

Result :说明成功的响应了先前的查询。

Error: 查询和响应中出现的错误。

结构如下所示:

jabber.com/contact’id=’1364564666’Type=’result’>

XMPP通信协议

一、 Stream


http://etherx.jabber.org/streams"

xmlns="jabber:client" from="127.0.0.1" id="e38900bc" xml:lang="en"

version="1.0">


xmlns 表示通信客户端

from 客户端的地址(来源)

id

lang 通信语言

-->



DIGEST-MD5

PLAIN

ANONYMOUS

CRAM-MD5


http://jabber.org/features/compress">

zlib


http://jabber.org/features/iq-auth" />


http://jabber.org/features/iq-register" />

关于TSL 参考:http://www.jabbercn.org/RFC3920

1、TSL协议遵循以下规则:

A、 一个遵守本协议的初始化实体必须(MUST)在初始化流的头信息中包含一个'version'属性并把值设为“1.0”。

B、 如果TLS握手发生在两个服务器之间,除非服务器声称的DNS主机名已经被解析,通信不能(MUST NOT)继续进行。

C、 当一个遵守本协议的接收实体接收了一个初始化流(它的头信息中包含一个'version'属性并且值设为“1.0”),在发送应答流的的头信息(其中包含 版本标记)之后,它必须发送(MUST)元素(名字空间为 'urn:ietf:params:xml:ns:xmpp-tls')以及其他它支持的流特性。

D、 如果初始化实体选择使用TLS,TLS握手必须在SASL握手之前完成;这个顺序用于帮助保护SASL握手时发送的认证信息的安全,同时可以在必要的时候在TLS握手之前为SASL外部机制提供证书。

E、 TLS握手期间,一个实体不能(MUST NOT)在流的根元素中发送任何空格符号作为元素的分隔符(在下面的TLS示例中的任何空格符都仅仅是为了便于阅读);这个禁令用来帮助确保安全层字节精度。

F、 接收实体必须(MUST)在发送 元素的关闭符号">" 之后立刻开始TLS协商。初始化实体必须(MUST)在从接收实体接收到 元素的关闭符号">" 之后立刻开始TLS协商。

G、 初始化实体必须(MUST)验证接收实体出示的证书;关于证书验证流程参见Certificate Validation ( 第十四章第二节)。

H、 证书必须(MUST)检查初始化实体(比如一个用户)提供的主机名;而不是通过DNS系统解析出来的主机名;例如,如果用户指定一个主机 名"example.com"而一个DNS SRV [SRV]查询返回"im.example.com",证书必须(MUST)检查"example.com".如果任何种类的XMPP实体(例如客户端或 服务器)的JID出现在一个证书里,它必须(MUST)表现为一个别名实体里面的UTF8字符串,存在于subjectAltName之中。如何使用 [ASN.1] 对象标识符 "id-on-xmppAddr" 定义在本文的第五章第一节第一小节。

I、 如果 TLS 握手成功了,接收实体必须(MUST) 丢弃TLS 生效之前从初始化实体得到的任何不可靠的信息

J、 如果 TLS 握手成功了,初始化实体必须(MUST) 丢弃TLS 生效之前从接收实体得到的任何不可靠的信息

K、 如果 TLS 握手成功了,接收实体不能(MUST NOT)在流重新开始的时候通过提供其他的流特性来向初始化实体提供 STARTTLS 扩展

L、 如果 TLS 握手成功了,初始化实体必须(MUST)继续进行SASL握手

M、 如果 TLS 握手失败了,接收实体必须(MUST)终止XML流和相应的TCP连接。

N、 关于必须(MUST)支持的机制,参照 Mandatory-to-Implement Technologies (第十四章第七节) 。

2、当一个初始化实体用TLS保护一个和接收实体之间的流,其步骤如下:

A. 初始化实体打开一个TCP连接,发送一个打开的XML流头信息(其'version'属性设置为"1.0")给接收实体以初始化一个流。

B. 接收实体打开一个TCP连接,发送一个XML流头信息(其'version'属性设置为"1.0")给初始化实体作为应答。

C. 接收实体向初始化实体提议STARTTLS范围(包括其他支持的流特性),如果TLS对于和接收实体交互是必需的,它应该(SHOULD)在元素中包含子元素

D. 初始化实体发出STARTTLS命令(例如, 一个符合'urn:ietf:params:xml:ns:xmpp-tls'名字空间的 元素) 以通知接收实体它希望开始一个TLS握手来保护流。

E. 接收实体必须(MUST)以'urn:ietf:params:xml:ns:xmpp-tls'名字空间中的元素 或元素应答。如果失败,接收实体必须(MUST)终止XML流和相应的TCP连接。如果继续进行,接收实体必须 (MUST)尝试通过TCP连接完成TLS握手并且在TLS握手完成之前不能(MUST NOT)发送任何其他XML数据。

F. 初始化实体和接收实体尝试完成TLS握手。(要符合[TLS]规范)

G. 如果 TLS 握手不成功, 接收实体必须(MUST)终止 TCP 连接. 如果 TLS 握手成功, 初始化实体必须(MUST)发送给接收实体一个打开的XML流头信息来初始化一个新的流(先发送一个关闭标签是不必要的, 因为接收实体和初始化实体必须(MUST)确保原来的流在TLS握手成功之后被关闭) 。

H. 在从初始化实体收到新的流头信息之后,接收实体必须(MUST)发送一个新的XML流头信息给初始化实体作为应答,其中应包含可用的特性但不包含STATRTTLS特性。

http://www.cnblogs.com/hoojo/archive/2012/06/18/2553975.html

在文章开始,请你了解和熟悉openfire方面的相关知识,这样对你理解下面代码以及下面代码的用途有很好的了解。同时,你可能需要安装一个简单的CS聊天工具,来测试你的代码是否成功的在openfire服务器上建立会话链接,并成功的向在线用户发送聊天消息。

必须了解:http://www.cnblogs.com/hoojo/archive/2012/05/17/2506769.html

http://www.cnblogs.com/hoojo/archive/2012/05/13/2498151.html(非windows 系统)

可选:http://www.cnblogs.com/hoojo/archive/2012/05/17/2506845.html

http://www.cnblogs.com/hoojo/archive/2012/06/18/2553975.html

聊天软件Spark,用于测试聊天消息发送是否成功,下载地址:http://www.igniterealtime.org/do... ark/spark_2_6_3.exe

然后你需要添加smack相关的jar包

smack.jar

smackx.jar

jar包下载地址:http://www.igniterealtime.org/do... ack/smack_3_2_2.zip

代码中还用到了junit,junit jar下载地址:http://ebr.springsource.com/repo... 8.2&type=binary

下面开始代码部分

package com.hoo.smack;

import java.util.Collection;

import java.util.Iterator;

import javax.net.SocketFactory;

import org.jivesoftware.smack.AccountManager;

import org.jivesoftware.smack.Chat;

import org.jivesoftware.smack.ChatManager;

import org.jivesoftware.smack.Connection;

import org.jivesoftware.smack.ConnectionConfiguration;

import org.jivesoftware.smack.MessageListener;

import org.jivesoftware.smack.Roster;

import org.jivesoftware.smack.RosterEntry;

import org.jivesoftware.smack.XMPPConnection;

import org.jivesoftware.smack.XMPPException;

import org.jivesoftware.smack.packet.Message;

import org.jivesoftware.smack.packet.Presence;

import org.jivesoftware.smack.packet.Session;

import org.jivesoftware.smack.packet.Message.Type;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

/**

* function: 利用Smack框架完成 XMPP 协议通信

* @author hoojo

* @createDate 2012-5-22 上午10:28:18

* @file ConnectionServerTest.java

* @package com.hoo.smack.conn

* @project jwchat

* @bloghttp://blog.csdn.net/IBM_hoojo

* @emailhoojo_@126.com

* @version 1.0

*/

public class SmackXMPPTest {

private Connection connection;

private ConnectionConfiguration config;

/** openfire服务器address */

private final static String server = "192.168.8.32";

private final void fail(Object o) {

if (o != null) {

System.out.println(o);

}

}

private final void fail(Object o, Object... args) {

if (o != null && args != null && args.length > 0) {

String s = o.toString();

for (int i = 0; i < args.length; i++) {

String item = args == null ? "" : args.toString();

if (s.contains("{" + i + "}")) {

s = s.replace("{" + i + "}", item);

} else {

s += " " + item;

}

}

System.out.println(s);

}

}

/**

* function: 初始Smack对openfire服务器链接的基本配置

* @author hoojo

* @createDate 2012-6-25 下午04:06:42

*/

@Before

public void init() {

try {

//connection = new XMPPConnection(server);

//connection.connect();

/** 5222是openfire服务器默认的通信端口,你可以登录http://192.168.8.32:9090/到管理员控制台查看客户端到服务器端口 */

config = new ConnectionConfiguration(server, 5222);

/** 是否启用压缩 */

config.setCompressionEnabled(true);

/** 是否启用安全验证 */

config.setSASLAuthenticationEnabled(true);

/** 是否启用调试 */

config.setDebuggerEnabled(false);

//config.setReconnectionAllowed(true);

//config.setRosterLoadedAtLogin(true);

/** 创建connection链接 */

connection = new XMPPConnection(config);

/** 建立连接 */

connection.connect();

} catch (XMPPException e) {

e.printStackTrace();

}

fail(connection);

fail(connection.getConnectionID());

}

@After

public void destory() {

if (connection != null) {

connection.disconnect();

connection = null;

}

}

/**

* function: ConnectionConfiguration 的基本配置相关信息

* @author hoojo

* @createDate 2012-6-25 下午04:11:25

*/

@Test

public void testConfig() {

fail("PKCS11Library: " + config.getPKCS11Library());

fail("ServiceName: {0}", config.getServiceName());

// ssl证书密码

fail("TruststorePassword: {0}", config.getTruststorePassword());

fail("TruststorePath: {0}", config.getTruststorePath());

fail("TruststoreType: {0}", config.getTruststoreType());

SocketFactory socketFactory = config.getSocketFactory();

fail("SocketFactory: {0}", socketFactory);

/*try {

fail("createSocket: {0}", socketFactory.createSocket("localhost", 3333));

} catch (IOException e) {

e.printStackTrace();

}*/

}

/**

* function: Connection 基本方法信息

* @author hoojo

* @createDate 2012-6-25 下午04:12:04

*/

@Test

public void testConnection() {

/** 用户管理 */

AccountManager accountManager = connection.getAccountManager();

for (String attr : accountManager.getAccountAttributes()) {

fail("AccountAttribute: {0}", attr);

}

fail("AccountInstructions: {0}", accountManager.getAccountInstructions());

/** 是否链接 */

fail("isConnected:", connection.isConnected());

fail("isAnonymous:", connection.isAnonymous());

/** 是否有权限 */

fail("isAuthenticated:", connection.isAuthenticated());

fail("isSecureConnection:", connection.isSecureConnection());

/** 是否使用压缩 */

fail("isUsingCompression:", connection.isUsingCompression());

}

/**

* function: 用户管理器

* @author hoojo

* @createDate 2012-6-25 下午04:22:31

*/

@Test

public void testAccountManager() {

AccountManager accountManager = connection.getAccountManager();

for (String attr : accountManager.getAccountAttributes()) {

fail("AccountAttribute: {0}", attr);

}

fail("AccountInstructions: {0}", accountManager.getAccountInstructions());

fail("supportsAccountCreation: {0}", accountManager.supportsAccountCreation());

try {

/** 创建一个用户boy,密码为boy;你可以在管理员控制台页面http://192.168.8.32:9090/user-summary.jsp查看用户/组的相关信息,来查看是否成功创建用户 */

accountManager.createAccount("boy", "boy");

/** 修改密码 */

accountManager.changePassword("abc");

} catch (XMPPException e) {

e.printStackTrace();

}

}

@Test

public void testUser() {

try {

/** 用户登陆,用户名、密码 */

connection.login("hoojo", "hoojo");

} catch (XMPPException e) {

e.printStackTrace();

}

/** 获取当前登陆用户 */

fail("User:", connection.getUser());

/** 所有用户组 */

Roster roster = connection.getRoster();

/** 好友用户组,你可以用Spark添加用户好友,这样这里就可以查询到相关的数据 */

Collection rosterEntiry = roster.getEntries();

Iterator iter = rosterEntiry.iterator();

while (iter.hasNext()) {

RosterEntry entry = iter.next();

fail("Groups: {0}, Name: {1}, Status: {2}, Type: {3}, User: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);

}

fail("-------------------------------");

/** 未处理、验证好友,添加过的好友,没有得到对方同意 */

Collection unfiledEntries = roster.getUnfiledEntries();

iter = unfiledEntries.iterator();

while (iter.hasNext()) {

RosterEntry entry = iter.next();

fail("Groups: {0}, Name: {1}, Status: {2}, Type: {3}, User: {4}", entry.getGroups(), entry.getName(), entry.getStatus(), entry.getType(), entry);

}

}

@Test

@SuppressWarnings("static-access")

public void testPacket() {

try {

connection.login("hoojo", "hoojo");

} catch (XMPPException e) {

e.printStackTrace();

}

//Packet packet = new Data(new DataPacketExtension("jojo@" + server, 2, "this is a message"));

//connection.sendPacket(packet);

/** 更改用户状态,available=true表示在线,false表示离线,status状态签名;当你登陆后,在Spark客户端软件中就可以看到你登陆的状态 */

Presence presence = new Presence(Presence.Type.available);

presence.setStatus("Q我吧");

connection.sendPacket(presence);

Session session = new Session();

String sessid = session.nextID();

connection.sendPacket(session);

/** 向jojo@192.168.8.32 发送聊天消息,此时你需要用Spark软件登陆jojo这个用户,

* 这样代码就可以向jojo这个用户发送聊天消息,Spark登陆的jojo用户就可以接收到消息

**/

/** Type.chat 表示聊天,groupchat多人聊天,error错误,headline在线用户; */

Message message = new Message("jojo@" + server, Type.chat);

//Message message = new Message(sessid, Type.chat);

message.setBody("h!~ jojo, I'am is hoojo!");

connection.sendPacket(message);

try {

Thread.sleep(1);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

/**

* function: 测试聊天消息管理类

* @author hoojo

* @createDate 2012-6-25 下午05:03:23

*/

@Test

public void testChatManager() {

/** 设置状态 */

try {

connection.login("hoojo", "hoojo");

} catch (XMPPException e) {

e.printStackTrace();

}

/** 设置状态 */

Presence presence = new Presence(Presence.Type.available);

presence.setStatus("Q我吧");

connection.sendPacket(presence);

/** 获取当前登陆用户的聊天管理器 */

ChatManager chatManager = connection.getChatManager();

/** 为指定用户创建一个chat,MyMessageListeners用于监听对方发过来的消息  */

Chat chat = chatManager.createChat("jojo@" + server, new MyMessageListeners());

try {

/** 发送消息 */

chat.sendMessage("h!~ jojo……");

/** 用message对象发送消息 */

Message message = new Message();

message.setBody("message");

message.setProperty("color", "red");

chat.sendMessage(message);

} catch (XMPPException e) {

e.printStackTrace();

}

try {

Thread.sleep(1000 * 1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

/**

* function: 消息监听器,用户监听对方发送的消息,也可以想对方发送消息

* @author hoojo

* @createDate 2012-6-25 下午05:05:31

* @file SmackXMPPTest.java

* @package com.hoo.smack

* @project jwchat

* @bloghttp://blog.csdn.net/IBM_hoojo

* @emailhoojo_@126.com

* @version 1.0

*/

class MyMessageListeners implements MessageListener {

public void processMessage(Chat chat, Message message) {

try {

/** 发送消息 */

chat.sendMessage("dingding……" + message.getBody());

} catch (XMPPException e) {

e.printStackTrace();

}

/** 接收消息 */

fail("From: {0}, To: {1}, Type: {2}, Sub: {3}", message.getFrom(), message.getTo(), message.getType(), message.toXML());

/*Collection bodys =  message.getBodies();

for (Body body : bodys) {

fail("bodies[{0}]", body.getMessage());

}

//fail(message.getLanguage());

//fail(message.getThread());

//fail(message.getXmlns());*/

fail("body: ", message.getBody());

}

}

}

好了,这些都是smack的基本功能,还有更多的东西需要研究,下次有机会再分享!

http://www.cnblogs.com/hoojo/archive/2012/06/25/2561576.html

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容