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&amp;

$./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 &quot;getprotobyname: cannot get proto : $!&quot;;



    #

    # Bind a UDP port

    #

    socket(DGFD, PF_INET, SOCK_DGRAM, $proto);

    bind (DGFD,  sockaddr_in(0, INADDR_ANY)) or die &quot;bind: $!&quot;;



    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 &quot;./network.pl&quot;;

 5:

 6: $poke = &quot;yo!&quot;;

 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 &quot;error getting message : $!&quot;;

27:  print &quot;$message \n&quot;;

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 &quot;./network.pl&quot;;



$hello = &quot;Hello world!&quot;;



$LISTFD = makeudpserv(&quot;test&quot;);



while (1) {

    $cliaddr = recv $LISTFD, $message, 32768, 0;

    print &quot;Received $message from client\n&quot;;

    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&amp;

$./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 + -
显示快捷键?