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

📄 x8561.htm

📁 Its a xmpp protocol book
💻 HTM
📖 第 1 页 / 共 4 页
字号:
>s and 
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>s) that the fragments represent.
This is in the same way as, for example, callbacks are called in Python
scripts using the JabberPy libraries. The 
<TT
CLASS="FUNCTION"
>setup_Jabber()</TT
>, coming next, is where the callback
definition is made.</P
><P
>The <TT
CLASS="FUNCTION"
>Process()</TT
> method returns <TT
CLASS="LITERAL"
>undef</TT
>
if the connection to the Jabber server is terminated while waiting for
XML. The <TT
CLASS="LITERAL"
>undef</TT
> value is dealt with appropriately by 
ending the script. </P
><P
>The <TT
CLASS="LITERAL"
>GRAIN</TT
> constant, set to one second in the script's
setup section, is used to specify how long to wait. For the most part, 
we're not expecting to receive much incoming Jabber traffic. The occasional
presence subscription (or unsubscription) request, perhaps (see later),
but other than that, the only packets travelling over the connection
to the Jabber server will be availability
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> packets representing coffee
state changes, sent from the script.
So normally, this delay of 1 second will take place. That's
a comfortable polling interval for our light sensor too. So we do that
within the same loop, happy in the knowledge that's it's most likely
been a second since the last poll.</P
><P
>Calling the ActiveX control's 
<TT
CLASS="FUNCTION"
>Poll()</TT
> again with the same arguments as before
("get a sensor value from the sensor attached to the
<TT
CLASS="LITERAL"
>SENSOR</TT
>th connector"), we pass the value to the 
<TT
CLASS="FUNCTION"
>set_status()</TT
> to determine the coffee state. If 
the state was different from last time (if <TT
CLASS="LITERAL"
>$s</TT
>
receives a value, and not <TT
CLASS="LITERAL"
>undef</TT
>), then we want to 
emit a <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element to reflect
that state. We achieve this by calling the 
<TT
CLASS="FUNCTION"
>set_presence()</TT
> function, passing it the connection
object and the state.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-2.2.5"
><TT
CLASS="FUNCTION"
>setup_Jabber()</TT
></A
></H3
><P
>Here we define the <TT
CLASS="FUNCTION"
>setup_Jabber()</TT
> function,
which is called
once to set up the connection to the Jabber server and authenticate with
a pre-defined user.</P
><P
><PRE
CLASS="SCREEN"
># Set up Jabber client connection, sending intial presence
# --------------------------------------------------------
sub setup_Jabber {
  my ($server, $port, $user, $pass, $resource, $initial_status) = @_;
  my $connection = new Net::Jabber::Client;

  # Connect
  my $status = $connection-&#62;Connect( hostname =&#62; $server,
                                     port     =&#62; $port );
  die "Cannot connect to Jabber server $server on port $port\n"
    unless $status;

  # Callbacks
  $connection-&#62;SetCallBacks( presence =&#62; \&#38;InPresence );

  # Ident/Auth
  my @result = $connection-&#62;AuthSend( username =&#62; $user,
                                      password =&#62; $pass,
                                      resource =&#62; $resource );
  die "Ident/Auth failed: $result[0] - $result[1]\n"
    if $result[0] ne "ok";

  # Roster 
  $connection-&#62;RosterGet();

  # Initial presence dependent upon initial status
  &#38;set_presence($connection, $initial_status);

  return $connection;
}&#13;</PRE
></P
><P
>First, we instantiate a new <TT
CLASS="LITERAL"
>Net::Jabber::Client</TT
>
object. <TT
CLASS="LITERAL"
>Net::Jabber</TT
> distinguishes between client-based
and component-based connections to Jabber; the component-based equivalent
of this class is <TT
CLASS="LITERAL"
>Net::Jabber::Component</TT
>. The
<TT
CLASS="FUNCTION"
>connection()</TT
> method is passed arguments that specify
the hostname and port of the Jabber server to connect to. It returns a
zero status if the connection could not be made. </P
><P
>We can register handlers for Jabber elements that are received over the
XML stream which is carried via the connection we've just made. Here
we are only interested in incoming <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>
elements&mdash;indeed, only those carrying presence subscription or
unsubscription requests, as we'll see in the definition of the
<TT
CLASS="FUNCTION"
>InPresence()</TT
> function.</P
><P
></P
><P
>The single method
<TT
CLASS="FUNCTION"
>SetCallBacks()</TT
> does what the collective
<TT
CLASS="FUNCTION"
>Jabber.Connection</TT
> methods 
<TT
CLASS="FUNCTION"
>setPresenceHandler()</TT
>,
<TT
CLASS="FUNCTION"
>setMessageHandler()</TT
>, and
<TT
CLASS="FUNCTION"
>setIqHandler()</TT
> do in a single call, taking a list
of element types and subroutine references, in the form of a hash.</P
><P
>After registering our callback for <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>
elements, it's time to authenticate, passing the username, password and
resource that we defined in our list of constants at the start of the
script. If the authentication is successful, the result of the call to
the <TT
CLASS="FUNCTION"
>AuthSend()</TT
> method is a single string with the
value "ok". If not, that value is replaced with an error code and the
descriptive text is available in a further string. (This is why we catch
the results of a call in an array, called <TT
CLASS="LITERAL"
>@result</TT
>). The
full list of Jabber error codes and texts are shown in 
<A
HREF="x4089.htm#JABTDG-CH-5-TAB-3"
>Table 5-3</A
>.</P
><P
>Why <TT
CLASS="FUNCTION"
>RosterGet()</TT
>? We're not subscribing to anyone, and
we're not really interested in anything but the values we're polling from
our brick. Yes, you guessed it&mdash;we want the script to receive and
process presence subscription and unsubscription requests, but we aren't
sent those by the JSM unless we've requested our roster beforehand. 
See <A
HREF="x7499.htm#JABTDG-CH-7-SECT-3.2.7"
>the section called <I
>Request for roster</I
> in Chapter 7</A
> for an explanation as to why.</P
><P
>Once we've kicked the JSM into sending any presence requests on to us, 
the job is almost done. The last thing to do in setting up our Jabber
connection is to send our initial availability information. 
<TT
CLASS="FUNCTION"
>setup_Jabber()</TT
> receives the initial coffee status
as the last argument in the call (in <TT
CLASS="LITERAL"
>$initial_status</TT
>),
which it now duly passes on to the
function that sends a <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>
element, <TT
CLASS="FUNCTION"
>set_presence()</TT
>. Along with the 
initial coffee status, we also
send the <TT
CLASS="LITERAL"
>$connection</TT
> object that represents the 
connection to the Jabber server that we've just established (and that
is referred to outside of this function with the <TT
CLASS="LITERAL"
>$jabber</TT
>
variable. This is so the <TT
CLASS="FUNCTION"
>set_presence()</TT
> function
can use the connection handle to send the element down the stream.</P
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-2.2.6"
><TT
CLASS="FUNCTION"
>set_presence()</TT
></A
></H3
><P
>This function is used by <TT
CLASS="FUNCTION"
>setup_Jabber()</TT
> to send
the script's (and the coffee's) initial presence. It's also used within
the main sensor poll / presence push loop to send further presence 
packets if the coffee's state changes. </P
><P
><PRE
CLASS="SCREEN"
>sub set_presence {
  my ($connection, $s) = @_;
  my $presence = Net::Jabber::Presence-&#62;new();
  my ($show, $status) = split("/", $status[$s], 2);
  $presence-&#62;SetPresence( show   =&#62; $show, 
                          status =&#62; $status );
  print $status, "\n";
  $connection-&#62;Send($presence);
}&#13;</PRE
></P
><P
>On receipt of the Jabber connection object and the status, which will
be 0 (<TT
CLASS="LITERAL"
>NOCOFFEE</TT
>) or 1 (<TT
CLASS="LITERAL"
>COFFEE</TT
>),
<TT
CLASS="FUNCTION"
>set_presence()</TT
> constructs a new 
<TT
CLASS="LITERAL"
>Net::Jabber::Presence</TT
> object. This object represents a
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element, upon which we can 
make method calls to hone the element as we wish. 
<TT
CLASS="FUNCTION"
>SetPresence()</TT
> is one of these methods, with which
we can set values for each of the <TT
CLASS="LITERAL"
>&#60;show/&#62;</TT
>
and <TT
CLASS="LITERAL"
>&#60;status/&#62;</TT
> tags. We retrieve the values
for each of these tags by pulling the strings from the appropriate 
member of the <TT
CLASS="LITERAL"
>@status</TT
> array, as described in 
<A
HREF="x8561.htm#JABTDG-CH-8-SECT-2.2.1"
>the section called <I
>Setup</I
></A
>.</P
><P
>We print the coffee's status (remember, this function is only called
when the status <I
CLASS="EMPHASIS"
>changes</I
>, not every time the 
sensor is polled), and send our newly built
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>
element off down the XML stream to the Jabber server by passing the
presence object as an argument to the <TT
CLASS="FUNCTION"
>Send()</TT
>
method of the connection object in <TT
CLASS="LITERAL"
>$connection</TT
>. This
works in the same way as the <TT
CLASS="FUNCTION"
>send()</TT
> function in 
JabberPy, and the <TT
CLASS="FUNCTION"
>send()</TT
> function in JabberBeans.
And by the diffusive magic of the presence subscription model
(see <A
HREF="x4089.htm#JABTDG-CH-5-SECT-5.4.2.3"
>the section called <I
>Presence Subscription</I
> in Chapter 5</A
>), everyone
who has subscribed to the script user's presence, and who is available,
will receive the coffee status information. </P
><P
><A
HREF="x8561.htm#JABTDG-CH-8-FIG-2"
>Figure 8-4</A
> shows the status information received
in the WinJab client. The string sent in the
<TT
CLASS="LITERAL"
>&#60;status/&#62;</TT
> tag is shown in the tooltip that
appears when the mouse hovers over the "coffee" roster item.</P
><DIV
CLASS="FIGURE"
><A
NAME="JABTDG-CH-8-FIG-2"
></A
><P
><B
>Figure 8-4. Receiving information on the coffee's status in WinJab</B
></P
><P
><IMG
SRC="CH-8-FIG-2.jpg"></P
></DIV
></DIV
><DIV
CLASS="SECT3"
><H3
CLASS="SECT3"
><A
NAME="JABTDG-CH-8-SECT-2.2.7"
><TT
CLASS="FUNCTION"
>InPresence()</TT
></A
></H3
><P
>Our presence handler, the callback subroutine 
<TT
CLASS="FUNCTION"
>InPresence()</TT
>, exists for a single purpose: to 
honor requests for subscription and unsubscription to the script
user's (and therefore the coffee's) presence. This callback is designed
to work in the same way as the <TT
CLASS="LITERAL"
>presenceCB()</TT
> callback
in the Python recipe described in <A
HREF="x7499.htm"
>the section called <I
>Presence-sensitive CVS notification</I
> in Chapter 7</A
>.</P
><P
>However, while the Python JabberPy library hands to the callbacks a
<TT
CLASS="LITERAL"
>Jabber.Connection</TT
> object and the element to be 
handled, the Perl Net::Jabber library hands over a session ID and the
element to be handled. The session ID is related to functionality 
for building Jabber servers, functionality that is not yet complete.
We can and should ignore it for our script's purposes. What is important
is the element to be handled, which appears as the second 
argument passed to the subroutine, which we collect into the
<TT
CLASS="LITERAL"
>$presence</TT
> variable from <TT
CLASS="LITERAL"
>$_[1]</TT
>.</P
><P
>What is common between the two libraries is that the element that
is passed to be handled, as the subject, so to speak, of the callback,
is an instance of the class that the callback represents. In other words,
here we have a callback to handle <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>
elements, and the element received is an instance of the 
<TT
CLASS="LITERAL"
>Net::Jabber::Presence</TT
> class (just as the element 
received by a JabberPy presence callback is an instance of the
<TT
CLASS="LITERAL"
>Jabber.Presence</TT
> class).</P
><P
><PRE
CLASS="SCREEN"
># Handle presence messages
# ------------------------
sub InPresence
{
  my $presence = $_[1];
  my $from = $presence-&#62;GetFrom();
  my $type = $presence-&#62;GetType();

  if ($type eq "subscribe") {
    print "Subscribe request ($from) ...\n";
    $jabber-&#62;Send($presence-&#62;Reply(type =&#62; 'subscribed'));
  }
    
  if ($type eq "unsubscribe") {
    print "Unsubscribe request ($from) ...\n";
    $jabber-&#62;Send($presence-&#62;Reply(type =&#62; 'unsubscribed'));
  }
}&#13;</PRE
></P
><P
>With an object in <TT
CLASS="LITERAL"
>$presence</TT
>, we can get information
from the element using data retrieval methods such as those used here:
<TT
CLASS="FUNCTION"
>GetFrom()</TT
> and <TT
CLASS="FUNCTION"
>GetType()</TT
>, which 
extract the values from the <TT
CLASS="LITERAL"
>from</TT
> and 
<TT
CLASS="LITERAL"
>type</TT
> attributes of the 
<TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element respectively.</P
><P
>If the <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
> element type 
represents a subscription request (<TT
CLASS="LITERAL"
>type='subscribe'</TT
>),
we unquestioningly honor the request, by sending back an 
affirmative reply. The <TT
CLASS="LITERAL"
>Reply()</TT
> method of the
presence object is one of a number of high-level functions that make it
very comfortable to turn elements around and send them back. In this case, the
method replaces the value of the <TT
CLASS="LITERAL"
>&#60;presence/&#62;</TT
>'s
<TT
CLASS="LITERAL"
>to</TT
> attribute with the value of the 
<TT
CLASS="LITERAL"
>from</TT
> attribute, and preserves any <TT
CLASS="LITERAL"
>id</TT
>
attribute. It also allows us to pass arguments as if we were calling
the <TT
CLASS="FUNCTION"
>SetPresence()</TT

⌨️ 快捷键说明

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