📄 ch16_05.htm
字号:
socket(Server, PF_INET, SOCK_STREAM, getprotobyname('tcp'));# so we can restart our server quicklysetsockopt(Server, SOL_SOCKET, SO_REUSEADDR, 1);# build up my socket address$my_addr = sockaddr_in($server_port, INADDR_ANY);bind(Server, $my_addr) or die "Couldn't bind to port $server_port: $!\n";# establish a queue for incoming connectionslisten(Server, SOMAXCONN) or die "Couldn't listen on port $server_port: $!\n";# accept and process connectionswhile (accept(Client, Server)) { # do something with new Client connection}close(Server);</pre></blockquote>The client doesn't need to <tt class="literal">bind</tt> to any address, but the serverdoes. We've specified its address as <tt class="literal">INADDR_ANY</tt>, which meansthat clients can connect from any available network interface. If youwant to sit on a particular interface (like the external side of agateway or firewall machine), use that interface's real addressinstead. (Clients can do this, too, but rarely need to.)</p><p><a name="INDEX-3095"></a><a name="INDEX-3096"></a>If you want to know which machine connected to you, call<tt class="literal">getpeername</tt> on the client connection. This returnsan IP address, which you'll have to translate into a name on your own(if you can):<blockquote><pre class="programlisting">use Socket;$other_end = getpeername(Client) or die "Couldn't identify other end: $!\n";($port, $iaddr) = unpack_sockaddr_in($other_end);$actual_ip = inet_ntoa($iaddr);$claimed_hostname = gethostbyaddr($iaddr, AF_INET);</pre></blockquote>This is trivially spoofable because the owner of that IP addresscan set up their reverse tables to say anything they want. For a small measure of additional confidence, translate back the other way again:<blockquote><pre class="programlisting">@name_lookup = gethostbyname($claimed_hostname) or die "Could not reverse $claimed_hostname: $!\n";@resolved_ips = map { inet_ntoa($_) } @name_lookup[ 4 .. $#name_lookup ];$might_spoof = !grep { $actual_ip eq $_ } @resolved_ips;</pre></blockquote><a name="INDEX-3097"></a><a name="INDEX-3098"></a><a name="INDEX-3099"></a>Once a client connects to your server, your server can do I/O bothto and from that client handle. But while the server is so engaged,it can't service any further incoming requests from other clients.To avoid getting locked down to just one client at a time,many servers immediately <tt class="literal">fork</tt> a clone of themselves to handleeach incoming connection. (Others <tt class="literal">fork</tt> in advance, or multiplexI/O between several clients using the <tt class="literal">select</tt> syscall.)<blockquote><pre class="programlisting">REQUEST:while (accept(Client, Server)) { if ($kidpid = fork) { close Client; # parent closes unused handle next REQUEST; } defined($kidpid) or die "cannot fork: $!" ; close Server; # child closes unused handle select(Client); # new default for prints $| = 1; # autoflush # per-connection child code does I/O with Client handle $input = <Client>; print Client "output\n"; # or STDOUT, same thing open(STDIN, "<<&Client") or die "can't dup client: $!"; open(STDOUT, ">&Client") or die "can't dup client: $!"; open(STDERR, ">&Client") or die "can't dup client: $!"; # run the calculator, just as an example system("bc -l"); # or whatever you'd like, so long as # it doesn't have shell escapes! print "done\n"; # still to client close Client; exit; # don't let the child back to accept!}</pre></blockquote><a name="INDEX-3100"></a><a name="INDEX-3101"></a>This server clones off a child with <tt class="literal">fork</tt> for each incomingrequest. That way it can handle many requests at once, as long asyou can create more processes. (You might want to limit this.)Even if you don't <tt class="literal">fork</tt>, the <tt class="literal">listen</tt> will allow up to <tt class="literal">SOMAXCONN</tt>(usually five or more) pending connections. Each connection usesup some resources, although not as much as a process. Forkingservers have to be careful about cleaning up after their expiredchildren (called "zombies" in Unix-speak) because otherwise they'dquickly fill up your process table. The <tt class="literal">REAPER</tt> code discussedin the section <a href="ch16_01.htm#ch16-sect-signals">Section 16.1, "Signals"</a> will take care of that for you, or youmay be able to assign <tt class="literal">$SIG{CHLD} = 'IGNORE'</tt>.</p><p><a name="INDEX-3102"></a>Before running another command, we connect the standard input andoutput (and error) up to the client connection. This way any commandthat reads from <tt class="literal">STDIN</tt> and writes to <tt class="literal">STDOUT</tt> can also talk to theremote machine. Without the reassignment, the command couldn't findthe client handle--which by default gets closed across the <tt class="literal">exec</tt>boundary, anyway.</p><p><a name="INDEX-3103"></a><a name="INDEX-3104"></a><a name="INDEX-3105"></a>When you write a networking server, we strongly suggest that youuse the <tt class="userinput"><b>-T</b></tt> switch to enable taint checking even if you aren'trunning setuid or setgid. This is always a good idea for serversand any other program that runs on behalf of someone else (like allCGI scripts), because it lessens the chances that people from theoutside will be able to compromise your system. See the section<a href="ch23_01.htm#ch23-sect-hid">Section 16.1, "Handling Insecure Data"</a> in <a href="ch23_01.htm">Chapter 23, "Security"</a> for muchmore about all this.</p><p><a name="INDEX-3106"></a><a name="INDEX-3107"></a>One additional consideration when writing Internet programs: manyprotocols specify that the line terminator should be <tt class="literal">CRLF</tt>, which can bespecified various ways: <tt class="literal">"\015\12"</tt>, or <tt class="literal">"\xd\xa"</tt>, or even<tt class="literal">chr(13).chr(10)</tt>. As of version 5.6 of Perl, saying <tt class="literal">v13.10</tt> alsoproduces the same string. (On many machines, you can also use<tt class="literal">"\r\n"</tt> to mean <tt class="literal">CRLF</tt>, but don't use <tt class="literal">"\r\n"</tt> if you want to beportable to Macs, where the meanings of <tt class="literal">\r</tt> and <tt class="literal">\n</tt> are reversed!)Many Internet programs will in fact accept a bare <tt class="literal">"\012"</tt> as a lineterminator, but that's because Internet programs usually try to beliberal in what they accept and strict in what they emit. (Now if onlywe could get people to do the same...)</p><a name="INDEX-3108"></a><a name="INDEX-3109"></a><a name="INDEX-3110"></a><h3 class="sect2">16.5.3. Message Passing</h3><p><a name="INDEX-3111"></a><a name="INDEX-3112"></a><a name="INDEX-3113"></a>As we mentioned earlier, UDP communication involves much lower overheadbut provides no reliability, since there are no promises that messageswill arrive in a proper order--or even that they will arrive at all.UDP is often said to stand for Unreliable Datagram Protocol.</p><p>Still, UDP offers some advantages over TCP, including the ability tobroadcast or multicast to a whole bunch of destination hosts at once(usually on your local subnet). If you find yourself getting overly concernedabout reliability and starting to build checks into your message system,then you probably should just use TCP to start with. True, it costsmore to set up and tear down a TCP connection, but if you can amortize thatover many messages (or one long message), it doesn't much matter.</p><p><a name="INDEX-3114"></a>Anyway, here's an example of a UDP program. It contacts the UDP timeport of the machines given on the command line, or everybodyit can find using the universal broadcast address if no argumentswere supplied.<a href="#FOOTNOTE-13">[13]</a> Not allmachines have a time server enabled, especially across firewallboundaries, but those that do will send you back a 4-byte integerpacked in network byte order that represents what time that machinethinks it is. The time returned, however, is in the number ofseconds since 1900. You have to subtract the number of secondsbetween 1900 and 1970 to feed that time to the <tt class="literal">localtime</tt> or<tt class="literal">gmtime</tt> conversion functions.<blockquote><pre class="programlisting">#!/usr/bin/perl# clockdrift - compare other systems' clocks with this one# without arguments, broadcast to anyone listening.# wait one-half second for an answer.use v5.6.0; # or betteruse warnings;use strict;use Socket;unshift(@ARGV, inet_ntoa(INADDR_BROADCAST)) unless @ARGV;socket(my $msgsock, PF_INET, SOCK_DGRAM, getprotobyname("udp")) or die "socket: $!";# Some borked machines need this. Shouldn't hurt anyone else.setsockopt($msgsock, SOL_SOCKET, SO_BROADCAST, 1) or die "setsockopt: $!";my $portno = getservbyname("time", "udp") or die "no udp time port";for my $target (@ARGV) { print "Sending to $target:$portno\n"; my $destpaddr = sockaddr_in($portno, inet_aton($target)); send($msgsock, "x", 0, $destpaddr) or die "send: $!";}# daytime service returns 32-bit time in seconds since 1900my $FROM_1900_TO_EPOCH = 2_208_988_800;my $time_fmt = "N"; # and it does so in this binary formatmy $time_len = length(pack($time_fmt, 1)); # any number's finemy $inmask = ''; # string to store the fileno bits for selectvec($inmask, fileno($msgsock), 1) = 1;# wait only half a second for input to show upwhile (select(my $outmask = $inmask, undef, undef, 0.5)) { defined(my $srcpaddr = recv($msgsock, my $bintime, $time_len, 0)) or die "recv: $!"; my($port, $ipaddr) = sockaddr_in($srcpaddr); my $sendhost = sprintf "%s [%s]", gethostbyaddr($ipaddr, AF_INET) || 'UNKNOWN', inet_ntoa($ipaddr); my $delta = unpack($time_fmt, $bintime) - $FROM_1900_TO_EPOCH - time(); print "Clock on $sendhost is $delta seconds ahead of this one.\n";}</pre></blockquote></p><blockquote class="footnote"><a name="FOOTNOTE-13"></a><p>[13] If that doesn't work, run <em class="emphasis">ifconfig -a</em>to find the proper local broadcast address.</p></blockquote><a name="INDEX-3115"></a><a name="INDEX-3116"></a><!-- BOTTOM NAV BAR --><hr width="515" align="left"><div class="navbar"><table width="515" border="0"><tr><td align="left" valign="top" width="172"><a href="ch16_04.htm"><img src="../gifs/txtpreva.gif" alt="Previous" border="0"></a></td><td align="center" valign="top" width="171"><a href="index.htm"><img src="../gifs/txthome.gif" alt="Home" border="0"></a></td><td align="right" valign="top" width="172"><a href="ch17_01.htm"><img src="../gifs/txtnexta.gif" alt="Next" border="0"></a></td></tr><tr><td align="left" valign="top" width="172">16.4. System V IPC</td><td align="center" valign="top" width="171"><a href="index/index.htm"><img src="../gifs/index.gif" alt="Book Index" border="0"></a></td><td align="right" valign="top" width="172">17. Threads</td></tr></table></div><hr width="515" align="left"><!-- LIBRARY NAV BAR --><img src="../gifs/smnavbar.gif" usemap="#library-map" border="0" alt="Library Navigation Links"><p><font size="-1"><a href="copyrght.htm">Copyright © 2001</a> O'Reilly & Associates. All rights reserved.</font></p><map name="library-map"> <area shape="rect" coords="2,-1,79,99" href="../index.htm"><area shape="rect" coords="84,1,157,108" href="../perlnut/index.htm"><area shape="rect" coords="162,2,248,125" href="../prog/index.htm"><area shape="rect" coords="253,2,326,130" href="../advprog/index.htm"><area shape="rect" coords="332,1,407,112" href="../cookbook/index.htm"><area shape="rect" coords="414,2,523,103" href="../sysadmin/index.htm"></map><!-- END OF BODY --></body></html>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -