UNPKG

epubjs

Version:

Render ePub documents in the browser, across many devices

370 lines (357 loc) 51.3 kB
<?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">&lt;</code><code class="n">SelectionKey</code><code class="o">&gt;</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">&lt;</code><code class="n">SelectionKey</code><code class="o">&gt;</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">&amp;&amp;</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">&gt;=</code> <code class="n">remaining</code> <code class="o">||</code> <code class="n">remaining</code> <code class="o">&lt;=</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">&amp;&amp;</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