|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239
|
|
Chapter 17:
|
|
|
|
Network Programming
|
|
Life in a great society,
or for that matter in a small,
is a web of tangled relations of all sorts,
whose adjustment so that it may be endurable
is an extraordinarily troublesome matter.
|
|
-Learned Hand, The Spirit of Liberty
|
|
Today, no programmer can avoid network programming, even if he wants to.
Most of the time, it seems, this is really Internet programming; transferring
files via FTP, getting information from the World Wide Web, or checking
the current time on a remote machine.
|
|
Perl supports all of these activities and so, by and large, does MacPerl. A
few similarities and differences are, however, worth noting:
|
|
-
Both Perl and MacPerl support the TCP and UDP1 protocols.
|
|
-
MacPerl has not implemented the ICMP protocol, used by the Unix
pingprogram. The Mac interface is incompatible with that of the Unix
protocol; it would be possible to write an extension for MacPerl to add
ICMP functionality, but at present none exists.
|
|
-
MacPerl supports AppleTalk, which Unix systems generally do not.2
|
|
|
|
1TCP (Transmission Control Protocol)is wrapped around the Internet Protocol (IP)
to form TCP/IP, the basis of reliable Internet communications. UDP (User Datagram
Protocol)is an alternate protocol used for situations (e.g., broadcast messages) where
guaranteed reception of ordered information is not required.
2A freeware package for Unix, netatalk, fills this need quite admirably.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
For more complete information than is presented here, see perlipcand the
documentation for the specific modules mentioned.
|
|
Sockets
|
|
A socket is a bi-directional interface for network communication between
two processes. One process writes to the socket; the other reads from it (and
vice-versa). A socket is thus something like a pair of Unix-style pipes, gen-
eralized for use over a network.
|
|
Each socket has its own address on the network. A web server, for instance,
would have an specific address(www.ptf.com), accessible by a specific
port(80) with a specific protocol(TCP) on a specific network (the Internet).
|
|
The standard Perl distribution contains two primary modules for dealing
with sockets. Socketis a low-level interface; IO::Socketis a high-level
"wrapper" for Socket. We will use IO::Socketfor most of our examples.
|
|
A socket is accessed through a filehandle. Like any other filehandle, you
can read from it (e.g., $foo = <SOCKET>;) and print to it using the normal
indirect object syntax (e.g., print SOCKET "some data";).
|
|
IO::Socket, however, returns a reference to a socket handle. As discussed
in Chapter 9, Curious Constructions, references to handles do not need to be
dereferenced. So, the returned reference can be substituted for SOCKET, as:
|
|
$foo = <$socket>;
print $socket "some data";
|
|
Here's an example of how to get the current time from a remote machine.
Many Internet servers provide the current time via port 13; here we try to
connect to the daytimeport of a web server, which IO::Socket::INET
resolves to port 13.
|
|
The new()method accepts a hash containing the address of the machine,
the port to connect to, and the protocol. It then creates a socket and connects
to the remote socket.
|
|
Most Internet communication uses CRLF (carriage return/line feed) for line
termination, but some uses just CR or LF. So, after we read from the server,
we convert any CRLFs or LFs to CRs, then display the results.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#!perl -w
use IO::Socket;
my($sock, $line, $cr, $lf, $crlf);
|
|
$cr = "\015";
$lf = "\012";
$/ = $crlf = "$cr$lf";
|
|
$sock = IO::Socket::INET->new(
PeerAddr => 'daytime.clock.org',
PeerPort => 'daytime(13)',
Proto=> 'tcp'
) or die($@);
|
|
while (defined($line = <$sock>)) {
$line =~ s/$cr?$lf/$cr/g;
print $line;
}
|
|
Displays:
|
|
Thu Dec 25 21:08:10 1997
|
|
Of course, we can also write to the filehandle. A slightly fancier script
could communicate with a web server, using the standard HTTP port (80).
After connecting, we print a request to the web server for a specified page
(GET /macperl/), tell the server what protocol we are using (HTTP/1.0),
then finish the request with two CRLFs. Finally, we read from the server,
converting any CRLFs or LFs to CRs, and display the results, as before.
|
|
...
PeerAddr => ' www.ptf.com',
PeerPort => 'http(80)',
...
print $sock "GET /macperl/ HTTP/1.0$crlf$crlf";
...
|
|
It is good to know how to do all this, and even better to know the workings
of the socket(), bind(), and connect()functions that IO::Socket
hides from your view. In most cases, however, you will not have to work
this hard. Here is an easier way ...
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
libnet
|
|
libnetis the common name for a group of Perl modules that support Internet
networking. It includes modules for FTP (file transfer), POP3 (receiving
email), SMTP (sending email), NNTP (accessing USENET news), etc. These
modules are included with the MacPerl standard distribution. For informa-
tion on updates, see the MacPerl Pages ( www.ptf.com/macperl).
|
|
Let's revisit the code we used above to get the current time. We can do the
same thing much more simply with the Net::Timemodule.
|
|
#!perl
use Net::Time qw(inet_daytime);
my($time);
|
|
$time = inet_daytime('daytime.clock.org');
$time =~ s/[\015\012]//g;
print "$time\n";
|
|
Displays:
|
|
Thu Dec 25 21:08:10 1997
|
|
One of the most important parts of libnet for MacPerl users, however, is the
Net::SMTPmodule. Perl programmers often send Internet mail (usually
using the SMTP protocol), using a Unix program called sendmail. MacPerl
users, sadly, do not have that luxury. No problem; they can use Perl!
|
|
The libnet modules have extra Mac OS magic when used with MacPerl. The
Internet Config host settings are automatically used as the defaults in the
configuration module Net::Config. Subsequently, instead of specifying an
SMTP host in the new()method, we let Internet Config supply that for us.
If Internet Config is not set up, the SMTP host must be supplied.3
|
|
#!perl
use strict;
use Net::SMTP;
use Mac::InternetConfig;
my($smtp, $email, @emails, $subject, $message);
|
|
|
|
3Also consider supplying other options to the new()constructor, including the
connection timeout.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$smtp= Net::SMTP->new();
$email= $InternetConfig{kICEmail()}; # my address
@emails= 'friend@some.host.com';
$subject = "Subject: My test message\n\n";
$message = <<EOM;
MacPerl sent this message. Nifty, eh?
EOM
|
|
$smtp->mail($email)or warn('failure');
$smtp->to(@emails)or warn('failure');
$smtp->data()or warn('failure');
$smtp->datasend($subject) or warn('failure');
$smtp->datasend($message) or warn('failure');
$smtp->dataend()or warn('failure');
$smtp->quit()or warn('failure');
|
|
The mail has now been sent, as long as none of the methods failed. See the
documentation of each module for the usage details. Also, remember that
you can use Internet Config for many of the default hosts.
|
|
You can also use the Mailtools modules, on the CD-ROM, for creating, man-
ipulating, and sending mail. Customized versions for MacPerl are usually
available at the MacPerl Pages. But Mailtools is not just for dealing with
individual messages; it can also be used for manipulating a mailbox such as
Eudora's. If you use Eudora, this script will get the first message from your
Inbox and display it:
|
|
#!perl
use Mail::Internet;
use Mail::Util qw(read_mbox);
use Mac::Files;
my($sysf, @msgs, $mail);
|
|
$sysf = FindFolder(kOnSystemDisk(),
kSystemFolderType());
@msgs = read_mbox("$sysf:Eudora Folder:In");
$mail = Mail::Internet->new($msgs[0]);
printf("From: %s", $mail->head->get("From:"));
printf("To: %s\n", $mail->head->get("To:"));
print @{$mail->body};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LWP
|
|
LWPis the class of the libwww-perlmodules, used for doing various web-
related functions. Itis commonly used to fetch pages on the web and to
manipulate their data; it is included with the MacPerl distribution.
|
|
The LWP::Simplemodule retrieves a WWW page. Revisiting our second
example under Socketsabove, the following script does basically the same
thing, except it does not return the HTTP headers, which you probably do
not want anyway, and it converts the line endings automatically.
|
|
#!perl -w
use LWP::Simple;
|
|
getprint(' http://www.ptf.com/macperl/');
|
|
The getprint()method gets the URL and prints the result, handling all
the details. LWPhas a lot more to offer; see its documentation ...
|
|
AppleTalk
|
|
Because MacPerl runs on Macintosh computers, we should be (and are!) able
to use the native protocol, AppleTalk, as our socket protocol. Even if an
Internet-style (TCP/IP, etc.) network is available, AppleTalk might be
preferable in some situations. For instance, AppleTalk does not care if the
numerical address of your computer changes!
|
|
The AppleTalk protocols and functions are provided the same way as they
are for Internet protocols, through the Socketmodule. Again, we make our
calls via IO::Socket, which inherits from Socket. Versions of MacPerl
prior to 5.1.6r4 do not recognize the subclass IO::Socket::APPLETALK,
you may need to upgrade to a more recent version.
|
|
The script below runs on two Macs that reside on the same AppleTalk net-
work.4AppleTalk does not connect to ports; instead, it uses services. Each
service has a name, a type, and a zone. These three characteristics must
form a unique address on the network. By default, the new()constructor
|
|
|
|
4It can also run on one Mac, if you have two versions of MacPerl (or the app and the
MPW tool), one running the client, and one running the server.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
will set the Typeto match the Object(name). The zone will default to *,
the default AppleTalk zone. These should be adjusted as necessary.
|
|
The only difference between the two calls to new()is that the server calls
the Listenparameter. The server then does a whileloop around the
accept()method, eventually closing the handle to the client.
|
|
Both sides of the connection are using Mac OS line termination, so we do not
have to worry about checking or changing the line termination characters.
|
|
#!perl -w
use strict;
use IO::Socket;
my($answer, $client, $line, $server, $sock);
|
|
$answer = MacPerl::Answer(
'Who are you?', 'Client', 'Server'
);
|
|
if ($answer) {
$sock = IO::Socket::APPLETALK->new(
Object=> 'MyAppleTalkSocket'
) or die('Cannot start client');
print $line while (defined($line = <$sock>));
|
|
} else {
$server = IO::Socket::APPLETALK->new(
Object=> 'MyAppleTalkSocket',
Listen=> 1,
) or die('Cannot start server');
|
|
printf("[Server at %s accepting clients]\n",
join '.', sockaddr_atlk(getsockname($server))
);
|
|
while ($client = $server->accept()) {
print $client "Connect!\n";
print "Connect!\n";
close($client);
}
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Debugging
|
|
Without a doubt, you will run into problems when working with sockets and
network programming. But, because these facilities do their work "behind
the scenes", it can be difficult to determine what is going wrong.
|
|
Hint:Always check return values. Most methods and functions return
undefor ()for failure, so it is easy to check if something fails (see the
Net::SMTPexample above), so be sure to do so!
|
|
You can't see what a socket is doing in a direct manner (as you could with a
text file or a window). Fortunately, OTSessionWatcher(included on the
CD-ROM) solves this problem. It prints an ASCII or binary representation
of the traffic that is going through OpenTransport TCP/IP (used by most
Macs for Internet networking). Using OTSessionWatcher, you can see what
data is being sent and received, as it is happening, and what is going wrong.
|
|
There are other similar methods you can employ in your own scripts, if you
don't wish to use OTSessionWatcher or if you are not using OpenTransport
for your program. Every time you send or receive data from a socket, you can
echo it to a file or to the MacPerl window:
|
|
print $sock"some data\015\012";
print STDOUT "some data\015\012";
while (defined($line = <$sock>)) {
print FILE$line;
print STDOUT $line;
}
|
|
This is not as nice as using OTSessionWatcher, which shows the raw bits of
data as they are passing through, but it can be of a big help. If the data you
are transmitting is binary, by the way, you may wish to format it and print
it in a more easily comprehensible (e.g., application-specific) form.
|
|
Note: The number one problem area in Internet socket programming,
especially in MacPerl, is in line termination. Make sure that your
program does exactly what you mean when you tell it to print a line
ending of any kind. Use the octal escape sequences \012and \015
(instead of \rand \n)to increase clarity and portability.
|
Copyright © 1997-1998 by Prime Time Freeware. All Rights Reserved.