0593-0596.html
来自「linux-unix130.linux.and.unix.ebooks130 l」· HTML 代码 · 共 422 行
HTML
422 行
<HTML>
<HEAD>
<TITLE>Developer.com - Online Reference Library - 0672311739:RED HAT LINUX 2ND EDITION:Network Programming</TITLE>
<META NAME="ROBOTS" CONTENT="NOINDEX, NOFOLLOW">
<SCRIPT>
<!--
function displayWindow(url, width, height) {
var Win = window.open(url,"displayWindow",'width=' + width +
',height=' + height + ',resizable=1,scrollbars=yes');
}
//-->
</SCRIPT>
</HEAD>
-->
<!-- ISBN=0672311739 //-->
<!-- TITLE=RED HAT LINUX 2ND EDITION //-->
<!-- AUTHOR=DAVID PITTS ET AL //-->
<!-- PUBLISHER=MACMILLAN //-->
<!-- IMPRINT=SAMS PUBLISHING //-->
<!-- PUBLICATION DATE=1998 //-->
<!-- CHAPTER=28 //-->
<!-- PAGES=0583-0604 //-->
<!-- UNASSIGNED1 //-->
<!-- UNASSIGNED2 //-->
<P><CENTER>
<a href="0590-0592.html">Previous</A> | <a href="../ewtoc.html">Table of Contents</A> | <a href="0597-0599.html">Next</A>
</CENTER></P>
<A NAME="PAGENUM-593"><P>Page 593</P></A>
<!-- CODE //-->
<PRE>
LOOP: while (1) {
unless ($paddr = accept(NEWFD, $LISTFD)) {
next LOOP;
}
syswrite(NEWFD, $hello, length($hello));
close NEWFD;
}
</PRE>
<!-- END CODE //-->
<P>In Listing 28.5, you simply place a socket in the listen state using
makelisten() and then enter a while loop that centers on the function
accept(). The purpose of accept() is exactly as it sounds: It accepts client connections. You pass two arguments to
accept(): a new variable (NEWFD) that will contain the socket identifier for the accepted connection and the socket
($LISTFD) that has been set up with listen().
</P>
<P>Whenever accept() returns a connection, you write a string to the new socket and
immediately close it.
</P>
<P>Before you can test your server, you need to add the entry for the test service that it uses.
Add the following three lines to the /etc/services file. You will have to be
root in order to edit this file.
</P>
<!-- CODE SNIP //-->
<PRE>test 8000/tcp
test 8000/udp
test1 8001/udp
</PRE>
<!-- END CODE SNIP //-->
<P>You have added three entries for your test programs, one for TCP and two others for
UDP that you will use later.
</P>
<P>Now to test your server, you need to execute the following commands:
</P>
<!-- CODE SNIP //-->
<PRE>$ ./server1&
$./client1 iest test
Hello world!
</PRE>
<!-- END CODE SNIP //-->
<P>iest is the hostname of your workstation. The server writes back your greeting and exits.
Because the server is executing inside a while loop, you can run
./client1 repeatedly. When the test is finished, use
kill to stop the server:
</P>
<!-- CODE SNIP //-->
<PRE>$ ps ax| grep server1 | awk `{ print $1 }'
pid
$ kill pid
</PRE>
<!-- END CODE SNIP //-->
<H3><A NAME="ch28_ 9">
A UDP Example
</A></H3>
<P>In order to implement the same test in UDP, you have to set up a
SOCK_DGRAM socket for both a client and a server. This function,
makeudpcli(), can also be found in network.pl and is
shown in Listing 28.6.
</P>
<A NAME="PAGENUM-594"><P>Page 594</P></A>
<P>Listing 28.6. makeudpcli().
</P>
<!-- CODE //-->
<PRE>sub makeudpcli {
my ($proto, $servaddr);
$proto = getprotobyname(`udp') or
die "getprotobyname: cannot get proto : $!";
#
# Bind a UDP port
#
socket(DGFD, PF_INET, SOCK_DGRAM, $proto);
bind (DGFD, sockaddr_in(0, INADDR_ANY)) or die "bind: $!";
return DGFD;
}
</PRE>
<!-- END CODE //-->
<P>In Listing 28.6, you retrieve the protocol information for UDP and then create a
SOCK_DGRAM socket. You then bind it, but you tell the system to go ahead and bind to any address and
any service port; in other words, you want the socket named but don't care what that name is.
</P>
<P>The reason for this extra bind() is quite straightforward. Because UDP is connectionless,
special attention has to be made to addresses when sending and receiving datagrams. When
datagram messages are read, the reader also receives the address of the originator so that it knows
where to send any replies. If you want to receive replies to your messages, you need to guarantee
that they come from a unique address. The call to
bind() ensures that the system allocates a unique address for you.
</P>
<P>Now that you have created a datagram socket, you can communicate with a server, using
the program in Listing 28.7, client2, which can be found on the CD-ROM.
</P>
<P>Listing 28.7. client2.
</P>
<!-- CODE //-->
<PRE>
1: #!/usr/bin/perl
2:
3: use Socket;
4: require "./network.pl";
5:
6: $poke = "yo!";
7:
8: $NETFD = makeudpcli();
9:
10: #
11: # Work out server address
12: #
13: $addr = gethostbyname($ARGV[0]);
14: $port = getservbyname($ARGV[1], `udp');
15:
16: $servaddr = sockaddr_in($port, $addr);
17:
18: #
19: # Poke the server
20: #
</PRE>
<!-- END CODE //-->
<A NAME="PAGENUM-595"><P>Page 595</P></A>
<!-- CODE //-->
<PRE>
21: send $NETFD, $poke, 0, $servaddr;
22:
23: #
24: # Recv the reply
25: #
26: recv $NETFD, $message, 32768, 0 or die "error getting message : $!";
27: print "$message \n";
28: close $NETFD;
</PRE>
<!-- END CODE //-->
<P>After you create the socket, you still have to resolve the server address, but instead of
providing this address to the connect() function, you have to provide it to the
send() function in line 21 so it knows where to, well, send the message. But why are you sending anything to the
server at all? After all, in the TCP example in Listing 28.3, the communication is one way.
</P>
<P>In the TCP example, the server sends a message as soon as you connect and then closes
the session. The act of connecting is in effect a message from the client to the server. Because
UDP lacks connections, you have to use a message from the client as a trigger for the conversation.
</P>
<P>The server creates a UDP socket in a slightly different manner because it needs to bind a
well-known port. It uses getservbyname() to retrieve a port number and specifies it as part of
the call to bind(). Look at makeudpserv() in
network.pl for details.
</P>
<P>The server's main loop is actually pretty close to that of the TCP server and is shown in
</P>
<P>Listing 28.8.
</P>
<P>Listing 28.8. server2.
</P>
<!-- CODE //-->
<PRE>#!/usr/bin/perl
#
#
use Socket;
require "./network.pl";
$hello = "Hello world!";
$LISTFD = makeudpserv("test");
while (1) {
$cliaddr = recv $LISTFD, $message, 32768, 0;
print "Received $message from client\n";
send $LISTFD, $hello, 0, $cliaddr;
}
</PRE>
<!-- END CODE //-->
<P>Instead of waiting for a client by looping on the
accept() function, the server loops on the recv() function. There is also no new socket to close after the reply is sent to the client.
</P>
<P>When these programs are run, you see the following:
</P>
<!-- CODE SNIP //-->
<PRE>$ ./server2&
$./client2 iest test
Received yo! from client
Hello world!
</PRE>
<!-- END CODE SNIP //-->
<A NAME="PAGENUM-596"><P>Page 596</P></A>
<P>So you see that from a programmer's standpoint, the differences between UDP and TCP
affect not only the socket functions you use and how you use them, but also how you
design your programs. Differences such as the lack of a connection and the lack of built-in
reliability mechanisms must be seriously considered when you design an application. There is no
guarantee, for example, that the server in this section ever receives your poke message. For that
reason, a mechanism such as a timer would be employed in a real-world application.
</P>
<H3><A NAME="ch28_ 10">
Blocking Versus Nonblocking Descriptors
</A></H3>
<P>So far, all the examples in this chapter have relied on blocking I/O. Certain operations, such
as reading, writing, and connecting or accepting connections, are set to block when they wait
for completion, which brings a program (or thread) to a halt. After
server1 sets up a listen, for example, it enters the
while loop and calls accept(). Until a client connects to the
listening socket, the program is halted. It doesn't repeatedly call
accept(); it calls it once and blocks. This condition is also true for
client2, which blocks on the recv() call until the server
replies. If the server is unavailable, the program will block forever. This is especially unwise for
an application that uses UDP, but how could a timer be implemented if the call to
recv() will never return?
</P>
<P>Writing can also block on TCP connections when the receiver of the data hasn't read
enough data to allow the current write to complete. In order to maintain reliability and proper
flow control, the systems on both ends of a connection maintain buffers, usually about 8192
bytes. If these buffers are full in either direction, communications in that direction will cease
until some space is freed up. This is yet another concern for servers that are writing large messages
to clients that aren't running on very powerful systems or are on remote networks with low
bandwidth links. In these scenarios, one client can slow things down for everyone.
</P>
<P>Blocking I/O is acceptable for programs that don't have to maintain GUI interfaces and
only have to maintain one communications channel. Needless to say, most programs cannot
afford to use blocking communications.
</P>
<P>I/O is said to be nonblocking when an operation returns an error or status code when it
cannot be completed. To demonstrate this, run
client2 without running the server. It will start
and not return until you halt it by pressing Ctrl+C.
</P>
<P>Now run nonblock:
</P>
<!-- CODE SNIP //-->
<PRE>$ ./nonblock
error getting message : Try again at ./nonblock line 30
</PRE>
<!-- END CODE SNIP //-->
<P>You receive the Try again message from the
recv() function.
</P>
<P>nonblock, shown in Listing 28.9, is a modified version of
client2, which is shown is Listing 28.7. Let's see what changes were made to
client2 to remove blocking.
</P>
<P><CENTER>
<a href="0590-0592.html">Previous</A> | <a href="../ewtoc.html">Table of Contents</A> | <a href="0597-0599.html">Next</A>
</CENTER></P>
</td>
</tr>
</table>
<!-- begin footer information -->
</body></html>
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?