A Nonblocking TCP Client Using IO::Poll


 
Network Programming with Perl
By Lincoln  D.  Stein
Slots : 1
Table of Contents
Chapter  16.   IO::Poll

    Content

As a practical example of IO::Poll, Figure 16.1 shows gab7.pl , the last of the gab series of Telnet-like TCP clients . This client is similar to the multiplexed gab5.pl client discussed in Chapter 12 (Figure 12.1). It tries to make an outgoing TCP connection to the host and port indicated on the command line or, if not otherwise specified, to the echo server on the local machine. It then copies its standard input to the socket and copies everything received on the socket to standard output. Like Telnet, gab7.pl can be used to talk directly to any of the conventional text-based servers.

Figure 16.1. The gab7.pl script uses IO::Poll to multiplex input and output

graphics/16fig01.gif

To make it more interesting, gab7.pl uses nonblocking I/O. Data read on STDIN is buffered to a scalar variable named $to_socket . Likewise, data received from the socket is buffered in $to_stdout . The data in the buffers is written to their appropriate destinations whenever poll() indicates that the operation won't block. If either buffer grows too large, then further reading from its associated input source is disabled until the buffer again has sufficient room.

Lines 1 “8: Load modules We begin by bringing in the IO::Socket and IO::Poll modules. IO::Poll doesn't import constants by default, so we must do this manually, asking for the POLLIN, POLLOUT , and POLLERR constants. We also bring in the Errno module so as to have access to the EWOULDBLOCK constant.

Lines 9 “10: Declare constants and globals We define the maximum size to which our internal buffers can grow. Further reading from the socket or STDIN is inhibited until the associated data buffer shrinks to a smaller size. We detect and handle PIPE errors, so we set the PIPE handler to IGNORE .

We define our globals. In addition to two scalars to hold buffered data, there are a pair of flags named $stdin_done and $sock_done . These flags are set to true when the corresponding handle is closed and are used during the determination of each handle's event mask.

Lines 11 “13: Open socket We read the desired hostname and port from the command line and connect in the usual way using IO::Socket.

Lines 14 “16: Create IO::Poll object We now create a new IO::Poll object and add the socket and STDIN filehandles to its list of monitored handles using the POLLIN mask. These masks will be adjusted when there is data to write as well as to read.

Lines 17 “18: Make filehandles nonblocking We now put the socket and STDOUT into nonblocking mode. This allows the client to continue working even if the socket or standard output are temporarily unable to accept new writes .

Lines 19 “20: Main loop We loop until there are no more handles to do I/O on. The loop condition is simply to check that the IO::Poll object's handles() method returns a nonempty list. At the very top of the loop we call poll() to block until IO::Poll indicates that one of the handles is ready for I/O.

Lines 21 “29: Handle readers The next chunk of code recovers the handles that have data to read or are signaling end of file by calling the IO::Poll object's handles() method with the mask POLLOUTPOLLERR .

If STDIN is ready for reading, we read from it and append the data to the variable $to_socket . Likewise, data from the socket is appended to $to_stdout . If either read fails, then we set one or both of the $stdin_done and $sock_done flags to true. We will check these flags at the end of the loop.

Lines 30 “48: Handle writers Now it's time for the writable handles. We call the IO::Poll object's handles() method with a flag that returns filehandles that are either writable or have errors.

If STDOUT is on the list, then we attempt to write the contents of $to_stdout to it. Likewise with $to_socket for the socket. Because both sockets are nonblocking, we have to deal with EWOULDBLOCK errors and with partial writes. The logic here is similar to that used in Chapter 13. On EWOULDBLOCK , we skip the filehandle and wait until later to try a write. On a partial read, we remove the portion of the buffer that was successfully written, leaving the unwritten portion to try later.

In the case of a syswrite() error that is not EWOULDBLOCK , we simply terminate with an error message.

Lines 49 “58: continue{} block The core logic of the program is all contained in the continue{} block, which is executed once at the end of each iteration of the loop. Its job is to create event masks for the three handles that are appropriate for the next iteration of the loop.

We begin by setting the three masks to a default of , which, if unchanged, removes the handle from the poll set. Next we examine the $to_stdout buffer. If it contains data, then we set the mask for STDOUT to POLLOUT , indicating that poll() should tell us when the handle is writable.

Similarly, we set the mask for STDIN to POLLIN , asking to be alerted when there is data to read from standard input. However, we suppress this if either of two circumstances apply: (1) the length of the buffer that contains data bound for the socket is already at its maximum value, in which case we don't want to make it larger; or (2) either the socket or standard input itself is closed.

Now we need to set the mask for the socket. Unlike standard input or output, the socket is read/write. If there is data to write to the socket ( $to_socket has nonzero length) and the socket was not previously closed, then we set its mask to POLLOUT . To this we add the POLLIN flag if the length of the buffer going to standard output is not already at its maximum.

Having created the masks, we call $poll->mask() three times to set them for their respective filehandles.

Line 59: Shut down the socket at termination time Our last step is to deal with the situation in which we reach the end of STDIN . As in the various versions of the gab client, the most elegant solution is to shut down our end of the socket for writing and then to wait for the peer to close down its end. The only twist here is that we don't want to do this while there is unsent data in the $to_socket buffer, so we wait for the length of the buffer to reach 0 before executing shutdown(1) .


   
Top


Network Programming with Perl
Network Programming with Perl
ISBN: 0201615711
EAN: 2147483647
Year: 2000
Pages: 173

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net