⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 x9016.htm

📁 Its a xmpp protocol book
💻 HTM
📖 第 1 页 / 共 5 页
字号:
>MLDBM</TT
> is a really useful wrapper around the 
<TT
CLASS="LITERAL"
>DB_File</TT
> module. <TT
CLASS="LITERAL"
>DB_File</TT
> provides
access to Berlekey DB database facilities using the <TT
CLASS="FUNCTION"
>tie()</TT
>
function. While you can't store references (i.e. complex data structures)
via <TT
CLASS="LITERAL"
>DB_File</TT
>, you can when you use the
<TT
CLASS="LITERAL"
>MLDBM</TT
> wrapper. </P
><P
>We will use the <TT
CLASS="LITERAL"
>LWP::Simple</TT
> module to grab the RSS
sources by URL, and the <TT
CLASS="LITERAL"
>XML::RSS</TT
> module to parse those
sources once retrieved.</P
><P
><PRE
CLASS="SCREEN"
>my $NAME     = 'RSS Punter';
my $ID       = 'rss.qmacro.dyndns.org';
my $VERSION  = '0.1';
my $reg_file = 'registrations';
my %reg;

my %cache;

my %sources = (

  'jonudell' =&#62; 'http://udell.roninhouse.com/udell.rdf',
  'slashdot' =&#62; 'http://slashdot.org/slashdot.rdf',

  # etc ...

);&#13;</PRE
></P
><P
>We start by declaring a few variables. We will see later in the script
that <TT
CLASS="LITERAL"
>$NAME</TT
>, <TT
CLASS="LITERAL"
>$ID</TT
> and 
<TT
CLASS="LITERAL"
>$VERSION</TT
> will be used to reflect information
in response to IQ queries. The variable <TT
CLASS="LITERAL"
>$reg_file</TT
> 
defines the name of the DB file to which we'll be
<TT
CLASS="FUNCTION"
>tie()</TT
>ing our registration hash <TT
CLASS="LITERAL"
>%reg</TT
>.
<TT
CLASS="LITERAL"
>%cache</TT
> is our RSS item cache, to hold items that we've
already seen, so we know when we've come to the end of the new items in
a particular source.</P
><P
>We define our RSS sources in <TT
CLASS="LITERAL"
>%sources</TT
>. You may wish
to define these differently, perhaps outside of the script. There are
a couple of examples here; add your own favourite channels to taste.</P
><P
><PRE
CLASS="SCREEN"
>tie (%reg, 'MLDBM', $reg_file) or die "Cannot tie to $reg_file: $!\n";&#13;</PRE
></P
><P
>This magic line makes any data we store in the <TT
CLASS="LITERAL"
>%reg</TT
>
hash persistent. It works by binding the operations on the hash (add,
delete, and so on) to Berlekey DB operations, using the MLDBM module to
stringify (and reconstruct) complex data structures so that they can
be stored (and retrieved).</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-3.3.2"
>Connection</A
></H3
><P
>Right. We're ready to connect to the Jabber server as a component. 
Despite what's involved (described in <A
HREF="x9016.htm#JABTDG-CH-8-SECT-3.1.1"
>the section called <I
>The script as <I
CLASS="EMPHASIS"
>component</I
></I
></A
>)
it's very easy using a library such as <TT
CLASS="LITERAL"
>Jabber::Connection</TT
>:</P
><P
><PRE
CLASS="SCREEN"
>my $c = new Jabber::Connection(
  server    =&#62; 'localhost:5999',
  localname =&#62; $ID,
  ns        =&#62; 'jabber:component:accept',
);&#13;</PRE
></P
><P
>We construct a <TT
CLASS="LITERAL"
>Jabber::Connection</TT
> 
object, specifying the details of the connecion we wish to make. The
<TT
CLASS="LITERAL"
>server</TT
> argument is used to specify the hostname, 
and optionally the port, of the Jabber server to which we wish to connect.
In the case of a component, we must always specify the port (which is
5999 in our case, according to the component instance definition shown
in <A
HREF="x9016.htm#JABTDG-CH-8-EX-8"
>Example 8-8</A
>). The same constructor can be used to create a client
connection to Jabber, in which case a default port of 5222&mdash;the
standard port for client connections&mdash;is assumed if none is 
explicitly specified. The <TT
CLASS="LITERAL"
>localname</TT
> argument is 
used to specify our name&mdash;the component's name&mdash;which in this 
case is <TT
CLASS="LITERAL"
>rss.qmacro.dyndns.org</TT
>. We specify the stream
namespace with the <TT
CLASS="LITERAL"
>ns</TT
> argument. In the same way that
a default port of 5222 is assumed if none is specified, a default 
stream namespace of <TT
CLASS="LITERAL"
>jabber:client</TT
> is assumed if no
<TT
CLASS="LITERAL"
>ns</TT
> argument is specified. We wish to connect as a
component using the <I
CLASS="EMPHASIS"
>TCP sockets</I
> connection method,
so we must specify the appropriate namespace: 
<TT
CLASS="LITERAL"
>jabber:component:accept</TT
>.</P
><P
>This constructor call results in a stream header being prepared, one
that looks like the one shown in <A
HREF="x9016.htm#JABTDG-CH-8-EX-10"
>Example 8-10</A
>.</P
><P
>The actual connection attempt, including the sending of the component's
stream header, is done by calling the <TT
CLASS="FUNCTION"
>connect()</TT
>
method on the connection object in <TT
CLASS="LITERAL"
>$c</TT
>:</P
><P
><PRE
CLASS="SCREEN"
>unless ($c-&#62;connect()) { die "oops: ".$c-&#62;lastError; }&#13;</PRE
></P
><P
>This will return a true value if the connect succeeded (success is 
measured in whether the socket connection was established and whether
the Jabber server sent a stream header in response). If it didn't
succeed, we can retrieve details of what happened using the 
<TT
CLASS="FUNCTION"
>lastError()</TT
> method.</P
><P
>We're connected. Before performing the authenticating handshake, we're
going to do a bit of preparation:</P
><P
><PRE
CLASS="SCREEN"
>$SIG{HUP} = $SIG{KILL} = $SIG{TERM} = $SIG{INT} = \&#38;cleanup;&#13;</PRE
></P
><P
>The idea is that the component will be run and only stopped in exceptional
circumstances. If it is stopped, we want to clean things up before the
script ends. Most importantly, we need to make sure our registration data
is safe, but also we want to play nicely with the server and gracefully
disconnect. This is done in the <TT
CLASS="FUNCTION"
>cleanup()</TT
> function.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-3.3.3"
>Preparation of the RSS event function and element handlers</A
></H3
><P
><PRE
CLASS="SCREEN"
>debug("registering RSS beat");
$c-&#62;register_beat(1800, \&#38;rss);&#13;</PRE
></P
><P
><TT
CLASS="LITERAL"
>Jabber::Connection</TT
> offers a simple way of having a
function execute at regular intervals. It avoids the need for setting
and re-setting <TT
CLASS="FUNCTION"
>alarm()</TT
>s. Calling the 
<TT
CLASS="FUNCTION"
>register_beat()</TT
> method takes two arguments.
The first represents the interval, in seconds. The second is a reference
to the function that should be invoked at each interval. Here, we're
saying we want the <TT
CLASS="FUNCTION"
>rss()</TT
> function called every
half an hour.</P
><P
><PRE
CLASS="SCREEN"
>debug("registering IQ handlers");
$c-&#62;register_handler('iq',\&#38;iq_register);
$c-&#62;register_handler('iq',\&#38;iq_version);
$c-&#62;register_handler('iq',\&#38;iq_browse);
$c-&#62;register_handler('iq',\&#38;iq_notimpl);&#13;</PRE
></P
><P
>Most of the traffic relating to our component will be the headline
messages emanating from it.
However, we are expecting incoming IQ elements, particularly for
registration in the <TT
CLASS="LITERAL"
>jabber:iq:register</TT
> namespace.
We've also already mentioned that it's customary for components
to honor basic "administrative" queries such as version checks. So the
list of calls to the <TT
CLASS="FUNCTION"
>register_handler()</TT
> method
here reflects what we want to offer in terms of handling these IQ
elements. </P
><P
>Whereas with <TT
CLASS="LITERAL"
>Net::Jabber</TT
>'s
<TT
CLASS="FUNCTION"
>SetCallBacks()</TT
> function, and with
<TT
CLASS="LITERAL"
>JabberPy</TT
>'s <TT
CLASS="FUNCTION"
>setIqHandler()</TT
> method
we specify a single function to act as a handler for incoming 
<TT
CLASS="LITERAL"
>&#60;iq/&#62;</TT
> elements, we can specify as
many handlers as we want for each element type with the 
<TT
CLASS="FUNCTION"
>register_handler()</TT
> method in 
<TT
CLASS="LITERAL"
>Jabber::Connection</TT
>. The first argument refers to the
element name (the name of the element's outermost tag), and the second
refers to
a function that will be called on receipt of an element of that name.
Each of the handlers for a particular element will be
called in the order they were registered. So when an 
<TT
CLASS="LITERAL"
>&#60;iq/&#62;</TT
> element is received over the 
XML stream, <TT
CLASS="LITERAL"
>Jabber::Connection</TT
> will dispatch it 
to <TT
CLASS="FUNCTION"
>iq_register()</TT
>, then to 
<TT
CLASS="FUNCTION"
>iq_version()</TT
>, then to
<TT
CLASS="FUNCTION"
>iq_browse()</TT
>, and then to
<TT
CLASS="FUNCTION"
>iq_notimpl()</TT
>. That is, unless one of those handler
functions decides that the element has been handled once and for all, and
that the dispatch processing for that element should stop there.
In this case, that handler simply 
returns a special value (defined in <TT
CLASS="LITERAL"
>Jabber::NS</TT
>) and
the dispatching stops for that element. The handlers can also cooperate,
in that the dispatcher will pass whatever one handler returns, into the
next handler in the list, and so on, so that you can effectively share
data across handler events for a particular element, building up a 
complex response as you go. </P
><P
>This "contextual response chain" model works in a similar way to how
the <TT
CLASS="LITERAL"
>mod_auth_*</TT
> authentication modules in JSM work.
Each one that wishes to express its interest in authenticating a 
user adds its "stamp" to the response to an IQ-get in the 
<TT
CLASS="LITERAL"
>jabber:iq:auth</TT
> namespace, before that response is
returned to the client.
<A
NAME="AEN9634"
HREF="#FTN.AEN9634"
>[9]</A
></P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-3.3.4"
>Authenticating handshake and launch of main loop</A
></H3
><P
>Once we've set up our handlers, we're ready to make the authenticating
handshake. This is simply a call to the <TT
CLASS="FUNCTION"
>auth()</TT
> 
method:</P
><P
><PRE
CLASS="SCREEN"
>$c-&#62;auth('secret');&#13;</PRE
></P
><P
>It takes one or three arguments, depending on
whether the authentication is for a client or a component.
<TT
CLASS="LITERAL"
>Jabber::Connection</TT
> decides which authentication context
is required by looking at the namespace specified (or defaulted) in the
connection constructor call. As we specified the namespace 
<TT
CLASS="LITERAL"
>jabber:component:accept</TT
>, the <TT
CLASS="FUNCTION"
>auth()</TT
>
method is expecting a single argument which is the secret
specified in the <TT
CLASS="LITERAL"
>&#60;secret/&#62;</TT
> tag of the
component instance definition. <TT
CLASS="FUNCTION"
>auth()</TT
> performs the
message digest function and sends the
<TT
CLASS="LITERAL"
>&#60;handshake/&#62;</TT
> element.</P
><P
>It's now appropriate for us to "launch" the component, with the 
<TT
CLASS="FUNCTION"
>start()</TT
> method: </P
><P
><PRE
CLASS="SCREEN"
>$c-&#62;start;&#13;</PRE
></P
><P
>This is the equivalent of the
<TT
CLASS="FUNCTION"
>MainLoop()</TT
> method in Perl's Tk library, and 
is a method from which there's no exit. Calling <TT
CLASS="FUNCTION"
>start()</TT
>
causes the connection object to perform an endless loop, which internally
calls a <TT
CLASS="FUNCTION"
>process()</TT
> method on a regular basis, 
receiving, examining and dispatching elements received on the XML stream.
It also starts and maintains the <I
CLASS="EMPHASIS"
>heartbeat</I
>, to which
the <TT
CLASS="FUNCTION"
>register_beat()</TT
> method is related.
<A
NAME="AEN9661"
HREF="#FTN.AEN9661"
>[10]</A
></P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-3.3.5"
>Handling registration requests</A
></H3
><P
>The first of the handlers defined for <TT
CLASS="LITERAL"
>&#60;iq/&#62;</TT
>
elements is the <TT
CLASS="FUNCTION"
>iq_register()</TT
> function. We put it 
first in the list as we consider receipt of 
<TT
CLASS="LITERAL"
>&#60;iq/&#62;</TT
> elements in the
<TT
CLASS="LITERAL"
>jabber:iq:register</TT
> namespace to be the most common.
We want this function to deal with the complete registration conversation.
This means it must respond to IQ-get and IQ-set type requests.</P
><P
><PRE
CLASS="SCREEN"
>sub iq_register {

  my $node = shift;

  debug("[iq_register]");&#13;</PRE
></P
><P
>The primary piece of data that the dispatcher passes to a callback is
the element to be handled. We receive this into the <TT
CLASS="LITERAL"
>$node</TT
>
variable; it's a <TT
CLASS="LITERAL"
>Jabber::NodeFactory::Node</TT
> object.
<A
NAME="AEN9679"
HREF="#FTN.AEN9679"
>[11]</A
>
The first thing we should do is make sure it's appropriate to continue
inside this function, which is only designed to handle 
<TT
CLASS="LITERAL"
>jabber

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -