epubjs
Version:
Render ePub documents in the browser, across many devices
370 lines (357 loc) • 51.3 kB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head><title>Scalable I/O with NIO</title><link rel="stylesheet" href="core.css" type="text/css"/><meta name="generator" content="DocBook XSL Stylesheets V1.74.0"/></head><body><div class="sect1" title="Scalable I/O with NIO"><div class="titlepage"><div><div><h1 class="title"><a id="learnjava3-CHP-13-SECT-5"/>Scalable I/O with NIO</h1></div></div></div><p>We’ll now conclude the discussion of the NIO package we began in
<a class="xref" href="ch12.html" title="Chapter 12. Input/Output Facilities">Chapter 12</a> by talking about nonblocking and
selectable network communications. All our server examples in this chapter
thus far have used a thread-bound pattern (one thread per I/O operation).
In Java, this is very natural because of the ease with which we can create
threads. It’s also very efficient, within limits. Problems arise when you
try to build very large-scale servers using this style of client handling.
While on a large machine it’s certainly possible to have hundreds or even
thousands of threads (especially if they’re mostly idle, waiting for I/O),
this is a resource-hungry solution. Every thread you start in Java
consumes memory for its internal stack, and the performance of managing
this number of threads is highly system-dependent.</p><p>An alternate approach is to take a lesson from the old, dark days
before threading was available and use nonblocking I/O operations to
manage numerous communications from a single thread. Better yet, our
server uses a configurable pool of threads, taking advantage of machines
with many processors.</p><p>At the heart of this process is the concept of
<span class="emphasis"><em>selectable</em></span> I/O. It’s not good enough to simply have
nonblocking I/O operations if you have no way to efficiently poll for work
to be done. The NIO package provides for efficient polling using
selectable channels. A selectable channel allows for the registration of a
special kind of listener called a <span class="emphasis"><em>selector</em></span> that can
check the readiness of the channel for operations, such as reading and
writing or accepting or creating network connections.</p><p>The selector and the selection process are not typical Java
listeners of the kind we’ll see elsewhere in this book, but instead rather
slavishly follow the conventions of C language systems. This is mainly for
performance reasons; because this API is primarily intended for
high-volume servers, it is bound very tightly to the traditional,
underlying operating system facilities with less regard for ease of use.
This, combined with the other details of using the NIO package, mean that
this section is somewhat dense and the server we create here is one of the
longer and more complex examples in the book. Don’t be discouraged if you
are a bit put off by this section. You can use the general techniques
earlier in this chapter for most applications and reserve this knowledge
for creating services that handle the very highest volumes of simultaneous
client requests.</p><div class="sect2" title="Selectable Channels"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-13-SECT-5.1"/>Selectable Channels</h2></div></div></div><p><a id="I_indexterm13_id771479" class="indexterm"/> <a id="I_indexterm13_id771492" class="indexterm"/> <a id="I_indexterm13_id771504" class="indexterm"/> <a id="I_indexterm13_id771515" class="indexterm"/>A selectable channel implements the <a id="I_indexterm13_id771524" class="indexterm"/><code class="literal">SelectableChannel</code>
interface, which specifies that the channel can be set to a nonblocking
mode and that it supports the select API that makes efficient polling
possible. The primary implementations of selectable channels are those
for working with the network: <a id="I_indexterm13_id771538" class="indexterm"/><code class="literal">SocketChannel</code>,
<a id="I_indexterm13_id771548" class="indexterm"/><code class="literal">ServerSocketChannel</code>,
and <a id="I_indexterm13_id771559" class="indexterm"/><code class="literal">DatagramChannel</code>. The
only other selectable channel is the <a id="I_indexterm13_id771570" class="indexterm"/><code class="literal">Pipe</code> (which can be used
in an analogous way for intra-VM communication).</p><p>At the heart of the process is the <code class="literal">Selector</code> object, which knows about a
particular set of selectable channels and provides a <a id="I_indexterm13_id771590" class="indexterm"/><code class="literal">select()</code> method for
determining their readiness for I/O operations. Conceptually, the
process is simple; you register one or more channels with a selector and
then poll it, asking it to tell you which set of channels is ready to
go. In actuality, there are a few additional pieces involved.</p><p>First, the <code class="literal">Selector</code> does not
work directly with channels but instead operates on <a id="I_indexterm13_id771611" class="indexterm"/><code class="literal">SelectionKey</code> objects. A
<code class="literal">SelectionKey</code> object is created
implicitly when the channel is registered with the <code class="literal">Selector</code>. It encapsulates the selectable
channel as well as information about what types of operations (e.g.,
read, write) we are interested in waiting for. That information is held
in the <code class="literal">SelectionKey</code> in a set of flags
called the <span class="emphasis"><em>interest set</em></span>, which can be changed by
the application at any time. <code class="literal">SelectionKey</code>s are also used to return the
results of a <code class="literal">select</code> operation. Each
call to <code class="literal">select()</code> returns the number
of <code class="literal">SelectionKey</code>s that are ready for
some type of I/O. The keys are then retrieved with the <a id="I_indexterm13_id771668" class="indexterm"/><code class="literal">selectedKeys()</code> method.
Each key also has a set of flags called the <span class="emphasis"><em>ready
set</em></span> that indicates which operation of interest is actually
ready (possibly more than one). For example, a <code class="literal">SelectionKey</code> interest set might indicate that
we want to know when its channel is ready for reading or writing. After
a select operation, if that key is in the set returned by the selector,
we know that it is ready for one or more of those operations, and we can
check the key’s ready set to find out which one.</p><p>Before we go on, we should say that although we have been saying
that channels are registered with selectors, the API is (confusingly)
the other way around. Selectors are actually registered with the one or
more channels they manage, but it’s better to mentally spackle over this
and think of them the other way around.</p></div><div class="sect2" title="Using Select"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-13-SECT-5.2"/>Using Select</h2></div></div></div><p><a id="idx10771" class="indexterm"/> <a id="idx10778" class="indexterm"/> <a id="idx10788" class="indexterm"/> <a id="idx10801" class="indexterm"/>A <a id="I_indexterm13_id771770" class="indexterm"/><code class="literal">Selector</code> object is
created using the <a id="I_indexterm13_id771781" class="indexterm"/><code class="literal">Selector.open()</code> method
(<code class="literal">Selector</code> uses a factory
pattern):</p><a id="I_13_tt891"/><pre class="programlisting"> <code class="n">Selector</code> <code class="n">selector</code> <code class="o">=</code> <code class="n">Selector</code><code class="o">.</code><code class="na">open</code><code class="o">();</code></pre><p>To register one or more channels with the selector, set them to
nonblocking mode:</p><a id="I_13_tt892"/><pre class="programlisting"> <code class="n">SelectableChannel</code> <code class="n">channelA</code> <code class="o">=</code> <code class="c1">// ...</code>
<code class="n">channelA</code><code class="o">.</code><code class="na">configureBlocking</code><code class="o">(</code> <code class="kc">false</code> <code class="o">);</code></pre><p>Next, register the channels:</p><a id="I_13_tt893"/><pre class="programlisting"> <code class="kt">int</code> <code class="n">interestOps</code> <code class="o">=</code> <code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_READ</code> <code class="o">|</code> <code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_WRITE</code><code class="o">;</code>
<code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">=</code> <code class="n">channelA</code><code class="o">.</code><code class="na">register</code><code class="o">(</code> <code class="n">selector</code><code class="o">,</code> <code class="n">interestOps</code> <code class="o">);</code></pre><p>When we register the channel, we have an opportunity to set the
initial interest operations (or “interest ops”). These are defined by
constant fields in the <a id="I_indexterm13_id771834" class="indexterm"/><code class="literal">SelectionKey</code>
class:</p><div class="variablelist"><dl><dt><span class="term"><code class="literal">OP_READ</code></span></dt><dd><p><a id="I_indexterm13_id771855" class="indexterm"/>Ready to read</p></dd><dt><span class="term"><code class="literal">OP_WRITE</code></span></dt><dd><p><a id="I_indexterm13_id771869" class="indexterm"/>Ready to write</p></dd><dt><span class="term"><code class="literal">OP_CONNECT</code></span></dt><dd><p><a id="I_indexterm13_id771884" class="indexterm"/>Client-socket connection ready</p></dd><dt><span class="term"><code class="literal">OP_ACCEPT</code></span></dt><dd><p><a id="I_indexterm13_id771899" class="indexterm"/>Server-socket connection ready</p></dd></dl></div><p>These fields are bit flags; you can logically OR them together as
in this example to express interest in more than one type of
operation.</p><p>The result of the <a id="I_indexterm13_id771912" class="indexterm"/><code class="literal">register()</code> method is a
<code class="literal">SelectionKey</code> object. We can use the
key to change the interest ops at any time with the <code class="literal">SelectionKey interestOps()</code> method or to
unregister the channel from the <code class="literal">Selector</code> with the key’s <code class="literal">cancel()</code> method.</p><p>This same key is also returned as the result of selection
operations when its channel is ready. When the <code class="literal">SelectionKey</code> is returned, its ready set holds
flags for the operations that do not block if called. We can retrieve
the value of the flags with the <a id="I_indexterm13_id771955" class="indexterm"/><code class="literal">readySet()</code> method.
Convenience methods are available to test for each operation in the
ready set: <a id="I_indexterm13_id771967" class="indexterm"/><code class="literal">isReadable()</code>,
<a id="I_indexterm13_id771978" class="indexterm"/><code class="literal">isWritable()</code>,
<a id="I_indexterm13_id771988" class="indexterm"/><code class="literal">isConnectable()</code>, and
<a id="I_indexterm13_id771999" class="indexterm"/><code class="literal">isAcceptable()</code>.</p><p>Depending on how you structure your application, it may not be
necessary to save the <code class="literal">SelectionKey</code> at
registration time. In our example, we let the <a id="I_indexterm13_id772019" class="indexterm"/><code class="literal">Selector</code> keep track of
the keys for us, simply using them when they are ready. In fact, we go
even further and put the <code class="literal">SelectionKey</code>
to work by asking it to hold a reference for us! The <code class="literal">SelectionKey attach()</code> method is a convenience
method that can attach an arbitrary object to the key for use by our
application. We show how this can be useful in a bit.</p><p>After one or more channels are registered with the <code class="literal">Selector</code>, we can perform a <code class="literal">select</code> operation using one of its <a id="I_indexterm13_id772057" class="indexterm"/><code class="literal">select()</code>
methods:</p><a id="I_13_tt894"/><pre class="programlisting"> <code class="kt">int</code> <code class="n">readyCount</code> <code class="o">=</code> <code class="n">selector</code><code class="o">.</code><code class="na">select</code><code class="o">();</code></pre><p>Without arguments, the method blocks until at least one channel is
ready for some operation or until the <code class="literal">Selector</code>’s <a id="I_indexterm13_id772084" class="indexterm"/><code class="literal">wakeup()</code> method is
called. Alternatively, you can use the form of <code class="literal">select()</code> that takes a timeout (in
milliseconds) to wait for a ready channel before returning. There is
also <a id="I_indexterm13_id772101" class="indexterm"/><code class="literal">selectNow()</code>, which
always returns immediately. Both of these return methods count the
number of ready channels.</p><p>You can use <code class="literal">select()</code> and
<code class="literal">wakeup()</code> somewhat like <code class="literal">wait()</code> and <code class="literal">notify()</code>. The wakeup is necessary because once
a selection is started, it will not see any changes to its key’s
interest ops until the next invocation. If another thread changes the
interest ops, it must use <code class="literal">wakeup()</code> to
prompt the selecting thread to <code class="literal">select()</code> again. The <code class="literal">Selector</code> is also heavily synchronized; for
example, calls to register new channels block until the select is
finished. Often it’s much easier to simply use <code class="literal">select</code> with a short timeout and a loop, like
this:</p><a id="I_13_tt895"/><pre class="programlisting"> <code class="k">while</code> <code class="o">(</code> <code class="n">selector</code><code class="o">.</code><code class="na">select</code><code class="o">(</code> <code class="mi">50</code> <code class="o">)</code> <code class="o">==</code> <code class="mi">0</code> <code class="o">);</code></pre><p>However, if another thread is allowed to change the interest ops,
you still need to use <code class="literal">wakeup()</code> to
maximize throughput. Otherwise, in the worst case, you could end up
waiting the full <code class="literal">select</code> wait period
on every iteration, even when there is work to be done.</p><p>Next, we can get the set of ready channels from the <code class="literal">Selector</code> with the <a id="I_indexterm13_id772196" class="indexterm"/><code class="literal">selectedKeys()</code> method
and iterate through them, doing whatever our application
dictates:</p><a id="I_13_tt896"/><pre class="programlisting"> <code class="n">Set</code> <code class="n">readySet</code> <code class="o">=</code> <code class="n">selector</code><code class="o">.</code><code class="na">selectedKeys</code><code class="o">();</code>
<code class="k">for</code><code class="o">(</code> <code class="n">Iterator</code> <code class="n">it</code> <code class="o">=</code> <code class="n">readySet</code><code class="o">.</code><code class="na">iterator</code><code class="o">();</code> <code class="n">it</code><code class="o">.</code><code class="na">hasNext</code><code class="o">();</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">=</code> <code class="o">(</code><code class="n">SelectionKey</code><code class="o">)</code><code class="n">it</code><code class="o">.</code><code class="na">next</code><code class="o">();</code>
<code class="n">it</code><code class="o">.</code><code class="na">remove</code><code class="o">();</code> <code class="c1">// remove the key from the ready set</code>
<code class="c1">// use the key</code>
<code class="o">}</code></pre><p>The ready set is returned to us as a <code class="literal">java.util.Set</code>, which we walk through with an
<code class="literal">Iterator</code> (see <a class="xref" href="ch01.html" title="Chapter 1. A Modern Language">Chapter 1</a>). One important thing to note is that
we’ve used the <code class="literal">Iterator</code>’s <code class="literal">remove()</code> method to remove the key from the
ready set. The <code class="literal">select()</code> methods add
keys only to the ready set or add flags to keys already in the set; they
never remove them, so we must clear the keys when we handle them. You
can get the full set of keys a <code class="literal">Selector</code> is managing with the <code class="literal">keys()</code> method, but you should not attempt to
remove keys from that set; use the <code class="literal">cancel()</code> method on individual keys instead. Or
you can close the entire <code class="literal">Selector</code>
with its <code class="literal">close()</code> method,
unregistering all its keys.<a id="I_indexterm13_id772284" class="indexterm"/><a id="I_indexterm13_id772291" class="indexterm"/><a id="I_indexterm13_id772298" class="indexterm"/><a id="I_indexterm13_id772305" class="indexterm"/></p></div><div class="sect2" title="LargerHttpd"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-13-SECT-5.3"/>LargerHttpd</h2></div></div></div><p><a id="idx10770" class="indexterm"/> <a id="idx10772" class="indexterm"/> <a id="idx10777" class="indexterm"/> <a id="idx10787" class="indexterm"/> <a id="idx10800" class="indexterm"/>Let’s put this information to use. In this section, we’ll
create the big brother of <code class="literal">TinyHttpd</code> (our minimalist web server)
called <code class="literal">LargerHttpd</code>. The <code class="literal">LargerHttpd</code> server is a nonblocking web server
that uses <a id="I_indexterm13_id772412" class="indexterm"/><code class="literal">SocketChannel</code>s and a
pool of threads to service requests. In this example, a single thread
executes a main loop that accepts new connections and checks the
readiness of existing client connections for reading or writing.
Whenever a client needs attention, it places the job in a queue where a
thread from our thread pool waits to service it. As we said, this
example is a bit longer than we would like, but it is really the minimum
that is necessary to show a realistic usage of the APIs:</p><a id="I_13_tt897"/><pre class="programlisting"><code class="kn">import</code> <code class="nn">java.io.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.concurrent.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.net.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.nio.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.nio.channels.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.nio.charset.*</code><code class="o">;</code>
<code class="kn">import</code> <code class="nn">java.util.regex.*</code><code class="o">;</code>
<code class="kd">public</code> <code class="kd">class</code> <code class="nc">LargerHttpd</code>
<code class="o">{</code>
<code class="n">Selector</code> <code class="n">clientSelector</code><code class="o">;</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">run</code><code class="o">(</code> <code class="kt">int</code> <code class="n">port</code><code class="o">,</code> <code class="kt">int</code> <code class="n">threads</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">IOException</code>
<code class="o">{</code>
<code class="n">clientSelector</code> <code class="o">=</code> <code class="n">Selector</code><code class="o">.</code><code class="na">open</code><code class="o">();</code>
<code class="n">ServerSocketChannel</code> <code class="n">ssc</code> <code class="o">=</code> <code class="n">ServerSocketChannel</code><code class="o">.</code><code class="na">open</code><code class="o">();</code>
<code class="n">ssc</code><code class="o">.</code><code class="na">configureBlocking</code><code class="o">(</code><code class="kc">false</code><code class="o">);</code>
<code class="n">InetSocketAddress</code> <code class="n">sa</code> <code class="o">=</code> <code class="k">new</code> <code class="n">InetSocketAddress</code><code class="o">(</code> <code class="n">InetAddress</code>
<code class="o">.</code><code class="na">getLoopbackAddress</code><code class="o">(),</code> <code class="n">port</code> <code class="o">);</code>
<code class="n">ssc</code><code class="o">.</code><code class="na">socket</code><code class="o">().</code><code class="na">bind</code><code class="o">(</code> <code class="n">sa</code> <code class="o">);</code>
<code class="n">ssc</code><code class="o">.</code><code class="na">register</code><code class="o">(</code> <code class="n">clientSelector</code><code class="o">,</code> <code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_ACCEPT</code> <code class="o">);</code>
<code class="n">Executor</code> <code class="n">executor</code> <code class="o">=</code> <code class="n">Executors</code><code class="o">.</code><code class="na">newFixedThreadPool</code><code class="o">(</code> <code class="n">threads</code> <code class="o">);</code>
<code class="k">while</code> <code class="o">(</code> <code class="kc">true</code> <code class="o">)</code> <code class="o">{</code>
<code class="k">try</code> <code class="o">{</code>
<code class="k">while</code> <code class="o">(</code> <code class="n">clientSelector</code><code class="o">.</code><code class="na">select</code><code class="o">(</code><code class="mi">100</code><code class="o">)</code> <code class="o">==</code> <code class="mi">0</code> <code class="o">);</code>
<code class="n">Set</code><code class="o"><</code><code class="n">SelectionKey</code><code class="o">></code> <code class="n">readySet</code> <code class="o">=</code> <code class="n">clientSelector</code><code class="o">.</code><code class="na">selectedKeys</code><code class="o">();</code>
<code class="k">for</code><code class="o">(</code><code class="n">Iterator</code><code class="o"><</code><code class="n">SelectionKey</code><code class="o">></code> <code class="n">it</code><code class="o">=</code><code class="n">readySet</code><code class="o">.</code><code class="na">iterator</code><code class="o">();</code>
<code class="n">it</code><code class="o">.</code><code class="na">hasNext</code><code class="o">();)</code>
<code class="o">{</code>
<code class="kd">final</code> <code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">=</code> <code class="n">it</code><code class="o">.</code><code class="na">next</code><code class="o">();</code>
<code class="n">it</code><code class="o">.</code><code class="na">remove</code><code class="o">();</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">key</code><code class="o">.</code><code class="na">isAcceptable</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">acceptClient</code><code class="o">(</code> <code class="n">ssc</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">else</code> <code class="o">{</code>
<code class="n">key</code><code class="o">.</code><code class="na">interestOps</code><code class="o">(</code> <code class="mi">0</code> <code class="o">);</code>
<code class="n">executor</code><code class="o">.</code><code class="na">execute</code><code class="o">(</code> <code class="k">new</code> <code class="n">Runnable</code><code class="o">()</code> <code class="o">{</code>
<code class="kd">public</code> <code class="kt">void</code> <code class="nf">run</code><code class="o">()</code> <code class="o">{</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">handleClient</code><code class="o">(</code> <code class="n">key</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">IOException</code> <code class="n">e</code><code class="o">)</code> <code class="o">{</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="n">e</code><code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code> <code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">IOException</code> <code class="n">e</code> <code class="o">)</code> <code class="o">{</code> <code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code><code class="n">e</code><code class="o">);</code> <code class="o">}</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="kt">void</code> <code class="nf">acceptClient</code><code class="o">(</code> <code class="n">ServerSocketChannel</code> <code class="n">ssc</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">IOException</code>
<code class="o">{</code>
<code class="n">SocketChannel</code> <code class="n">clientSocket</code> <code class="o">=</code> <code class="n">ssc</code><code class="o">.</code><code class="na">accept</code><code class="o">();</code>
<code class="n">clientSocket</code><code class="o">.</code><code class="na">configureBlocking</code><code class="o">(</code><code class="kc">false</code><code class="o">);</code> <code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">=</code> <code class="n">clientSocket</code><code class="o">.</code><code class="na">register</code><code class="o">(</code> <code class="n">clientSelector</code><code class="o">,</code>
<code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_READ</code> <code class="o">);</code>
<code class="n">HttpdConnection</code> <code class="n">client</code> <code class="o">=</code> <code class="k">new</code> <code class="n">HttpdConnection</code><code class="o">(</code> <code class="n">clientSocket</code> <code class="o">);</code>
<code class="n">key</code><code class="o">.</code><code class="na">attach</code><code class="o">(</code> <code class="n">client</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kt">void</code> <code class="nf">handleClient</code><code class="o">(</code> <code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">IOException</code>
<code class="o">{</code>
<code class="n">HttpdConnection</code> <code class="n">client</code> <code class="o">=</code> <code class="o">(</code><code class="n">HttpdConnection</code><code class="o">)</code><code class="n">key</code><code class="o">.</code><code class="na">attachment</code><code class="o">();</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">key</code><code class="o">.</code><code class="na">isReadable</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">client</code><code class="o">.</code><code class="na">read</code><code class="o">(</code> <code class="n">key</code> <code class="o">);</code>
<code class="o">}</code> <code class="k">else</code> <code class="o">{</code>
<code class="n">client</code><code class="o">.</code><code class="na">write</code><code class="o">(</code> <code class="n">key</code> <code class="o">);</code>
<code class="o">}</code>
<code class="n">clientSelector</code><code class="o">.</code><code class="na">wakeup</code><code class="o">();</code>
<code class="o">}</code>
<code class="kd">public</code> <code class="kd">static</code> <code class="kt">void</code> <code class="nf">main</code><code class="o">(</code> <code class="n">String</code> <code class="n">argv</code><code class="o">[]</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">IOException</code> <code class="o">{</code>
<code class="c1">//new LargerHttpd().run( Integer.parseInt(argv[0]), 3/*threads*/ );</code>
<code class="k">new</code> <code class="nf">LargerHttpd</code><code class="o">().</code><code class="na">run</code><code class="o">(</code> <code class="mi">1235</code><code class="o">,</code> <code class="mi">3</code><code class="cm">/*threads*/</code> <code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code>
<code class="kd">class</code> <code class="nc">HttpdConnection</code>
<code class="o">{</code>
<code class="kd">static</code> <code class="n">Charset</code> <code class="n">charset</code> <code class="o">=</code> <code class="n">Charset</code><code class="o">.</code><code class="na">forName</code><code class="o">(</code><code class="s">"8859_1"</code><code class="o">);</code>
<code class="kd">static</code> <code class="n">Pattern</code> <code class="n">httpGetPattern</code> <code class="o">=</code> <code class="n">Pattern</code><code class="o">.</code><code class="na">compile</code><code class="o">(</code><code class="s">"(?s)GET /?(\\S*).*"</code><code class="o">);</code>
<code class="n">SocketChannel</code> <code class="n">clientSocket</code><code class="o">;</code>
<code class="n">ByteBuffer</code> <code class="n">buff</code> <code class="o">=</code> <code class="n">ByteBuffer</code><code class="o">.</code><code class="na">allocateDirect</code><code class="o">(</code> <code class="mi">64</code><code class="o">*</code><code class="mi">1024</code> <code class="o">);</code>
<code class="n">String</code> <code class="n">request</code><code class="o">;</code>
<code class="n">String</code> <code class="n">response</code><code class="o">;</code>
<code class="n">FileChannel</code> <code class="n">file</code><code class="o">;</code>
<code class="kt">int</code> <code class="n">filePosition</code><code class="o">;</code>
<code class="n">HttpdConnection</code> <code class="o">(</code> <code class="n">SocketChannel</code> <code class="n">clientSocket</code> <code class="o">)</code> <code class="o">{</code>
<code class="k">this</code><code class="o">.</code><code class="na">clientSocket</code> <code class="o">=</code> <code class="n">clientSocket</code><code class="o">;</code>
<code class="o">}</code>
<code class="kt">void</code> <code class="nf">read</code><code class="o">(</code> <code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">IOException</code> <code class="o">{</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">request</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">&&</code> <code class="o">(</code><code class="n">clientSocket</code><code class="o">.</code><code class="na">read</code><code class="o">(</code> <code class="n">buff</code> <code class="o">)</code> <code class="o">==</code> <code class="o">-</code><code class="mi">1</code>
<code class="o">||</code> <code class="n">buff</code><code class="o">.</code><code class="na">get</code><code class="o">(</code> <code class="n">buff</code><code class="o">.</code><code class="na">position</code><code class="o">()-</code><code class="mi">1</code> <code class="o">)</code> <code class="o">==</code> <code class="sc">'\n'</code> <code class="o">)</code> <code class="o">)</code>
<code class="n">processRequest</code><code class="o">(</code> <code class="n">key</code> <code class="o">);</code>
<code class="k">else</code>
<code class="n">key</code><code class="o">.</code><code class="na">interestOps</code><code class="o">(</code> <code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_READ</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kt">void</code> <code class="nf">processRequest</code><code class="o">(</code> <code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">buff</code><code class="o">.</code><code class="na">flip</code><code class="o">();</code>
<code class="n">request</code> <code class="o">=</code> <code class="n">charset</code><code class="o">.</code><code class="na">decode</code><code class="o">(</code> <code class="n">buff</code> <code class="o">).</code><code class="na">toString</code><code class="o">();</code>
<code class="n">Matcher</code> <code class="n">get</code> <code class="o">=</code> <code class="n">httpGetPattern</code><code class="o">.</code><code class="na">matcher</code><code class="o">(</code> <code class="n">request</code> <code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">get</code><code class="o">.</code><code class="na">matches</code><code class="o">()</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">request</code> <code class="o">=</code> <code class="n">get</code><code class="o">.</code><code class="na">group</code><code class="o">(</code><code class="mi">1</code><code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">request</code><code class="o">.</code><code class="na">endsWith</code><code class="o">(</code><code class="s">"/"</code><code class="o">)</code> <code class="o">||</code> <code class="n">request</code><code class="o">.</code><code class="na">equals</code><code class="o">(</code><code class="s">""</code><code class="o">)</code> <code class="o">)</code>
<code class="n">request</code> <code class="o">=</code> <code class="n">request</code> <code class="o">+</code> <code class="s">"index.html"</code><code class="o">;</code>
<code class="n">System</code><code class="o">.</code><code class="na">out</code><code class="o">.</code><code class="na">println</code><code class="o">(</code> <code class="s">"Request: "</code><code class="o">+</code><code class="n">request</code><code class="o">);</code>
<code class="k">try</code> <code class="o">{</code>
<code class="n">file</code> <code class="o">=</code> <code class="k">new</code> <code class="n">FileInputStream</code> <code class="o">(</code> <code class="n">request</code> <code class="o">).</code><code class="na">getChannel</code><code class="o">();</code>
<code class="o">}</code> <code class="k">catch</code> <code class="o">(</code> <code class="n">FileNotFoundException</code> <code class="n">e</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">response</code> <code class="o">=</code> <code class="s">"404 Object Not Found"</code><code class="o">;</code>
<code class="o">}</code>
<code class="o">}</code> <code class="k">else</code>
<code class="n">response</code> <code class="o">=</code> <code class="s">"400 Bad Request"</code> <code class="o">;</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">response</code> <code class="o">!=</code> <code class="kc">null</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">buff</code><code class="o">.</code><code class="na">clear</code><code class="o">();</code>
<code class="n">charset</code><code class="o">.</code><code class="na">newEncoder</code><code class="o">().</code><code class="na">encode</code><code class="o">(</code>
<code class="n">CharBuffer</code><code class="o">.</code><code class="na">wrap</code><code class="o">(</code> <code class="n">response</code> <code class="o">),</code> <code class="n">buff</code><code class="o">,</code> <code class="kc">true</code> <code class="o">);</code>
<code class="n">buff</code><code class="o">.</code><code class="na">flip</code><code class="o">();</code>
<code class="o">}</code>
<code class="n">key</code><code class="o">.</code><code class="na">interestOps</code><code class="o">(</code> <code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_WRITE</code> <code class="o">);</code>
<code class="o">}</code>
<code class="kt">void</code> <code class="nf">write</code><code class="o">(</code> <code class="n">SelectionKey</code> <code class="n">key</code> <code class="o">)</code> <code class="kd">throws</code> <code class="n">IOException</code> <code class="o">{</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">response</code> <code class="o">!=</code> <code class="kc">null</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">clientSocket</code><code class="o">.</code><code class="na">write</code><code class="o">(</code> <code class="n">buff</code> <code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">buff</code><code class="o">.</code><code class="na">remaining</code><code class="o">()</code> <code class="o">==</code> <code class="mi">0</code> <code class="o">)</code>
<code class="n">response</code> <code class="o">=</code> <code class="kc">null</code><code class="o">;</code>
<code class="o">}</code> <code class="k">else</code> <code class="k">if</code> <code class="o">(</code> <code class="n">file</code> <code class="o">!=</code> <code class="kc">null</code> <code class="o">)</code> <code class="o">{</code>
<code class="kt">int</code> <code class="n">remaining</code> <code class="o">=</code> <code class="o">(</code><code class="kt">int</code><code class="o">)</code><code class="n">file</code><code class="o">.</code><code class="na">size</code><code class="o">()-</code><code class="n">filePosition</code><code class="o">;</code>
<code class="kt">long</code> <code class="n">sent</code> <code class="o">=</code> <code class="n">file</code><code class="o">.</code><code class="na">transferTo</code><code class="o">(</code> <code class="n">filePosition</code><code class="o">,</code> <code class="n">remaining</code><code class="o">,</code>
<code class="n">clientSocket</code><code class="o">);</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">sent</code> <code class="o">>=</code> <code class="n">remaining</code> <code class="o">||</code> <code class="n">remaining</code> <code class="o"><=</code> <code class="mi">0</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">file</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="n">file</code> <code class="o">=</code> <code class="kc">null</code><code class="o">;</code>
<code class="o">}</code> <code class="k">else</code>
<code class="n">filePosition</code> <code class="o">+=</code> <code class="n">sent</code><code class="o">;</code>
<code class="o">}</code>
<code class="k">if</code> <code class="o">(</code> <code class="n">response</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">&&</code> <code class="n">file</code> <code class="o">==</code> <code class="kc">null</code> <code class="o">)</code> <code class="o">{</code>
<code class="n">clientSocket</code><code class="o">.</code><code class="na">close</code><code class="o">();</code>
<code class="n">key</code><code class="o">.</code><code class="na">cancel</code><code class="o">();</code>
<code class="o">}</code> <code class="k">else</code>
<code class="n">key</code><code class="o">.</code><code class="na">interestOps</code><code class="o">(</code> <code class="n">SelectionKey</code><code class="o">.</code><code class="na">OP_WRITE</code> <code class="o">);</code>
<code class="o">}</code>
<code class="o">}</code></pre><p>From a bird’s-eye view, the structure of <code class="literal">LargerHttpd</code> is the same as <code class="literal">TinyHttpd</code>. The main class, <code class="literal">LargerHttpd</code>, accepts connections, and a
connection class, <code class="literal">HttpdConnection</code>,
encapsulates a socket and handles the conversation with the client.
However, this time, instead of each connection object being a <code class="literal">Runnable</code> serviced in its own thread, its
functionality is broken into two primary methods called <code class="literal">read()</code> and <code class="literal">write()</code>. The job of our <code class="literal">LargerHttpd</code> is to accept new client socket
connections, wrap them in an instance of <code class="literal">HttpdConnection</code>, and then watch the client’s
status with a <code class="literal">Selector</code>. Whenever we
detect that a client is ready to send or receive data, we hand off a
<code class="literal">Runnable</code> task to our <code class="literal">Executor</code>. The task calls <code class="literal">read()</code> or <code class="literal">write()</code> on the corresponding client, based on
the operation that is is ready.</p><p>The <code class="literal">HttpConnection</code> object
encapsulates the state of the conversation with the client. Because its
interface is rather coarse, it must keep track of whether it is waiting
to read more input, generate a response, or write file output. The
<code class="literal">HttpdConnection</code> also manages the
interest set of its key so that it can effectively schedule itself to be
woken up when it’s ready for reading or writing. The association between
the <code class="literal">HttpdConnection</code> and the key is
made by using the key’s <code class="literal">attach()</code> and
<code class="literal">attachment()</code> methods.</p><p><code class="literal">LargerHttpd</code>’s <code class="literal">acceptClient()</code> method does several things.
First, it accepts the new socket connection. Next, it configures and
registers it with the selector with an initial interest set for reading.
Finally, it creates the <code class="literal">HttpdConnection</code> for the socket, and attaches
the <code class="literal">HttpdConnection</code> object to the key
for later retrieval.</p><p>The main loop of <code class="literal">LargerHttpd</code> is
fairly straightforward. First, we set up the <a id="I_indexterm13_id772701" class="indexterm"/><code class="literal">ServerSocketChannel</code>.
This is similar to setting up a plain <code class="literal">ServerSocket</code>, except that we must first create
an <a id="I_indexterm13_id772718" class="indexterm"/><code class="literal">InetSocketAddress</code>
object to hold the local loopback address and port combination of our
server socket and then explicitly bind our socket to that address with
the <code class="literal">ServerSocketChannel bind()</code>
method. We also configure the server socket to nonblocking mode and
register it with our main <code class="literal">Selector</code> so
that we can select for client connections in the same loop that we use
to select for client read and write readiness.</p><p>In the main <code class="literal">select</code> loop, we
check to see whether the key is ready for an <code class="literal">accept</code> operation and if so, we call <code class="literal">acceptClient()</code>; if not, we set the key’s
interest set to zero with the <code class="literal">interestOps()</code> method and dispatch the key to
our <code class="literal">handleClient()</code> method via a
<code class="literal">Runnable</code> task. It’s important that we
change the interest set to zero to clear it before the next loop;
otherwise, we’d be in a race to see whether the thread pool performed
its maximum work before we detected another ready condition. Setting the
interest ops to 0 and resetting it in the <code class="literal">HttpdConnection</code> object upon completion ensures
that only one thread is handling a given client at a time.</p><p>For each operation that is ready, we dispatch a task to our
<code class="literal">Executor</code>. The task calls <code class="literal">handleClient()</code>, passing it the selection key.
From the key, we retrieve the associated <code class="literal">HttpdConnection</code> object and call the
appropriate service method based on whether the key is ready for reading
or writing. After that, it’s up to the connection object to do its job.
Each call to the <code class="literal">read()</code> method simply
does what would be one iteration of a read loop in a thread-bound
application. Each read gets as much data as available and checks to see
whether we’ve reached the end of a line (a <code class="literal">\n</code> newline character). Upon reaching the end
of a line, we dispatch the call to the <code class="literal">processRequest()</code> method, which turns the byte
buffer into text and uses the same techniques as our <code class="literal">TinyHttpd</code> to parse the request into a file
pathname. On each incomplete call to <code class="literal">read()</code>, we set the interest ops of our key
back to <code class="literal">OP_READ</code>. Upon completing the
read and processing the request, we switch to using <code class="literal">OP_WRITE</code> because we are now ready to send a
response.</p><p>The <code class="literal">write()</code> method keeps track
of whether it’s sending a text response (error message) or a file by
using the <code class="literal">response</code> and <code class="literal">file</code> instance variables. When sending a file,
we use the <code class="literal">FileChannel</code>’s <a id="I_indexterm13_id772890" class="indexterm"/><code class="literal">transferTo()</code> method to
transfer bytes from the file directly to the network socket without
copying them into Java’s memory space. (This is indeed an efficient
little web server.) And that’s about it. When we’re done, we close the
client socket and cancel our key, which causes it to be removed from the
<code class="literal">Selector</code>’s key set during the next
<code class="literal">select</code> operation (discarding our
<code class="literal">HttpdConnection</code> object with
it).<a id="I_indexterm13_id772923" class="indexterm"/><a id="I_indexterm13_id772930" class="indexterm"/><a id="I_indexterm13_id772938" class="indexterm"/><a id="I_indexterm13_id772945" class="indexterm"/><a id="I_indexterm13_id772952" class="indexterm"/></p></div><div class="sect2" title="Nonblocking Client-Side Operations"><div class="titlepage"><div><div><h2 class="title"><a id="learnjava3-CHP-13-SECT-5.4"/>Nonblocking Client-Side Operations</h2></div></div></div><p><a id="I_indexterm13_id772966" class="indexterm"/> <a id="I_indexterm13_id772979" class="indexterm"/> <a id="I_indexterm13_id772990" class="indexterm"/> <a id="I_indexterm13_id773002" class="indexterm"/>Our example showed <a id="I_indexterm13_id773011" class="indexterm"/><a id="I_indexterm13_id773016" class="indexterm"/><code class="literal">SocketChannel</code> used for
nonblocking, selectable I/O in a typical server application. It’s less
common to need nonblocking I/O from a client, but there is certainly no
reason you can’t do it. Perhaps you’re writing a peer-to-peer (P2P)
application that manages many connections from both sides.</p><p>For the client side of communications, one additional tool is
provided: a nonblocking socket-connect operation. The process of
creating a TCP connection