New I0 FunctionAliTY Javatm 2 Standard Edition 1.4

zhaozj2021-02-17  53

New I / 0 FunctionAliTY Javatm 2 Standard Edition 1.4

John Zukowskioctober, 2001

Back in January 2000, while many people were arguing about whether the year 2000 was the last or first year of the century, life began for JSR 51 as an approved Java Specification Request (JSR). The name of that JSR is New I / O APIs for the Java Platform. Many people think of the new capabilities as just offering non-blocking I / O operations. However, the new features introduced into the JavaTM 2 Platform, Standard Edition (J2SETM), version 1.4 Beta, include many other new and interesting features. While the API certainly will offer support for scalable I / O operations for both sockets and files, you'll also find a regular expression package for pattern matching, encoders and decoders for character set conversions, and improved file system support like File Locking and Memory Mapping. All Four of these New Features Will Be Covered In this article.

Note: The Java Native Interface (JNI) Changes Made To Support The New I / O Operations Will NOT BE COVERED. FOR Information On these Changes, See The Resources Section At The end of this article.

Buffers

Starting from the simplest and building up to the most complex, the first improvement to mention is the set of Buffer classes found in the java.nio package. These buffers provide a mechanism to store a set of primitive data elements in an in-memory container . Basically, imagine wrapping a combined DataInputStream / DataOutputStream around a fixed-size byte array and then only being able to read and write one data type, like char, int, or double There are seven such buffers available.:

Bytebuffer Charbuffer Doublebuffer Floatbuffer InTbuffer Longbuffer Shortbuffer

The ByteBuffer actually supports reading and writing the other six types, but the others are type specific. To demonstrate the use of a buffer, the following snippet converts a String to a CharBuffer and reads a character at a time. You convert the String to a Charbuffer with the wrad, the Get Each Letter with the get method.charbuffer buff = charbuffer.wrap (args [0]);

For (int i = 0, n = buff.length (); i

System.out.println (buff.get ());

}

When using buffers, it is important to realize there are different sizing and positioning values ​​to worry about. The length method is actually non-standard, specific to CharBuffer. There is nothing wrong with it, but it really reports the remaining length, so if .,......................

Charbuffer buff = charbuffer.wrap (args [0]);

For (int i = 0; buff.length ()> 0; i ) {

System.out.println (buff.get ());

}

Getting Back to the Different Sizing and Positioning Values, The Four Values ​​Are Known As Mark, Position, Limit, And Capacity:

Mark - Setable Position With Mark Method That Can Be Used To Reset The Position With Reset, <= position,> = 0 Position - Current Read / Write Position Wtem Be Read, <= Capacity Capacity - Size of Buffer,> = LIMIT

The position is an important piece of information to keep in mind when reading from and writing to a buffer. For instance, if you want to read what you just wrote you must move the position to where you want to read from, otherwise, you ' ll read past the limit and get whatever just happens to be there. This is where the flip method comes in handy, changing the limit to the current position and moving the current position to zero. You can also rewind a buffer to keep the current limit And Move The Position Back to Zero. for Example, Removing The Flip Call from The Following Snippet Will Get Back a Space, Assuming Nothing Was Put in The buffer Originally.buff.put ('a');

Buff.flip ();

Buff.get ();

The wrap mechanism shown above is an example of a non-direct buffer. Non-direct buffers can also be created and sized with the allocate method, essentially wrapping the data into an array. At a slightly higher creation cost, you can also create a Contiuous Memory Block, Also Called A Direct Buffer, with the allocatedirect method. Direct Buffers Rely on The System's Native I / O Operations To Optimize Access Operations.

Mapped files

There is one specialized form of direct ByteBuffer known as a MappedByteBuffer. This class represents a buffer of bytes mapped to a file. To map a file to a MappedByteBuffer, you first must get the channel for a file. A channel represents a connection to something , such as a pipe, socket, or file, that can perform I / O operations. In the case of a FileChannel, you can get one from a FileInputStream, FileOutputStream, or RandomAccessFile through the getChannel method. Once you have the channel, you Map it to a buffer with map, specifying the mode and portion of the file you want to map. The file number can be opened-only (map_ro), Copy-on-write (map_cow), or read-write (map_rw) .Here's The Basic Process for Creating A Read-Only MappedBytebuffer from A File:

String filename = ...;

FileInputStream Input = New

FileInputStream (filename);

Filechannel Channel = Input.getChannel ();

Int fileLength = (int) channel.size ();

Mappedbytebuffer buffer =

Channel.map (FileChannel.map_ro, 0, FileLength);

You'll Find The Channel-Related Classes in the java.nio.channels package.

Once the MappedByteBuffer has been created, you can access it like any other ByteBuffer. In this particular case though, it is read-only, so any attempt to put something will throw an exception, NonWritableChannelException in this case. If you need to treat the bytes as characters, you must convert the ByteBuffer into a CharBuffer through the use of a character set for the conversion. This character set is specified by the Charset class. you then decode the file contents through the CharsetDecoder class. There is also a CharsetEncoder to Go in The Other Direction.

// ISO-8859-1 is iso latin alphabet # 1

Charset Charset = Charset.Forname ("ISO-8859-1"); charsetdecoder decoder = charset.newdecoder ();

CharBuffer Charbuffer = decoder.decode (buffer);

The Java.Nio.charset package.

Regular Expressions

Once you've mapped the input file to a CharBuffer, you can do pattern matching on the file contents. Think of running grep or wc on the file to do regular expression matching or word counting, respectively. That's where the java.util.regex Package comes INTO Play and the pattern and matcher classes get used.

The Pattern class provides a whole slew of constructs for matching regular expressions Basically, you provide the pattern as a String See the class documentation for full details of the patterns Here are some samples to get you started...:

Line Pattern, Any Number of Characters Followed by Carriage Return / or Line Feed: * $ Series of Numbers: [0-9] * OR / D * a control character {cntrl} an Upper Or Lowercase Us-Ascii Character, FOLLOWED by White Space, Followed by Puncture: [{Lower} {Upper}] / s {punct}

When you provide the pattern, you tell the Pattern class to compile it. Because pattern matching tries to find the largest possible match, in the case of the end-of-line character ($), you do not want to match the entire file to the end of it. You must use the compile option of MULTILINE. There are other options for tasks like case-insensitive matching and Unicode-aware case folding, among a few others. So, if your pattern was for the line pattern above , The Code Would Look Like Such:

Pattern linepattern = pattern.compile (". * $",

Pattern.multiline;

When it is time to match the pattern, you call the matcher method to get a Matcher back. From this, you can find out if the pattern matches or find and get the matching piece with group, or you can split the string by providing the break pattern, and getting the individual pieces back with split for instance, the following is a framework for reading a line at a time and getting words out of each line.Matcher matcher = p.matcher (aString).;

Pattern WordBreakPattern =

Pattern.Compile ("[{space} {punct}]");

// Loop THROUGH THE LINES

While (LINEMATCHER.FIND ()) {

Charsequence line = linematcher.group ();

String word [] = wordbreakpattern.split (line);

// for Each Word

For (int i = 0, n = words.Length; i

// Lines with Just Break Characters Return an Empty String

IF (Words [i] .length ()> 0) {

System.out.println (":" Words [i] ":");

}

}

}

There IS Also A Shortcut for matching with boolean b = pattern.matches (". * / R? / N", asseting, but it it isn't effect for when you need to recheck for matches as the pattern is not compiled.

To Combine All The Previously Mentioned Skills, The Following Example Performs a Word / Line Count On File Passed Into The Program:

Import java.io. *;

Import java.nio. *;

Import java.nio.channels. *;

Import java.nio.charset. *;

Import java.util. *;

Import java.util.regex. *;

Public class wordcount {

Public static void main (string args []) throws

EXCEPTION {

String filename = args [0];

// map file from filename to byte buffer

FileInputStream Input = New

FileInputStream (filename);

Filechannel Channel = Input.getChannel ();

Int fileLength = (int) channel.size ();

Mappedbytebuffer buffer =

CHANNEL.MAP (filechannel.map_ro, 0,

FileLength);

// Convert to Character Buffer

Charset Charset = Charset.Forname ("ISO-8859-1");

CharSetDecoder decoder = charset.newdecoder ();

CharBuffer Charbuffer = decoder.decode (buffer);

// CREATE LINE PATTERN

Pattern linepattern = pattern.compile (". * $",

Pattern.multiline;

// CREATE WORD PATTERN

Pattern WordBreakPattern =

Pattern.Compile ("[{space} {punct}]");

// Match line Pattern to Buffer

Matcher linematcher =

LinePattern.matcher (Charbuffer);

Map map = new treMap ();

Integer One = new integer (1);

// for Each Line

While (LINEMATCHER.FIND ()) {

// Get Line

Charsequence line = linematcher.group ();

// Get Array of Words on line

String word [] = wordbreakpattern.split (line);

// for Each Word

For (int i = 0, n = words.Length; i

IF (Words [i] .length ()> 0) {

Integer Frequency =

(Integer) Map.get (Words [i]);

IF (frequence == null) {

FREQUENCY = one;

} else {

Int value = frequency.intvalue ();

FREQUENCY = New Integer (Value 1);

}

Map.put (Words [i], frequency);

}

}

}

System.out.println (MAP);

}

}

.

Socket Channels

Moving on from file channels takes us to channels for reading from and writing to socket connections. These channels can be used in a blocking or non-blocking fashion. In the blocking fashion, they just replace the call to connect or accept, depending on whether YOU ARE A Client OR A Server. in The Non-Blocking Fashion, There is no equivalent.

The new classes to deal with for basic socket reading and writing are the InetSocketAddress class in the java.net package to specify where to connect to, and the SocketChannel class in the java.nio.channels package to do the actual reading and writing operations. Connecting with inetsocketaddress is very Similar To Working with the socket class. All you have to do is provide the host and port:

String host = ...;

Inetsocketaddress socketaddress = new

InetsocketAddress (Host, 80);

Once you have the InetSocketAddress, that's where life changes Instead of reading from the socket's input stream and writing to the output stream, you need to open a SocketChannel and connect it to the InetSocketAddress.:

Socketchannel Channel = SocketChannel.Open ();

Channel.connect (socketaddress);

Once Connected, you can read from or writh to the channels. For instance, you can WTRING IN A Charbuffer with the help of an charstencoder to send an http request:

Charset Charset = Charset.Forname ("ISO-8859-1");

Charsetencoder Encoder = charset.newencoder ();

String request = "get / / n / r / n / R";

Channel.Write (Encoder.Encode (Charbuffer.wrap (Request));

You can then read the response from the channel. Since the response for this HTTP request will be text, you'll need to convert that response into a CharBuffer through a CharsetDecoder. By creating just a CharBuffer to start, you can keep reusing the object To Avoid Unnecessary Garbage Collection Between Reads:

Bytebuffer buffer = bytebuffer.allocateDirect (1024);

Charbuffer charbuffer = charbuffer.allocate (1024);

While (Channel.Read (Buffer))! = -1) {

Buffer.flip ();

Decoder.Decode (Buffer, Charbuffer, False); Charbuffer.flip ();

System.out.println (Charbuffer);

Buffer.clear ();

Charbuffer.clear ();

}

The following program connects all these pieces to read the main page of a Web site through an HTTP request. Feel free to save the output to a file to compare the results to viewing the page with a browser.

Import java.io. *;

Import java.net. *;

Import java.nio. *;

Import java.nio.channels. *;

Import java.nio.charset. *;

Public class readurl {

Public static void main (string args []) {

String host = args [0];

SocketChannel Channel = NULL;

Try {

// setup

Inetsocketaddress socketaddress =

New InetSocketAddress (Host, 80);

Charset charset =

Charset.Forname ("ISO-8859-1");

CharSetDecoder decoder =

Charset.newdecoder ();

CharsetEncoder Encoder =

Charset.newencoder ();

// allocate buffers

BYTEBUFFER BUFFER =

Bytebuffer.allocatedIRECT (1024);

Charbuffer charbuffer =

Charbuffer.allocate (1024);

// connect

Channel = Socketchannel.open ();

Channel.connect (socketaddress);

// send request

String request = "get / / n / r / n / R";

Channel.Write (Encoder.Encode (Charbuffer.wrap (Request));

// read response

While (Channel.Read (Buffer))! = -1) {

Buffer.flip ();

// Decode Buffer

Decoder.Decode (Buffer, Charbuffer, False);

// Display

Charbuffer.flip ();

System.out.println (Charbuffer);

Buffer.clear ();

Charbuffer.clear ();

}

} catch (unknownhostexception e) {

System.err.Println (e);

} catch (ioexception e) {

System.err.Println (e);

} finally {

IF (CHANNEL! = NULL) {

Try {

Channel.Close ();

} catch (ioexception ignored) {

}

}

}

}

}

Non-blocking reads

Now comes the interesting part, and what people are most interested in in the new I / O packages. How do you configure the channel connection to non-blocking? The basic step is to call the configureBlocking method on the opened SocketChannel, and pass in a value of false. Once You Call The Connect Method, The Method Now Returns Immediately.String Host = ...

Inetsocketaddress socketaddress =

New InetSocketAddress (Host, 80);

Channel = Socketchannel.open ();

Channel.configureblocking (false);

Channel.connect (socketaddress);

Once you have a non-blocking channel, you then have to figure out how to actually work with the channel. The SocketChannel is an example of a SelectableChannel. These selectable channels work with a Selector. Basically, you register the channel with the Selector, Tell The Selector What Events You Are Interested in, And It Notifies You When Something Interesting Happens.

To Get a selector instance, Just Call The Static Open method of the class:

Selector selector = selector.open ();

Registering with the Selector is done through the register method of the channel. The events are specified by fields of the SelectionKey class. In the case of the SocketChannel class, the available operations are OP_CONNECT, OP_READ, and OP_WRITE. So, if you were interested In Read and Connection Operations, you would register as Follows:

Channel.Register (Selector,

SelectionKey.op_connect | SelectionKey.op_read;

At this point, you have to wait on the selector to tell you when events of interest happen on registered channels. The select method of the Selector will block until something interesting happens. To find this out, you can put a while (selector.select ()> 0) loop in its own thread and then go off and do your own thing while the I / O events are being processed. The select method returns when something happens, where the value returned is the count of channels ready to be acted upon. This value does not really matter though.Once something interesting happens, you have to figure out what happened and respond accordingly. For the channel registered here with the selector, you expressed interest in both the OP_CONNECT and OP_READ operations, so you know it can only be one of those events. So, what you do is get the Set of ready objects through the selectedKeys method, and iterate. The element in the Set is a SelectionKey, and you can check if it isConnectable or isReadable for the two States Of intert.

Here's The Basic Framework of The loop So Far:

While (selector.select (500)> 0) {

// Get Set of Ready Objects

Set readyKeys = select .selectedKeys ();

Iterator readyitor = readyKeys.Item ();

// Walk THROUGH SET

WHILE (readyitor.hasnext ()) {

// Get key from set

SelectionKey Key =

(SelectionKey) Readyitor.next ();

// Remove Current Entry

Readyitor.remove ();

// Get Channel

SocketChannel KeyChannel =

(Socketchannel) key.channel ();

IF (Key.isconnectable ()) {

} else if (Key.IsReadable ()) {

}

}

}

The remove method call requires a little explanation. The ready set of channels can change while you are processing them. So, you should remove the one you are processing when you process it. There's also a timeout setup here for the select call so it doesn 't wait forever if there is nothing to do. there's also a call to get the channel from the key in there. you'll need that for each operation.For the sample program here you're doing the equivalent of reading from an HTTP connection, so upon connection you need to send the initial HTTP request. Basically, once you know the connection is made, you send a GET request for the root of the site. When the selector reports that the channel is connectable, it may not have finished connecting yet. So, you should always check if the connection is pending through isConnectionPending and call finishConnect if it is. Once connected, you can write to the channel, but must use a ByteBuffer, not the more familiar I / O streams.

Here's What The Connection Code Looks Like:

// Outside While Loop

Charset charset =

Charset.Forname ("ISO-8859-1");

Charsetencoder Encoder = charset.newencoder ();

// inside if (channel.isconnectable ())

// Finish Connection

KeyChannel.isconnectionped ()) {

KeyChannel.finishConnect ();

}

// send request

String request = "get / / n / r / n / R";

KeyChannel.write

(Encoder.Encode (Charbuffer.wrap (Request));

The reading from a socket channel is just like from a file channel. There is one exception though. It is more likely that the buffer may not be full when reading from a socket. Not a big deal though, as you are just going to read What is ready.

// Outside While Loop

CharsetDecoder decoder = charset.newdecoder (); bytebuffer buffer = bytebuffer.allocateDirect (1024);

Charbuffer charbuffer = charbuffer.allocate (1024);

// inside if (Channel.Isreadable ())

// read what's ready in response

KeyChannel.read (Buffer);

Buffer.flip ();

// Decode Buffer

Decoder.Decode (Buffer, Charbuffer, False);

// Display

Charbuffer.flip ();

System.out.print (Charbuffer);

// Clear for Next Pass

Buffer.clear ();

Charbuffer.clear ();

. Add in the necessary exception handling code and you have your socket reader Be sure to close the channel in the finally clause to make sure its resources are released, even if there is an exception Here's the complete client code.:

Import java.io. *;

Import java.net. *;

Import java.nio. *;

Import java.nio.channels. *;

Import java.nio.charset. *;

Import java.util. *;

Public class nonblockingreadurl {

Static selector selector;

Public static void main (string args []) {

String host = args [0];

SocketChannel Channel = NULL;

Try {

// setup

Inetsocketaddress socketaddress =

New InetSocketAddress (Host, 80);

Charset charset =

Charset.Forname ("ISO-8859-1");

CharSetDecoder decoder =

Charset.newdecoder ();

CharsetEncoder Encoder =

Charset.newencoder ();

// allocate buffers

BYTEBUFFER BUFFER =

Bytebuffer.allocatedIRECT (1024);

Charbuffer charbuffer =

Charbuffer.allocate (1024);

// connect

Channel = Socketchannel.open ();

Channel.configureblocking (false);

Channel.connect (socketaddress);

// Open Selector

Selector = selector.open ();

// Register Interest in When Connection

Channel.Register (Selector,

SelectionKey.op_Connect |

SelectionKey.op_read;

// Wait for Somethingy Of Interest to Happen

While (selector.select (500)> 0) {// Get Set of Ready Objects

Set readyKeys = select .selectedKeys ();

Iterator readyitor = readyKeys.Item ();

// Walk THROUGH SET

WHILE (readyitor.hasnext ()) {

// Get key from set

SelectionKey Key =

(SelectionKey) Readyitor.next ();

// Remove Current Entry

Readyitor.remove ();

// Get Channel

SocketChannel KeyChannel =

(Socketchannel) key.channel ();

IF (Key.isconnectable ()) {

// Finish Connection

KeyChannel.isconnectionped ()) {

KeyChannel.finishConnect ();

}

// send request

String Request =

"GET / / N / R / N / R";

KeyChannel.write (Encoder.Encode)

Charbuffer.wrap (request));

} else if (Key.IsReadable ()) {

// read what's ready in response

KeyChannel.read (Buffer);

Buffer.flip ();

// Decode Buffer

Decoder.Decode (Buffer,

Charbuffer, false;

// Display

Charbuffer.flip ();

System.out.print (Charbuffer);

// Clear for Next Pass

Buffer.clear ();

Charbuffer.clear ();

} else {

System.err.Println ("OOOPS");

}

}

}

} catch (unknownhostexception e) {

System.err.Println (e);

} catch (ioexception e) {

System.err.Println (e);

} finally {

IF (CHANNEL! = NULL) {

Try {

Channel.Close ();

} catch (ioexception ignored) {

}

}

}

SYSTEM.OUT.PRINTLN ();

}

}

Non-blocking servers

The final piece is having a Web server use the NIO package. With the new I / O capabilities, you can create a Web server that does not require one thread per connection. You can certainly pool threads for long processing tasks, but all you have To do is select and wait for something to do, not have all the threads waiting separately.

The Basic Setup of The Server Using Channels Involves You Calling Bind To Connect A Servetsocketchannel To a inetsocketaddress.serversocketchannel Channel =

ServersocketChannel.open ();

Channel.configureblocking (false);

Inetsocketaddress isa =

New inetsocketaddress (port);

Channel.socket (). Bind (ISA);

Every, else, except this time you need, and check for isacceptable. It is what simple.

The following code example shows just how simple this is. It is your basic single-threaded server, sending back a canned text message for each request. Just use telnet to connect to port 9999 and see the response.

Import java.io. *;

Import java.net. *;

Import java.nio. *;

Import java.nio.channels. *;

Import java.util. *;

Public class server {

Private static int port = 9999;

Public static void main (string args [])

Throws exception {

Selector selector = selector.open ();

Serversocketchannel Channel =

ServersocketChannel.open ();

Channel.configureblocking (false);

Inetsocketaddress isa =

New inetsocketaddress (port);

Channel.socket (). Bind (ISA);

// Register Interest in When Connection

Channel.Register (Selector,

SelectionKey.op_Accept);

// Wait for Somethingy Of Interest to Happen

While (selector.select ()> 0) {

// Get Set of Ready Objects

Set readyKeys = select .selectedKeys ();

Iterator readyitor = readyKeys.Item ();

// Walk THROUGH SET

WHILE (readyitor.hasnext ()) {

// Get key from set

SelectionKey Key =

(SelectionKey) Readyitor.next ();

// Remove Current Entry

Readyitor.remove ();

IF (Key.isacceptable ()) {

// Get Channel

ServersocketChannel KeyChannel =

(ServersocketChannel) key.channel (); // ACCEPT REQUEST

Socket Socket = KeyChannel.accept ();

// Return CANNED Message

PrintWriter out = new printwriter

Socket.getOutputStream (), true);

Out.println ("Hello, NIO");

Out.close ();

} else {

System.err.Println ("OOOPS");

}

}

}

// NEVER ENDS

}

}

After accepting the request, you could get the channel from the socket, make it non-blocking, and register that with the selector, too. This framework just provides the basics of using the NIO classes within a Web server. For additional information about creating A Multi-Threaded Server, See The Javaworld Article Reference in The Resources Section.

Conclusion

The New I / O features introduced to the J2SE version 1.4 Beta release provide exciting new ways to improve the performance of your programs. By taking advantage of the new capabilities, not only will they be faster but they can be much more scalable because you won 'T Have to Worry About Tasks Like One Thread Per Connection. This is especially important on the server side, greatly increasing the Possible Number of Simultaneous Connections Supported.

Note: If you look at the list of capabilities in JSR 51, you'll notice there is mention of scanning and formatting support, similar to C's printf This feature did not make the 1.4 beta release and will be saved for a later version. .

转载请注明原文地址:https://www.9cbs.com/read-28634.html

New Post(0)