Java Network Programming (3rd ed) [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Java Network Programming (3rd ed) [Electronic resources] - نسخه متنی

Harold, Elliotte Rusty

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








12.1 An Example Client


Although the
new I/O APIs aren't specifically designed for
clients, they do work for them. I'm going to begin
with a client program using the new I/O APIs because
it's a little simpler. In particular, many clients
can be implemented with one connection at a time, so I can introduce
channels
and buffers before talking about selectors and non-blocking I/O.

To demonstrate the basics, I'll implement a simple
client for the character generator protocol defined in RFC
864. This protocol is designed for testing clients. The server
listens for connections on port 19. When a client connects, the
server sends a continuous sequence of characters until the client
disconnects. Any input from the client is ignored. The RFC does not
specify which character sequence to send, but recommends that the
server use a recognizable pattern. One common pattern is rotating,
72-character carriage return/linefeed delimited lines of the 95 ASCII
printing characters, like this:

!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm

I picked this protocol for the examples in this chapter because both
the protocol for transmitting the data and the algorithm to generate
the data are simple enough that they won't obscure
the I/O. However, chargen can transmit a lot of data over a
relatively few connections and quickly saturate a network connection.
It's thus a good candidate for the new I/O APIs.

When
implementing a client that takes advantage of Java
1.4's new I/O APIs, begin by invoking the static
factory method SocketChannel.open() to create a new
java.nio.channels.SocketChannel object. The
argument to this method is a
java.net.SocketAddress object indicating the host
and port to connect to. For example, this fragment connects the
channel to SocketAddress rama = new InetSocketAddress(", 19);
SocketChannel client = SocketChannel .open(rama);

The channel is opened in blocking mode, so the next line of code
won't execute until the connection is established.
If the connection can't be established, an
IOException is thrown.

If this were a traditional client, you'd now ask for
the socket's input and/or output streams. However,
it's not. With a
channel you write directly to the channel
itself. Rather than writing byte arrays, you write
ByteBuffer objects.
We've got a pretty good idea that the lines of text
are 74 ASCII characters long (72 printable characters followed by a
carriage return/linefeed pair) so we'll create a
ByteBuffer that has a 74-byte capacity using the
static allocate( ) method:

ByteBuffer buffer= ByteBuffer.allocate(74);

Pass this ByteBuffer object to the
channel's read( ) method. The
channel fills this buffer with the data it reads from the socket. It
returns the number of bytes it successfully read and stored in the
buffer:

int bytesRead = client.read(buffer);

By default, this will read at least one byte or return -1 to indicate
the end of the data, exactly as an InputStream
does. It will often read more bytes if more bytes are available to be
read. Shortly you'll see how to put this client in
non-blocking mode where it will return 0 immediately if no bytes are
available, but for the moment this code blocks just like an
InputStream. As you could probably guess, this
method can also throw an IOException if anything
goes wrong with the read.

Assuming there is some data in the bufferthat is, n >
0this data can be copied to System.out.
There are ways to extract a byte array from a
ByteBuffer that can then be written on a
traditional OutputStream such as
System.out. However, it's more
informative to stick with a pure, channel-based solution. Such a
solution requires wrapping the OutputStream
System.out in a channel using the
Channels utility class, specifically,
its newChannel( ) method:

WritableByteChannel output = Channels.newChannel(System.out);

You can then write the data that was read onto this output channel
connected to System.out. However, before you do
that you have to flip the
buffer
so that the output channel starts from the beginning of the data that
was read rather than the end:

buffer.flip( );
output.write(buffer);

You don't have to tell the output channel how many
bytes to write. Buffers keep track of how many bytes they contain.
However, in general, the output channel is not guaranteed to write
all the bytes in the buffer. In this specific case, though,
it's a blocking channel and it will either do so or
throw an IOException.

You shouldn't create a new buffer for each read and
write. That would kill the performance. Instead, reuse the existing
buffer.
You'll need to clear the buffer before reading into
it again:

buffer.clear( );

This is a little different than flipping. Flipping leaves the data in
the buffer intact, but prepares it for writing rather than reading.
Clearing resets the buffer to a pristine state.[2]

[2] Actually that's a tad simplistic. The old data
is still present. It's not overwritten, but it will
be overwritten with new data read from the source as soon as
possible.


Example 12-1 puts this together into a complete
client. Because chargen is by design an endless protocol,
you'll need to kill the program using Ctrl-C.


Example 12-1. A channel-based chargen client


import java.nio.*;
import java.nio.channels.*;
import java.net.*;
import java.io.IOException;
public class ChargenClient {
public static int DEFAULT_PORT = 19;
public static void main(String[] args) {
if (args.length == 0) {
System.out.println("Usage: java ChargenClient host [port]");
return;
}
int port;
try {
port = Integer.parseInt(args[1]);
}
catch (Exception ex) {
port = DEFAULT_PORT;
}
try {
SocketAddress address = new InetSocketAddress(args[0], port);
SocketChannel client = SocketChannel.open(address);
ByteBuffer buffer = ByteBuffer.allocate(74);
WritableByteChannel out = Channels.newChannel(System.out);
while (client.read(buffer) != -1) {
buffer.flip( );
out.write(buffer);
buffer.clear( );
}
}
catch (IOException ex) {
ex.printStackTrace( );
}
}
}

Here's the output from a sample run:

$ java ChargenClient 
!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefg
!"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefgh
"#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghi
#$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghij
$%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijk
%&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijkl
&'( )*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklm
...

So far, this is just an alternate vision of a program that could have
easily been written using streams. The really new feature comes if
you want the client to do something besides copying all input to
output. You can run this connection in either blocking or
non-blocking mode in which read( ) returns
immediately even if no data is available. This allows the program to
do something else before it attempts to read. It
doesn't have to wait for a slow network connection.
To change the blocking mode, pass true (block) or
false (don't block) to the
configureBlocking( ) method.
Let's make this connection non-blocking:

client.configureBlocking(false);

In non-blocking mode, read() may return 0 because it doesn't read
anything. Therefore the loop needs to be a little different:

while (true) {
// Put whatever code here you want to run every pass through the loop
// whether anything is read or not
int n = client.read(buffer);
if (n > 0) {
buffer.flip( );
out.write(buffer);
buffer.clear( );
}
else if (n == -1) {
// This shouldn't happen unless the server is misbehaving.
break;
}
}

There's not a lot of call for this in a
one-connection client like this one. Perhaps you could check to see
if the user has done something to cancel input, for example. However,
as you'll see in the next section, when a program is
processing multiple connections, this enables code to run very
quickly on the fast connections and more slowly on the slow ones.
Each connection gets to run at its own speed without being held up
behind the slowest driver on the one-lane road.


/ 164