`

Instant Messaging java(2)

    博客分类:
  • XMPP
阅读更多

IM概念和Jabber协议
从现在开始,我们将建立一个基础的程序,在以后的章节都要使用。我们将开始开发一个基本地jabber服务。以此为基础,我们在以后章节增强它的功能。
3.1一个基本的Jabber服务
如果你曾经写过服务类程序,一个基本的Jabber服务的代码设计对你来说是非常熟悉的。即使没有,也不用担心。服务类程序从根本来说是非常简单的。最大的困难和复杂度在于附加的一些特征,作为一个大规模的服务器,需要安全有效的应付多用户并发的情况。
本书中设计的Jabber服务是一个简单的可靠的以及遵循jabber协议。简单是为了我们更容易的理解和修改,因为我们感兴趣的地方以及的目的是学习典型服务器的重要特征。一旦我们明白了这些基本特征,修改和扩展是非常容易。
一个简单的jabber服务需要做三件事:
l       连接到客户端
l       读取jabber xml流
l       对客户端的输入作出反应
令人不可思议的是,我们实现这些事情只需要寥寥几个java类就可以实现。在这一节,我们建立一个基本的jabber服务实现这三个任务。
3.1.1我们的目标
在开始之前,设置一个明确的目标是非常有帮助的。目标指导我们设计和实施的准则。在系统中,我们会用到以下准则:
简单——要求事情简单纯粹是从美学观点来看的。另外,简单的软件意味着更加可靠,容易理解和易于修改。后面的两个准则是在此基础上的。保持事物的简单性,我们将会有一个好的开端。
服从标准——我们开发一个Jabber服务的目的是明白和阐明jabber协议。很明晰,我们不想为了正确的实现而失去教育的意义。现在最合适的效果是介于这之间的。
容易理解——代码必须是容易理解的。一个混乱的难以理解的代码是很难说明问题和解释他们。我们要实现的是倾斜的代码。
便于修改——虽然代码是用来学习的。你可能更加希望他用在真实世界中,在许多时候,你根据你的需要修改。我们将尽量使它易于修改,无论面对什么情况。
同时也要明确我们的软件暂时不会做的事情:安全性,伸缩性,可管理性,有效性行和运行速度。一个一般快的机器就可以应付20个人的网络系统,足够中小部门的使用。
3.1.2我们的服务代码
我们的代码将接受和处理来自客户端的连接,解析xml流,依照jabber命令发出xml流。软件的启动被严格的限制,但是当我们附加新的jabber协议支持,扩展了服务特性和功能。
有意思的是我们可以使得我们的软件实现jabber的高级功能。在第十章,他们包含了服务器安全的实现,在现有系统中嵌入jabber功能,集成其他企业资源。
3.1.3基本的服务设计
我的jabber服务被分开为三个基本模块:
Session Pool:一个jabber客户服务器会话集合。每一个会话管理一个人java.net.Socket连接和SessionID和jabberID等元数据。
XML解析:xml解析类处理输入的jabber xml数据,把他们转变成java 包对象。Xml解析类依赖于简单xmlAPI(SAX)解析。
Packet处理:服务器对输入的Packet作出反应。在多数情况下,服务简单的将Packet路由到特定的输入终端。然而许多Packet必须经过处理,生成一个或多个回复Packet。
 
把所有的Packet对象集合到packetQueue进行处理,
 
PacketQueue:这个类是关于Packet对象的基本队列集合结构。PacketQueue参与到Packet处理模块对Packet对象的xml解析以及存储Packet对象到PacketQueue中。Packet处理把Packets对象一处队列并处理它们。PacketQueue的设计是线程安全的,允许单独的服务器线程同步处理packetQueue。
Jabber Packets——Packets类存储的信息数据包包含了jabber服务器与客户端互发JabberXml数据包。  
当客户端连接到服务器,基本的服务操作开始。Server将为此连接建立一个会话对象,并开始解析进入的xml。Xml解析后转变成packet对象被推入到packet队列。一个packet处理的集合把packet从队列中取出,处理它们并产生输出的packet,使用session索引找到正确的session对象的xml输出流,并且输出xml到流中。
session池,xml解析,和packet队列在一起互相协助处理jabber packet。我们将在packet处理类中实现多个协议。现在我们提供了最基本的输入packet路由。我们遍历jabber协议知道将添加更多的packet处理。

3.2会话池维持客户端连接

一个典型的jabber服务将维持许多并发的,长时间的客户端连接。每一个会话在客户端和服务器端定义一个上下文包,并在他们之间通行。每一Session的上下文都必须为维持每一个连接保持连接。它它包含了如下信息:

l       Session保持连接的jabberID

l       Session保持连接的StreamID

l       被Session用到的java.net.Socket以及对应的java.io.Reader/Writer对象

l       Session状态(disconnected,connected,streaming,authenticated)

 

这个连接的集合和他的维持信息被封装在一个Session对象中。另外,所有的在服务器中的会话在包信息存取都是非常容易丢失的。我们开发一个集中的SessionIndex类就是为了保持我们的活动的会话不丢失以及保证通过jabberID能够找到会话。

我们首先看看Session类。

3.2.1会话类抽象一个连接

会话类提供了一个方便的session上下文信息分组的办法。类开始声明了两个构造器,基本的数据域和访问方法。我们也提供了两个为SessionSocket对象读写功能的方法。大部分时候,session对象用到java.io.Writer写信息到SessionSocket的outputStream中,或者Java.io.Reader读取信息。通过创建和保存一个Reader和Werter,使用者可以不用得到Socket,自己就可以得到Input/OutputStream,和创建Reader/writer。

public class Session{

public Session(Socket socket) { setSocket(socket); }

public Session() { setStatus(DISCONNECTED); }

JabberID jid;

public JabberID getJID() { return jid; }

public void setJID(JabberID newID) { jid = newID; }

String sid;

public String getStreamID() { return sid; }

public void setStreamID(String streamID) { sid = streamID; }

Socket sock;

public Socket getSocket() { return sock; }

public void setSocket(Socket socket) {

sock = socket;

setStatus(CONNECTED);

}

Writer out;

public Writer getWriter() throws IOException {

if (out == null){

out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));

}

return out;

}

Reader in;

public Reader getReader() throws IOException {

if (in == null){

in = new BufferedReader(new InputStreamReader(sock.getInputStream()));

}

return in;

}

最让人感兴趣的是session里的状态管理代码。通过几个类我们能够知道怎么在状态发生改变同时session状态发生改变。我们在下面的章节将会在客户端代码中真切的体会。为了支持这些功能,我们用的会话状态事件模型可以在Swing类中发现。

在此模型中,注册监听事件。当一个事件被触发,会话会通知注册的监听事件。这个过程一时间看来有些小混乱。但是其实这是一条康庄大道,是解决多个对象监控另外对象的变量的一个好办法。ListinListing 3.2 The Session class st atus event code

LinkedList statusListeners = new LinkedList();

public boolean addStatusListener(StatusListener listener){

return statusListeners.add(listener);

}

public boolean removeStatusListener(StatusListener listener){

return statusListeners.remove(listener);

}

public static final int DISCONNECTED = 1;

public static final int CONNECTED = 2;

public static final int STREAMING = 3;

public static final int AUTHENTICATED = 4;

int status;

public int getStatus() { return status; }

public synchronized void setStatus(int newStatus){

status = newStatus;

ListIterator iter = statusListeners.listIterator();

while (iter.hasNext()){

StatusListener listener = (StatusListener)iter.next();

listener.notify(status);

}

}

}

一个Java.util.LinkedList类用来维持一个Session监听列表。加入和删除一个状态是非常容易的。所有的状态时间监听必须继承StatusListener接口。

The StatusListener interface

public interface StatusListener {

public void notify(int status);

}

它只有一个notify()方法,被用在setStatus()方法发送一个事件信息给监听器。为了连贯性,我也为我设想的Session类的4个状态定义几个标准值。

会话类抽象了在客户端和服务器端的一个网络连接和jabberSesison。服务器将处理许多同步的Session,通一个组织结构为jabberID定位Sessions。SessionIndex类承担这些职责。

3.2.3SessionIndex类提供session查找

SessionIndex的主要职责是更具jabberID查找Session对象。对服务器来说按照jabberID定位到正确的Session对象是很重要的,因为多数的jabber包来自于jabber用户。服务器必须根据收到的jabberID定位到适当的session并回复包到客户端。

为实现这些功能,SessonIndex类包括两个Java.util.HashTable对象:userIndex和JidIndex。UserIndex Hashtable提供了Session的用户和Session对象的映射。JidIndex提供了Session的jabberID字符串和Session对象之间的映射。

使用SessonIndex引导session的查找使用以下算法:

l       检查如果接收者的jabber id是在jidindex 。比较使用
精确匹配允许客户发送信息到其他特定客户。

l       如果没有,查看jabberId的用户名在userIndex中

l       如果没有找到,返回null。

使用这个算法服务器能够显示一个合理的消息回路。例如,假设一个客户端的jabberID是iain@shigeoka.com/home,连接服务器。SessionIndex将查找JabberID,在jidIndex中找到它,返回合适的Session。现在,假设消息的地址是iain@shigeoka.com。SessinIndex类在JIdIndex中查找,但没有找到。然而当SessionIndex在userIndex中查找,找到iain,返回连接iain@shigeoka.com/home的Session。

 

最后,考虑一下消息地址iain@shigeoka/work。SessionIndex类在jidIndex中查找失败,但是能够看到iain实体在userIndex并且返回Session附在iain@shigeoka.com/home。因此,消息被传到适当的用户但是是备用的资源。这个动作符合jabber消息路由规定。

执行的类可以简单明了的管理这些映射。


public class SessionIndex {

Hashtable userIndex = new Hashtable();

Hashtable jidIndex = new Hashtable();

public Session getSession(String jabberID){

return getSession(new JabberID(jabberID));

}

public Session getSession(JabberID jabberID){

String jidString = jabberID.toString();

Session session = (Session)jidIndex.get(jidString);

if (session == null){

LinkedList resources = (LinkedList)userIndex.get(jabberID.getUser());

if (resources == null){

return null;

}

session = (Session)resources.getFirst();

}

 

return session;

}

public void removeSession(Session session){

String jidString = session.getJID().toString();

String user = session.getJID().getUser();

if (jidIndex.containsKey(jidString)){

jidIndex.remove(jidString);

}

LinkedList resources = (LinkedList)userIndex.get(user);

if (resources == null){

return;

}

if (resources.size() <= 1){

userIndex.remove(user);

return;

}

resources.remove(session);

}

public void addSession(Session session){

jidIndex.put(session.getJID().toString(),session);

String user = session.getJID().getUser();

LinkedList resources = (LinkedList)userIndex.get(user);

if (resources == null){

resources = new LinkedList();

userIndex.put(user,resources);

}

resources.addLast(session);

}

}

如你所见,我在userIndex中为每一个用户名使用了一个Sessions的LinkedList,messages遵循着先到先服务的原则。换句话说,如果你连接以下客户端:

iain@shigeoka.com/home

iain@shigeoka.com/work

iain@shigeoka.com/laptop

一个发送到iain@shigeoka.com的消息将别发送到iain@shigeoka.com/home。如果iain@shigeoka.com/home没有连接,那么消息发送到iain@shigeoka.comiain@shigeoka.com/work。这些不是标准的jabber路由动作,但是是在第8章之前,在我们添加用户帐户,用户出席和支持协议之前,能做到得到最好的状态。当我们在这里使用用户出席支持,我们将能够实现更加老道的,优先的路由元数据按照指定的jabber标准。使用SessionIndex的关键是包处理类关联的QueueThread类。

  3.3xml解析子系统
XML解析是jabber服务所做的最复杂的任务了,然而,对于java的Coder们来说,这个任务显然是小儿科,因为我们用完善的Java Sax解析库来做这些工作。我们仅仅需要对查找出处理这些的方法。
服务器的XML解析类的任务就是将xml流信息写入Packet对象,存储的PacketQueue。我们用packet和packetQueue类开始我们的xml解析过程
 
3.3.1描述Jabber包
jabber协议包含了客户端与服务器端交换xml碎片的办法。我们把这些xml碎片作为一个包来提交。我们把这些信息包装到java对象中,他们工作使用起来要比xml字符串简单的多。使用java对象还可以享受到java的类型检测,继承以及多态等特征的好处。
预览jabber协议就可以知道它有三个核心包需要操心:
<message></message>
,<presence></presence>
<iq></iq>
另外需要我们注意的是打开的和关闭的流标签以及流错误包:
<stream:stream>
<stream:error></stream:error>
</stream:stream>
支持这些包和标签从根本上说是很简单的。在本章最后,我们的jabber服务将能够识别和控制他们。第一步是识别他们的一般特征和封装他们到java类。
Jabber包其实是一个xml片段,因此可以把它当成一个mini的xml文档。在java中又很多的方法处理xml文档。最流行的依据w3cdom标准。大部分的javaxml解析库包含标准的支持w3cdom的java类。在dom中,xml文档时类似于树的结构体。
我们需要的xml表示不是如dom标准所能胜任的。我们知道我们接受各种各样的xml文档和我们指明的java对象。另外,不使用dom也可使我们的代码避免依附到dom库和建立服务器所需的代码。最后,我们的包类必须做比呈现xml片段更多的功能。Packets类必须能充当如下两个角色:
Pack Store——packets类是一个主要的数据储存类。信息能够被标准的java方法调用。我们能够存储包到类似w3cdom的树形数据结构。
XML writer——packet类能够知道到怎么创建自己的xml字符串表示方式。这个特征允许其他类从xml字符串呈现对象转换成的packet对象。
一个单独的xml解析类在本章最后将从接受的xml字符串转变成packet对象。Packet类的数据结构反映了xml片段的结构。例如,考虑如下xml包:
<message to='recipient'>
<subject>hello</subject>
<body>How are you doing?</body>
</message>
我们可以把它们组织到三个包中:
Packet: message (attribute to='recipient)
Packet: subject
String (value "hello")
Packet: body
String (value "How are you doing?");
在这样的数据结构中,我们能够看到一个包有一个元素名,零个或多个属性名值对,零个或多个子节。一个packet的子对象是一个字符串或其packet对象。另外,每个包有一个相关的命名空间。
作为一个java类,我们能够存储packet的子节到java.util.List类型的List中。他的属性在java.util.Hashtable,并且其他值是字符串。如果这个包没有父节点,包的parent值是null。
这个类包括三个构造器:
The Packet class constructors
public class Packet {
public Packet(String element){
setElement(element);
}
public Packet(String element, String value){
setElement(element);
children.add(value);
}
public Packet(Packet parent,
String element,
String namespace,
Attributes atts){
setElement(element);
setNamespace(namespace);
setParent(parent);
//Copy attributes into hashtable
for (int i = 0; i < atts.getLength(); i++){
attributes.put(atts.getQName(i), atts.getValue(i));
}
}
parent和children成员变来那个负责packet的树形结构和他们的子节。我们能够用LinkedList类型的类存储packet和Strings。另外,packet的命名空间和元素名必须被存储成字符串。
String namespace;
public void setNamespace(String name) { namespace = name; }
public String getNamespace() { return namespace; }
String element;
public void setElement(String element) { this.element = element; }
public String getElement() { return element; }
Packet parent;
public Packet getParent() { return parent; }
public void setParent(Packet parent){
this.parent = parent;
if (parent != null){
parent.children.add(this);
}
}
LinkedList children = new LinkedList();
public LinkedList getChildren() {return children;}
packet提供了几个构造器。假设我们用个如下的xml包:
<item>
ItemValue
<sub-item>sub-item value</sub-item>
<sub-item>another value</sub-item>
</item>
有三个典型的典型任务,其他的类将在packet中执行:
l       获取给定元素名的子包。(例如:<sub-item>sub-item value</sub-item>).
l       获取packet的第一个字符串值。
l       获取字符串值相关的第一个子包。
第一个方法是从一个给定的元素名定位第一个子包。他在getFirstChild()中实现。例如,考虑最近的<itme>xml包。你能够在itme包对象中调用getFirstChild(“sub-itme”),得到元素名为sub-item的子包。
public Packet getFirstChild(String subelement){
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object child = childIterator.next();
if (child instanceof Packet){
Packet childPacket = (Packet)child;
if (childPacket.getElement().equals(subelement)) {
return childPacket;
}
}
}
return null;
}
另外一些基本的任务是依据一个元素获得字符串值。例如,我们得到了<sub-item>子包,我们能够知道他的值(“sub-item value”)。你能够通过调用<sub-item>的包对象getValue()方法得到。
public String getValue(){
StringBuffer value = new StringBuffer();
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object valueChild = childIterator.next();
if (valueChild instanceof String){
value.append((String)valueChild);
}
}
return value.toString().trim();
}
甚至有许多类似的情况,当我们想知道sub-packet的子节的字符串值。再上一个例子中,我们通过调用子包的getFirstChild(“sub-item”),得到<sub-item>的子包值。然后调用getValue()得到他的字符串值。有了这些方便的方法,我们能够更方便的结合到getChildValue()方法中。public String getChildValue(String subelement){
Packet child = getFirstChild(subelement);
if (child == null){
return null;
}
return child.getValue();
}
输入包经常依赖于会话上下文和其它动作路由。每一个在我们的jabber服务中的client或server能够有一个相关的session对象出处session上下文。这个packet存储这个会话对象的引用。
Session session;
public void setSession(Session session) { this.session = session; }
public Session getSession() {
if (session != null){
return session;
}
if (parent != null){
return parent.getSession();
}
return null;
}
许多jabber协议依赖于packet属性翻译包相关信息和他的动作。Packet类存储属性到java.util.Hashtable。另外,他提供了几个方便的方法访问大部分的一般packet属性:
l       to——包的接受对象
l       from——包发送者
l       id——包ID,唯一标识包
l       type——包类型。依据协议而定。
Hashtable attributes = new Hashtable();
public String getAttribute(String attribute) {
return (String)attributes.get(attribute);
}
public void setAttribute(String attribute, String value) {
if (value == null){
removeAttribute(attribute);
} else {
attributes.put(attribute,value);
}
}
public void removeAttribute(String attribute){
attributes.remove(attribute);
}
public void clearAttributes(){
attributes.clear();
}
public String getTo() { return (String)attributes.get("to"); }
public void setTo(String recipient) { setAttribute("to",recipient); }
public String getFrom() { return (String)attributes.get("from"); }
public void setFrom(String sender){ setAttribute("from",sender); }
public String getType() { return (String)attributes.get("type"); }
public void setType(String type){ setAttribute("type",type); }
public String getID() { return (String)attributes.get("id"); }
public void setID(String ID) { setAttribute("id",ID); }
}
最后,packet类能够把它自己写成一个xml字符串,依靠java.io.Writer。创建xml呈现的过程包括遍历树顺序,输出合适的元素,属性,以及子节。
public void writeXML() throws IOException {
writeXML(session.getWriter());
}
public void writeXML(Writer out) throws IOException{
out.write("<");
out.write(element);
//Output the attributes for the element
Enumeration keys = attributes.keys();
while (keys.hasMoreElements()){
String key = (String)keys.nextElement();
out.write(" ");
out.write(key);
out.write("='");
out.write((String)attributes.get(key));
out.write("'");
}
//Empty element
if (children.size() == 0){
out.write("/>");
out.flush();
return;
}
out.write(">");
//Iterate over each child
Iterator childIterator = children.iterator();
while (childIterator.hasNext()){
Object child = childIterator.next();
//Send value to Writer
if (child instanceof String){
out.write((String)child);
//Or recursively write its children's XML
} else {
((Packet)child).writeXML(out);
}
}
out.write("</");
out.write(element);
out.write(">");
out.flush();
}
public String toString(){
try {
StringWriter reply = new StringWriter();
writeXML(reply);
return reply.toString();
} catch (Exception ex){
}
return "<" + element + ">";
}
}
packet置入,接受来源,一个单独的服务端包队列。
 

3.3.2 服务的焦点,packetQueue类

packetQueue类是一个受限的响应集合的基本数据结构类。然而,他是服务器端的信息流的焦点。Packet从客户端聚集到packetQueue。然后packet按照packet的元素名发散出去。许多操作方法能够被服务器激活,并且同步到packetQueue。

Comand操作设计模式:

 

For those familiar with design patterns, our packet handling system is a minor

variation on the Command Processor design pattern.9

“The Command Processor design pattern separates the request for a service

from its execution. A command processor component manages requests as

separate objects, schedules their execution, and provides additional services

such as the storing of request objects for later undo.
 
 

 

 

 

 

 


的jabber服务中,xml解析充当命令控制器的作用,接受请求和转换

在我们的jabber服务,XML解析动作作为command控制器接受请求,转换xml流到命令。QueeueThread充当命令处理机获得paceket并调度他们的执行。不管像不像命令处理模式,packets是不会自己处理他们自己的运行。双重功能的命令处理供应者和命令处理代替我们的packet处理类,提供我们的整个的包处理功能。

使用comandprocessor模式的好处是:

l       请求触发的可扩展性——支持选择启动的方法是的能够很容易的桥连我们的jabber服务和其它message系统。

l       请求的操作和数量上的可扩展性——快速改变jabber协议是非常重要的。

l       相关服务的可编程——在特殊情况下,我们只要稍稍修改QueueThread就能够容易的记录packets和重放jabber会话。

l       应用的可测试性——QueueThread是一个极好的接入点,为我们测试xml解析和packet处理。一旦开启,记录和重放session是一个巨大的帮助。

l       并发——包和他们的处理是一个相对分离的计算。当线程同时执行时,QueueThread能够容易的分配处理。

Commander processor模式有一些缺陷:

l       效率的损耗——转换数据格式和提供中间处理步骤需要而外的计算时间的存储空间。

l       潜在着过多的命令类——大部分时候,我们避免把所有的包表现为单一的,一般的paceket类。我们为简单性而降低了一半的呈现效率。另外,一个packet类能够呈现不定数量的packet类。我们的服务必须建立额为的逻辑来垂腭这些包。

PacketQueue必须:

l       存储包——接受packet对象推入packetQueue的底

l       回收包——允许packet对象退出packetQueue,并移除它

l       线程安全——无障碍的允许多线程同步从packetQueeue 中push和pull packet。

l       提供线程同步——如果PacketQueue是空的,调整pull包之前让调用者停止一会儿直到一个新的packet进入队列,或者线程终止。线程能够同步的使用它们的操作和保存服务资源。

操作packet对象的线程能够有效的不断的从packetqueue拉出packet对象。PacketQueue自己必须确保这些工作的线程只能够在packetQueue正常有效的情况下执行。

packetQueeue的对象是非常简单的。一个java.util.LinkedList对象,用来存储Packets和一点用来支持服务器多线程环境的基本要素。

在下面高亮的部分提示了方法push()和pull()是线程安全保护的。每一个push()方法的调用时引起了notifyAll()唤醒等待的pull()处理线程。Pull()方法的定时器在队列是空的时候调用wait()方法。


public class PacketQueue {

//Actual storage is handled by a java.util.LinkedList

LinkedList queue = new LinkedList();

public synchronized void push(Packet packet){

queue.add(packet);

notifyAll();

}

public synchronized Packet pull(){

try {

while (queue.isEmpty()) {

wait();

}

} catch (InterruptedException e){

return null;

}

return (Packet)queue.remove(0);

}

}

loop是必须的,因为push()方法调用notifyAll(),唤醒所有等待的pull()。如果仅仅一个packet被推进队列,第一个线程执行移除从队列中。所有其他等待的线程继续运行,队列是空的,他们定时wait(),直到下一个包的到来。

如果你不能理解java线程代码,你可以信任这些工作,写一些测试验证一下,或者学习关于java线程的知识。我已经尽力将本书的中关于thread的代码最小化,但是服务代码倾向于沉重的线程。如果你希望学习到线程,有几个很好的书可以看。另外,java在线文档也提供免费指导。

现在,我们看看用jabberInputHandle类处理xml解析。

3.3.5java中的 SAX 解析(略)

3.4包处理和服务器线程

我最终需要的jabber服务器,包括服务包处理类和服务器端线程。包处理是继承自PacketListerer接口的简单对象,处理包对象。我们用的包处理类实现我们知道的jabber协议。

在这一章,我们将创建三个packet处理类:

l       当未关闭的<stream:stream>发送时一个OpenStreamhandler类为每一个连接创建Sessiong对象。

l       一个简单的messagehandler类,实现Jabber包路由初始版本。

l       当发送关闭标签<stream:stream>时候,CloseStreamhandler类经关闭连接。

服务的线程系统有三个类组成,每一个类代表一个线程执行:

l       Server——主应用程序类和主执行线程。这个类创建jabber服务socket和接受新的jabber客户连接。

l       ProcessTread——服务器线程类接受连接处理。他创建一个jabberInputHandler和一个输入xml流过程。ProcessThread线程处理和连接一一相对。

l       QueueThread——这个线程类从PacketQueeue得到包然后他打发给适当的包处理类。在jabber服务中仅仅有一个活动的queueThread对象。


在同一时间有许多线程在服务上执行,然而,只有三种类型的需要你的关心,并且只有ProcessThread有超过一个的实例在执行。理解服务工作的最后办法,看看3.6图,显示了服务器上都发生了什么。

我们看看服务的主应用首先启动。他的首要任务是创建QueueThread类。新的queueThread立即调用pull()接受PacketQueue的包。QueueThread定时是因为队列中没有包。服务然后等待这用java.net.ServerSocekt.accept()调用连接。


在服务器上一个客户连接。这促使服务器创建一个processThread。这个ProcessTrhead用saX解析开启解析输入xml流和我们的JabberInputhandler类。当客户端发送xml包到服务器,ProcessTrhead的解析类创建包对象并调用push()加入到PacketQueue。

队列中新的Packet唤醒QueueThread。QueeuThread确定包已经被推入队列,然后用当前处理类处理它。包处理发送输出xml包到客户端。

注意仅有一个服务线程接受连接,并且一个QueueThread处理从packetQueue的包对象。然而,每一个客户链接有一个活动的processThread。有共享执行线程的方法处理输入的客户连接。另外,服务器有许多的包处理线程。然而,在我们的服务中,我们使他们尽量的简单。

从服务的主类创建QueueThread和ProcessThread,我们首先看到有两个帮助线程类,在运行服务助力额之前。QueueThread是个更加的复杂,我们就在这开始。

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics