Documentation cleanup
This commit is contained in:
parent
3d83768039
commit
927aaf8d12
@ -22,10 +22,10 @@
|
|||||||
<title>Before Getting Started</title>
|
<title>Before Getting Started</title>
|
||||||
<para>
|
<para>
|
||||||
The minimum requirements to run the examples which are introduced in
|
The minimum requirements to run the examples which are introduced in
|
||||||
this chapter are just two; the latest version of Netty and JDK 1.5 or
|
this chapter are only two; the latest version of Netty and JDK 1.5 or
|
||||||
above. The latest version of Netty is available in
|
above. The latest version of Netty is available in
|
||||||
<ulink url="&Downloads;">the project download page</ulink>. To get the
|
<ulink url="&Downloads;">the project download page</ulink>. To download
|
||||||
right version of JDK, please refer to your preferred JDK vendor's web
|
the right version of JDK, please refer to your preferred JDK vendor's web
|
||||||
site.
|
site.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
@ -53,9 +53,9 @@
|
|||||||
a protocol which discards any received data without any response.
|
a protocol which discards any received data without any response.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
What you are supposed to do to implement the DISCARD protocol is to log
|
To implement the DISCARD protocol, you only need to log the received data.
|
||||||
the received data, and that's all. Let's start straight from the handler
|
Let us start straight from the handler implementation, which handles I/O
|
||||||
implementation, which handles I/O events generated by Netty.
|
events generated by Netty.
|
||||||
</para>
|
</para>
|
||||||
<programlisting>package org.jboss.netty.example.discard;
|
<programlisting>package org.jboss.netty.example.discard;
|
||||||
|
|
||||||
@ -80,8 +80,8 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
|
|||||||
&ChannelPipelineCoverage; annotates a handler type to tell if the
|
&ChannelPipelineCoverage; annotates a handler type to tell if the
|
||||||
handler instance of the annotated type can be shared by more than
|
handler instance of the annotated type can be shared by more than
|
||||||
one &Channel; (and its associated &ChannelPipeline;).
|
one &Channel; (and its associated &ChannelPipeline;).
|
||||||
<classname>DiscardServerHandler</classname> doesn't manage any
|
<classname>DiscardServerHandler</classname> does not manage any
|
||||||
stateful information, and therefore it's annotated with the value
|
stateful information, and therefore it is annotated with the value
|
||||||
<literal>"all"</literal>.
|
<literal>"all"</literal>.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
@ -90,7 +90,7 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
|
|||||||
<classname>DiscardServerHandler</classname> extends
|
<classname>DiscardServerHandler</classname> extends
|
||||||
&SimpleChannelHandler;, which is an implementation of
|
&SimpleChannelHandler;, which is an implementation of
|
||||||
&ChannelHandler;. &SimpleChannelHandler; provides various event
|
&ChannelHandler;. &SimpleChannelHandler; provides various event
|
||||||
handler methods that you can override. For now, it's just enough
|
handler methods that you can override. For now, it is just enough
|
||||||
to extend &SimpleChannelHandler; rather than to implement
|
to extend &SimpleChannelHandler; rather than to implement
|
||||||
the handler interfaces by yourself.
|
the handler interfaces by yourself.
|
||||||
</para>
|
</para>
|
||||||
@ -100,8 +100,8 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
|
|||||||
We override the <methodname>messageReceived</methodname> event
|
We override the <methodname>messageReceived</methodname> event
|
||||||
handler method here. This method is called with a &MessageEvent;,
|
handler method here. This method is called with a &MessageEvent;,
|
||||||
which contains the received data, whenever new data is received
|
which contains the received data, whenever new data is received
|
||||||
from a client. In this example, we just ignore the received data
|
from a client. In this example, we ignore the received data by doing
|
||||||
by doing nothing to implement the DISCARD protocol.
|
nothing to implement the DISCARD protocol.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
<callout arearefs="example.discard.co4">
|
<callout arearefs="example.discard.co4">
|
||||||
@ -119,9 +119,9 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {<co id="exampl
|
|||||||
</callout>
|
</callout>
|
||||||
</calloutlist>
|
</calloutlist>
|
||||||
<para>
|
<para>
|
||||||
So far so good. We have implemented the half of the DISCARD server.
|
So far so good. We have implemented the first half of the DISCARD server.
|
||||||
What's left now is to write the <methodname>main</methodname> method
|
What's left now is to write the <methodname>main</methodname> method
|
||||||
which starts the server up with the <classname>DiscardServerHandler</classname>.
|
which starts the server with the <classname>DiscardServerHandler</classname>.
|
||||||
</para>
|
</para>
|
||||||
<programlisting>package org.jboss.netty.example.discard;
|
<programlisting>package org.jboss.netty.example.discard;
|
||||||
|
|
||||||
@ -157,7 +157,7 @@ public class DiscardServer {
|
|||||||
&ChannelFactory; implementations. We are implementing a server-side
|
&ChannelFactory; implementations. We are implementing a server-side
|
||||||
application in this example, and therefore
|
application in this example, and therefore
|
||||||
&NioServerSocketChannelFactory; was used. Another thing to note is
|
&NioServerSocketChannelFactory; was used. Another thing to note is
|
||||||
that it doesn't create I/O threads by itself. It is supposed to
|
that it does not create I/O threads by itself. It is supposed to
|
||||||
acquire threads from the thread pool you specified in the
|
acquire threads from the thread pool you specified in the
|
||||||
constructor, and it gives you more control over how threads should
|
constructor, and it gives you more control over how threads should
|
||||||
be managed in the environment where your application runs, such as
|
be managed in the environment where your application runs, such as
|
||||||
@ -167,8 +167,9 @@ public class DiscardServer {
|
|||||||
<callout arearefs="example.discard2.co2">
|
<callout arearefs="example.discard2.co2">
|
||||||
<para>
|
<para>
|
||||||
&ServerBootstrap; is a helper class that sets up a server. You can
|
&ServerBootstrap; is a helper class that sets up a server. You can
|
||||||
set up the server by yourself using a &Channel; directly, but it's a
|
set up the server using a &Channel; directly. However, please note
|
||||||
tedious process and you won't need to do that in most cases.
|
that this is a tedious process and you do not need to do that in most
|
||||||
|
cases.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
<callout arearefs="example.discard2.co3">
|
<callout arearefs="example.discard2.co3">
|
||||||
@ -192,18 +193,18 @@ public class DiscardServer {
|
|||||||
<literal>keepAlive</literal>. Please note that the
|
<literal>keepAlive</literal>. Please note that the
|
||||||
<literal>"child."</literal> prefix was added to all options. It
|
<literal>"child."</literal> prefix was added to all options. It
|
||||||
means the options will be applied to the accepted &Channel;s instead
|
means the options will be applied to the accepted &Channel;s instead
|
||||||
of the options of the &ServerSocketChannel;. You could do like the following:
|
of the options of the &ServerSocketChannel;. You could do the
|
||||||
|
following to set the options of the &ServerSocketChannel;:
|
||||||
<programlisting>bootstrap.setOption("reuseAddress", true);</programlisting>
|
<programlisting>bootstrap.setOption("reuseAddress", true);</programlisting>
|
||||||
to set the options of the &ServerSocketChannel;.
|
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
<callout arearefs="example.discard2.co5">
|
<callout arearefs="example.discard2.co5">
|
||||||
<para>
|
<para>
|
||||||
We are ready to go now. What's left is to bind to the port and to
|
We are ready to go now. What's left is to bind to the port and to
|
||||||
start the server. Here, we bind to the port <literal>8080</literal>
|
start the server. Here, we bind to the port <literal>8080</literal>
|
||||||
of all NICs (network interface cards) in the machine. You are fine
|
of all NICs (network interface cards) in the machine. You can now
|
||||||
to call the <methodname>bind</methodname> method as many times as
|
call the <methodname>bind</methodname> method as many times as
|
||||||
you want, with different bind addresses.
|
you want (with different bind addresses.)
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
</calloutlist>
|
</calloutlist>
|
||||||
@ -215,21 +216,21 @@ public class DiscardServer {
|
|||||||
<section>
|
<section>
|
||||||
<title>Looking into the Received Data</title>
|
<title>Looking into the Received Data</title>
|
||||||
<para>
|
<para>
|
||||||
Now that we wrote our first server, we need to test if it really works.
|
Now that we have written our first server, we need to test if it really
|
||||||
The easiest way to test it is to use the <command>telnet</command>
|
works. The easiest way to test it is to use the <command>telnet</command>
|
||||||
command. For example, you could enter "<command>telnet localhost
|
command. For example, you could enter "<command>telnet localhost
|
||||||
8080</command>" in the command line and type something.
|
8080</command>" in the command line and type something.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
However, can we say that the server is working fine? We can't really
|
However, can we say that the server is working fine? We cannot really
|
||||||
know that because it's a discard server. You will not going to get any
|
know that because it is a discard server. You will not get any response
|
||||||
response at all. To prove it's really working, let's modify the server
|
at all. To prove it is really working, let us modify the server to print
|
||||||
to print what it has received.
|
what it has received.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
We already know that &MessageEvent; is generated whenever data is
|
We already know that &MessageEvent; is generated whenever data is
|
||||||
received and the <methodname>messageReceived</methodname> handler method
|
received and the <methodname>messageReceived</methodname> handler method
|
||||||
will be invoked. Let's put some code into the
|
will be invoked. Let us put some code into the
|
||||||
<methodname>messageReceived</methodname> method of the
|
<methodname>messageReceived</methodname> method of the
|
||||||
<classname>DiscardServerHandler</classname>:
|
<classname>DiscardServerHandler</classname>:
|
||||||
</para>
|
</para>
|
||||||
@ -246,7 +247,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
|||||||
It is safe to assume the message type in socket transports is always
|
It is safe to assume the message type in socket transports is always
|
||||||
&ChannelBuffer;. &ChannelBuffer; is a fundamental data structure
|
&ChannelBuffer;. &ChannelBuffer; is a fundamental data structure
|
||||||
which stores a sequence of bytes in Netty. It's similar to NIO
|
which stores a sequence of bytes in Netty. It's similar to NIO
|
||||||
<classname>ByteBuffer</classname>, but it's easier to use and more
|
<classname>ByteBuffer</classname>, but it is easier to use and more
|
||||||
flexible. For example, Netty allows you to create a composite
|
flexible. For example, Netty allows you to create a composite
|
||||||
&ChannelBuffer; which combines multiple &ChannelBuffer;s reducing
|
&ChannelBuffer; which combines multiple &ChannelBuffer;s reducing
|
||||||
the number of unnecessary memory copy.
|
the number of unnecessary memory copy.
|
||||||
@ -273,7 +274,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
|||||||
<title>Writing an Echo Server</title>
|
<title>Writing an Echo Server</title>
|
||||||
<para>
|
<para>
|
||||||
So far, we have been consuming data without responding at all. A server,
|
So far, we have been consuming data without responding at all. A server,
|
||||||
however, is usually supposed to respond to a request. Let's learn how to
|
however, is usually supposed to respond to a request. Let us learn how to
|
||||||
write a response message to a client by implementing the
|
write a response message to a client by implementing the
|
||||||
<ulink url="http://tools.ietf.org/html/rfc862">ECHO</ulink> protocol,
|
<ulink url="http://tools.ietf.org/html/rfc862">ECHO</ulink> protocol,
|
||||||
where any received data is sent back.
|
where any received data is sent back.
|
||||||
@ -281,9 +282,8 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
|||||||
<para>
|
<para>
|
||||||
The only difference from the discard server we have implemented in the
|
The only difference from the discard server we have implemented in the
|
||||||
previous sections is that it sends the received data back instead of
|
previous sections is that it sends the received data back instead of
|
||||||
printing the received data out to the console. Therefore, it's just
|
printing the received data out to the console. Therefore, it is enough
|
||||||
enough again to modify the <methodname>messageReceived</methodname>
|
again to modify the <methodname>messageReceived</methodname> method:
|
||||||
method:
|
|
||||||
</para>
|
</para>
|
||||||
<programlisting>@Override
|
<programlisting>@Override
|
||||||
public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
||||||
@ -324,10 +324,10 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
|||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
Because we are going to ignore any received data but to send a message
|
Because we are going to ignore any received data but to send a message
|
||||||
as soon as a connection is established, we can't use the
|
as soon as a connection is established, we cannot use the
|
||||||
<methodname>messageReceived</methodname> method this time. Instead,
|
<methodname>messageReceived</methodname> method this time. Instead,
|
||||||
we should override the <methodname>channelConnected</methodname> method.
|
we should override the <methodname>channelConnected</methodname> method.
|
||||||
Here's the implementation:
|
The following is the implementation:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>package org.jboss.netty.example.time;
|
<programlisting>package org.jboss.netty.example.time;
|
||||||
|
|
||||||
@ -361,7 +361,7 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
|
|||||||
<callout arearefs="example.time.co1">
|
<callout arearefs="example.time.co1">
|
||||||
<para>
|
<para>
|
||||||
As explained, <methodname>channelConnected</methodname> method will
|
As explained, <methodname>channelConnected</methodname> method will
|
||||||
be invoked when a connection is established. Let's write the 32-bit
|
be invoked when a connection is established. Let us write the 32-bit
|
||||||
integer that represents the current time in seconds here.
|
integer that represents the current time in seconds here.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
@ -373,11 +373,11 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
|
|||||||
<literal>4</literal> bytes. The &ChannelBuffers; helper class is
|
<literal>4</literal> bytes. The &ChannelBuffers; helper class is
|
||||||
used to allocate a new buffer. Besides the
|
used to allocate a new buffer. Besides the
|
||||||
<methodname>buffer</methodname> method, &ChannelBuffers; provides a
|
<methodname>buffer</methodname> method, &ChannelBuffers; provides a
|
||||||
lot of useful methods related with &ChannelBuffer;. Please refer to
|
lot of useful methods related to the &ChannelBuffer;. For more
|
||||||
the API reference.
|
information, please refer to the API reference.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
On the other hand, it's a good idea to use static imports for
|
On the other hand, it is a good idea to use static imports for
|
||||||
&ChannelBuffers;:
|
&ChannelBuffers;:
|
||||||
<programlisting>import static org.jboss.netty.buffer.&ChannelBuffers;.*;
|
<programlisting>import static org.jboss.netty.buffer.&ChannelBuffers;.*;
|
||||||
...
|
...
|
||||||
@ -392,19 +392,19 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
|
|||||||
<para>
|
<para>
|
||||||
But wait, where's the <methodname>flip</methodname>? Didn't we used
|
But wait, where's the <methodname>flip</methodname>? Didn't we used
|
||||||
to call <methodname>ByteBuffer.flip()</methodname> before sending a
|
to call <methodname>ByteBuffer.flip()</methodname> before sending a
|
||||||
message in NIO? &ChannelBuffer; doesn't have such a method because
|
message in NIO? &ChannelBuffer; does not have such a method because
|
||||||
it has two pointers; one for read operations and the other for write
|
it has two pointers; one for read operations and the other for write
|
||||||
operations. The writer index increases when you write something to
|
operations. The writer index increases when you write something to
|
||||||
a &ChannelBuffer; while the reader index doesn't change. The reader
|
a &ChannelBuffer; while the reader index does not change. The reader
|
||||||
index and the writer index represents where the message starts and
|
index and the writer index represents where the message starts and
|
||||||
ends respectively.
|
ends respectively.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
In contrast, NIO buffer doesn't provide a clean way to figure out
|
In contrast, NIO buffer does not provide a clean way to figure out
|
||||||
where the message content starts and ends without calling the
|
where the message content starts and ends without calling the
|
||||||
<methodname>flip</methodname> method. You will be in trouble when
|
<methodname>flip</methodname> method. You will be in trouble when
|
||||||
you forget to flip the buffer because nothing or incorrect data will
|
you forget to flip the buffer because nothing or incorrect data will
|
||||||
be sent. Such an error doesn't happen in Netty because we have
|
be sent. Such an error does not happen in Netty because we have
|
||||||
different pointer for different operation types. You will find it
|
different pointer for different operation types. You will find it
|
||||||
makes your life much easier as you get used to it -- a life without
|
makes your life much easier as you get used to it -- a life without
|
||||||
flipping out!
|
flipping out!
|
||||||
@ -412,7 +412,7 @@ public class TimeServerHandler extends &SimpleChannelHandler; {
|
|||||||
<para>
|
<para>
|
||||||
Another point to note is that the <methodname>write</methodname>
|
Another point to note is that the <methodname>write</methodname>
|
||||||
method returns a &ChannelFuture;. A &ChannelFuture; represents an
|
method returns a &ChannelFuture;. A &ChannelFuture; represents an
|
||||||
I/O operation which was not occurred yet. It means, any requested
|
I/O operation which has not yet occurred. It means, any requested
|
||||||
operation might not have been performed yet because all operations
|
operation might not have been performed yet because all operations
|
||||||
are asynchronous in Netty. For example, the following code might
|
are asynchronous in Netty. For example, the following code might
|
||||||
close the connection even before a message is sent:
|
close the connection even before a message is sent:
|
||||||
@ -424,7 +424,7 @@ ch.close();</programlisting>
|
|||||||
Therefore, you need to call the <methodname>close</methodname>
|
Therefore, you need to call the <methodname>close</methodname>
|
||||||
method after the &ChannelFuture;, which was returned by the
|
method after the &ChannelFuture;, which was returned by the
|
||||||
<methodname>write</methodname> method, notifies you when the write
|
<methodname>write</methodname> method, notifies you when the write
|
||||||
operation has been done. Also, <methodname>close</methodname>
|
operation has been done. Please note that, <methodname>close</methodname>
|
||||||
might not close the connection immediately, and it returns a
|
might not close the connection immediately, and it returns a
|
||||||
&ChannelFuture;.
|
&ChannelFuture;.
|
||||||
</para>
|
</para>
|
||||||
@ -432,7 +432,7 @@ ch.close();</programlisting>
|
|||||||
<callout arearefs="example.time.co4">
|
<callout arearefs="example.time.co4">
|
||||||
<para>
|
<para>
|
||||||
How do we get notified when the write request is finished then?
|
How do we get notified when the write request is finished then?
|
||||||
It's as simple as adding a &ChannelFutureListener; to the returned
|
This is as simple as adding a &ChannelFutureListener; to the returned
|
||||||
&ChannelFuture;. Here, we created a new anonymous &ChannelFutureListener;
|
&ChannelFuture;. Here, we created a new anonymous &ChannelFutureListener;
|
||||||
which closes the &Channel; when the operation is done.
|
which closes the &Channel; when the operation is done.
|
||||||
</para>
|
</para>
|
||||||
@ -449,9 +449,9 @@ ch.close();</programlisting>
|
|||||||
<title>Writing a Time Client</title>
|
<title>Writing a Time Client</title>
|
||||||
<para>
|
<para>
|
||||||
Unlike DISCARD and ECHO servers, we need a client for the TIME protocol
|
Unlike DISCARD and ECHO servers, we need a client for the TIME protocol
|
||||||
because a human can't translate a 32-bit binary data into a date on a
|
because a human cannot translate a 32-bit binary data into a date on a
|
||||||
calendar. Let's make sure the server works correctly and learn how to
|
calendar. In this section, we discuss how to make sure the server works
|
||||||
write a client with Netty.
|
correctly and learn how to write a client with Netty.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The biggest and only difference between a server and a client in Netty
|
The biggest and only difference between a server and a client in Netty
|
||||||
@ -500,7 +500,7 @@ public class TimeClient {
|
|||||||
<callout arearefs="example.time2.co3">
|
<callout arearefs="example.time2.co3">
|
||||||
<para>
|
<para>
|
||||||
Please note that there's no <literal>"child."</literal> prefix.
|
Please note that there's no <literal>"child."</literal> prefix.
|
||||||
A client-side &SocketChannel; doesn't have a parent.
|
A client-side &SocketChannel; does not have a parent.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
<callout arearefs="example.time2.co4">
|
<callout arearefs="example.time2.co4">
|
||||||
@ -511,10 +511,10 @@ public class TimeClient {
|
|||||||
</callout>
|
</callout>
|
||||||
</calloutlist>
|
</calloutlist>
|
||||||
<para>
|
<para>
|
||||||
As you saw, it's not really different from the server side startup. What
|
As you can see, it is not really different from the server side startup.
|
||||||
about the &ChannelHandler; implementation? It should receive a 32-bit
|
What about the &ChannelHandler; implementation? It should receive a
|
||||||
integer from the server, translate it into a human readable format, print
|
32-bit integer from the server, translate it into a human readable format,
|
||||||
the translated time, and close the connection:
|
print the translated time, and close the connection:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>package org.jboss.netty.example.time;
|
<programlisting>package org.jboss.netty.example.time;
|
||||||
|
|
||||||
@ -536,10 +536,10 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
|
|||||||
}
|
}
|
||||||
}</programlisting>
|
}</programlisting>
|
||||||
<para>
|
<para>
|
||||||
It looks very simple and doesn't look any different from the server side
|
It looks very simple and does not look any different from the server side
|
||||||
example. However, this handler sometimes will refuse to work raising an
|
example. However, this handler sometimes will refuse to work raising an
|
||||||
<exceptionname>IndexOutOfBoundsException</exceptionname>. Let's figure
|
<exceptionname>IndexOutOfBoundsException</exceptionname>. We discuss why
|
||||||
out why in the next section.
|
this happens in the next section.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@ -554,20 +554,20 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
|
|||||||
<para>
|
<para>
|
||||||
In a stream-based transport such as TCP/IP, packets can be fragmented and
|
In a stream-based transport such as TCP/IP, packets can be fragmented and
|
||||||
reassembled during transmission even in a LAN environment. For example,
|
reassembled during transmission even in a LAN environment. For example,
|
||||||
let's assume you have received three packets:
|
let us assume you have received three packets:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>+-----+-----+-----+
|
<programlisting>+-----+-----+-----+
|
||||||
| ABC | DEF | GHI |
|
| ABC | DEF | GHI |
|
||||||
+-----+-----+-----+</programlisting>
|
+-----+-----+-----+</programlisting>
|
||||||
<para>
|
<para>
|
||||||
because of the packet fragmentation, a server can receive them like the
|
because of the packet fragmentation, a server can receive them as
|
||||||
following:
|
follows:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>+----+-------+---+---+
|
<programlisting>+----+-------+---+---+
|
||||||
| AB | CDEFG | H | I |
|
| AB | CDEFG | H | I |
|
||||||
+----+-------+---+---+</programlisting>
|
+----+-------+---+---+</programlisting>
|
||||||
<para>
|
<para>
|
||||||
Therefore, a receiving part, regardless it's server-side or client-side,
|
Therefore, a receiving part, regardless it is server-side or client-side,
|
||||||
should defrag the received packets into one or more meaningful
|
should defrag the received packets into one or more meaningful
|
||||||
<firstterm>frames</firstterm> that could be easily understood by the
|
<firstterm>frames</firstterm> that could be easily understood by the
|
||||||
application logic. In case of the example above, the received packets
|
application logic. In case of the example above, the received packets
|
||||||
@ -582,17 +582,17 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
|
|||||||
The First Solution
|
The First Solution
|
||||||
</title>
|
</title>
|
||||||
<para>
|
<para>
|
||||||
Now let's get back to the TIME client example. We have the same problem
|
Now let us get back to the TIME client example. We have the same problem
|
||||||
here. A 32-bit integer is a very small amount of data, and it is not
|
here. A 32-bit integer is a very small amount of data, and it is not
|
||||||
likely to be fragmented often. However, the problem is that it
|
likely to be fragmented often. However, the problem is that it
|
||||||
<emphasis>can</emphasis> be fragmented, and the possibility of
|
<emphasis>can</emphasis> be fragmented, and the possibility of
|
||||||
fragmentation will increase as the traffic goes higher.
|
fragmentation will increase as the traffic increases.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
The simplistic solution is to create an internal cumulative buffer and
|
The simplistic solution is to create an internal cumulative buffer and
|
||||||
wait until all 4 bytes are received into the internal buffer. Here's
|
wait until all 4 bytes are received into the internal buffer. The
|
||||||
the modified <classname>TimeClientHandler</classname> implementation
|
following is the modified <classname>TimeClientHandler</classname>
|
||||||
that fixes the problem.
|
implementation that fixes the problem:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>package org.jboss.netty.example.time;
|
<programlisting>package org.jboss.netty.example.time;
|
||||||
|
|
||||||
@ -630,7 +630,7 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
|
|||||||
buffer and therefore cannot serve multiple &Channel;s. If an
|
buffer and therefore cannot serve multiple &Channel;s. If an
|
||||||
instance of <classname>TimeClientHandler</classname> is shared by
|
instance of <classname>TimeClientHandler</classname> is shared by
|
||||||
multiple &Channel;s (and consequently multiple &ChannelPipeline;s),
|
multiple &Channel;s (and consequently multiple &ChannelPipeline;s),
|
||||||
the content of the <varname>buf</varname> will be messed up.
|
the content of the <varname>buf</varname> will be corrupted.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
<callout arearefs="example.time3.co2">
|
<callout arearefs="example.time3.co2">
|
||||||
@ -657,11 +657,11 @@ public class TimeClientHandler extends &SimpleChannelHandler; {
|
|||||||
</callout>
|
</callout>
|
||||||
</calloutlist>
|
</calloutlist>
|
||||||
<para>
|
<para>
|
||||||
There's another place that needs a fix. Do you remember that we have
|
There's another place that needs a fix. Do you remember that we
|
||||||
added a <classname>TimeClientHandler</classname> instance to the
|
added a <classname>TimeClientHandler</classname> instance to the
|
||||||
<emphasis>default</emphasis> &ChannelPipeline; of the &ClientBootstrap;?
|
<emphasis>default</emphasis> &ChannelPipeline; of the &ClientBootstrap;?
|
||||||
It means one <classname>TimeClientHandler</classname> is going to handle
|
It means one <classname>TimeClientHandler</classname> is going to handle
|
||||||
multiple &Channel;s and consequently the data will be messed up. To
|
multiple &Channel;s and consequently the data will be corrupted. To
|
||||||
create a new <classname>TimeClientHandler</classname> instance per
|
create a new <classname>TimeClientHandler</classname> instance per
|
||||||
&Channel;, we should implement a &ChannelPipelineFactory;:
|
&Channel;, we should implement a &ChannelPipelineFactory;:
|
||||||
</para>
|
</para>
|
||||||
@ -676,7 +676,7 @@ public class TimeClientPipelineFactory implements &ChannelPipelineFactory; {
|
|||||||
}
|
}
|
||||||
}</programlisting>
|
}</programlisting>
|
||||||
<para>
|
<para>
|
||||||
Now let's replace the following lines of <classname>TimeClient</classname>:
|
Now let us replace the following lines of <classname>TimeClient</classname>:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>TimeClientHandler handler = new TimeClientHandler();
|
<programlisting>TimeClientHandler handler = new TimeClientHandler();
|
||||||
bootstrap.getPipeline().addLast("handler", handler);</programlisting>
|
bootstrap.getPipeline().addLast("handler", handler);</programlisting>
|
||||||
@ -702,17 +702,17 @@ bootstrap.getPipeline().addLast("handler", handler);</programlisting>
|
|||||||
</title>
|
</title>
|
||||||
<para>
|
<para>
|
||||||
Although the first solution has resolved the problem with the TIME
|
Although the first solution has resolved the problem with the TIME
|
||||||
client, the modified handler doesn't look that clean. Imagine a more
|
client, the modified handler does not look that clean. Imagine a more
|
||||||
complicated protocol which is composed of multiple fields such as a
|
complicated protocol which is composed of multiple fields such as a
|
||||||
variable length field. Your &ChannelHandler; implementation will
|
variable length field. Your &ChannelHandler; implementation will
|
||||||
become unmaintainable very quickly.
|
become unmaintainable very quickly.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
As you already might have noticed, you can add more than one
|
As you may have noticed, you can add more than one &ChannelHandler; to
|
||||||
&ChannelHandler; to a &ChannelPipeline;, and therefore, you can
|
a &ChannelPipeline;, and therefore, you can split one monolithic
|
||||||
split one monolithic &ChannelHandler; into multiple modular ones to
|
&ChannelHandler; into multiple modular ones to reduce the complexity of
|
||||||
reduce the complexity of your application. For example, you could
|
your application. For example, you could split
|
||||||
split <classname>TimeClientHandler</classname> into two handlers:
|
<classname>TimeClientHandler</classname> into two handlers:
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
@ -764,8 +764,8 @@ public class TimeDecoder extends &FrameDecoder; {
|
|||||||
<callout arearefs="example.time4.co3">
|
<callout arearefs="example.time4.co3">
|
||||||
<para>
|
<para>
|
||||||
If <literal>null</literal> is returned, it means there's not
|
If <literal>null</literal> is returned, it means there's not
|
||||||
enough data yet. &FrameDecoder; will call again when more data is
|
enough data yet. &FrameDecoder; will call again when there is a
|
||||||
in.
|
sufficient amount of data.
|
||||||
</para>
|
</para>
|
||||||
</callout>
|
</callout>
|
||||||
<callout arearefs="example.time4.co3">
|
<callout arearefs="example.time4.co3">
|
||||||
@ -781,7 +781,7 @@ public class TimeDecoder extends &FrameDecoder; {
|
|||||||
</callout>
|
</callout>
|
||||||
</calloutlist>
|
</calloutlist>
|
||||||
<para>
|
<para>
|
||||||
If you are a adventurous person, you might want to try the
|
If you are an adventurous person, you might want to try the
|
||||||
&ReplayingDecoder; which simplifies the decoder even more. You will
|
&ReplayingDecoder; which simplifies the decoder even more. You will
|
||||||
need to consult the API reference for more information though.
|
need to consult the API reference for more information though.
|
||||||
</para>
|
</para>
|
||||||
@ -798,10 +798,9 @@ public class TimeDecoder extends &ReplayingDecoder;<&VoidEnum;> {
|
|||||||
}</programlisting>
|
}</programlisting>
|
||||||
<para>
|
<para>
|
||||||
Additionally, Netty provides out-of-the-box decoders which enables
|
Additionally, Netty provides out-of-the-box decoders which enables
|
||||||
you to implement most protocol very easily and helps you avoid from
|
you to implement most protocols very easily and helps you avoid from
|
||||||
ending up with a monolithic unmaintainable handler implementation.
|
ending up with a monolithic unmaintainable handler implementation.
|
||||||
You might want to take a look into the following packages for more
|
Please refer to the following packages for more detailed examples:
|
||||||
detailed examples:
|
|
||||||
<itemizedlist>
|
<itemizedlist>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
@ -825,9 +824,9 @@ public class TimeDecoder extends &ReplayingDecoder;<&VoidEnum;> {
|
|||||||
Speaking in POJO instead of ChannelBuffer
|
Speaking in POJO instead of ChannelBuffer
|
||||||
</title>
|
</title>
|
||||||
<para>
|
<para>
|
||||||
All the examples we visited so far used a &ChannelBuffer; as a primary
|
All the examples we have reviewed so far used a &ChannelBuffer; as a
|
||||||
data structure of a protocol message. In this section, we will improve
|
primary data structure of a protocol message. In this section, we will
|
||||||
the TIME protocol client and server example to use a
|
improve the TIME protocol client and server example to use a
|
||||||
<ulink url="http://en.wikipedia.org/wiki/POJO">POJO</ulink> instead of a
|
<ulink url="http://en.wikipedia.org/wiki/POJO">POJO</ulink> instead of a
|
||||||
&ChannelBuffer;.
|
&ChannelBuffer;.
|
||||||
</para>
|
</para>
|
||||||
@ -836,12 +835,12 @@ public class TimeDecoder extends &ReplayingDecoder;<&VoidEnum;> {
|
|||||||
your handler becomes more maintainable and reusable by separating the
|
your handler becomes more maintainable and reusable by separating the
|
||||||
code which extracts information from &ChannelBuffer; out from the
|
code which extracts information from &ChannelBuffer; out from the
|
||||||
handler. In the TIME client and server examples, we read only one
|
handler. In the TIME client and server examples, we read only one
|
||||||
32-bit integer and it's not a big deal to use &ChannelBuffer; directly.
|
32-bit integer and it is not a major issue to use &ChannelBuffer; directly.
|
||||||
However, you will find it is necessary to make the separation as you
|
However, you will find it is necessary to make the separation as you
|
||||||
implement a real world protocol.
|
implement a real world protocol.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
First, let's define a new type called <classname>UnixTime</classname>.
|
First, let us define a new type called <classname>UnixTime</classname>.
|
||||||
</para>
|
</para>
|
||||||
<programlisting>package org.jboss.netty.example.time;
|
<programlisting>package org.jboss.netty.example.time;
|
||||||
|
|
||||||
@ -887,7 +886,7 @@ protected Object decode(
|
|||||||
</calloutlist>
|
</calloutlist>
|
||||||
<para>
|
<para>
|
||||||
With the updated decoder, the <classname>TimeClientHandler</classname>
|
With the updated decoder, the <classname>TimeClientHandler</classname>
|
||||||
doesn't use &ChannelBuffer; anymore:
|
does not use &ChannelBuffer; anymore:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>@Override
|
<programlisting>@Override
|
||||||
public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
||||||
@ -897,7 +896,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) {
|
|||||||
}</programlisting>
|
}</programlisting>
|
||||||
<para>
|
<para>
|
||||||
Much simpler and elegant, right? The same technique can be applied on
|
Much simpler and elegant, right? The same technique can be applied on
|
||||||
the server side. Let's update the
|
the server side. Let us update the
|
||||||
<classname>TimeServerHandler</classname> first this time:
|
<classname>TimeServerHandler</classname> first this time:
|
||||||
</para>
|
</para>
|
||||||
<programlisting>@Override
|
<programlisting>@Override
|
||||||
@ -963,7 +962,7 @@ public class TimeEncoder implements &SimpleChannelHandler; {
|
|||||||
in the &ChannelPipeline;.
|
in the &ChannelPipeline;.
|
||||||
</para>
|
</para>
|
||||||
<para>
|
<para>
|
||||||
On the other hand, it's a good idea to use static imports for
|
On the other hand, it is a good idea to use static imports for
|
||||||
&Channels;:
|
&Channels;:
|
||||||
<programlisting>import static org.jboss.netty.channel.&Channels;.*;
|
<programlisting>import static org.jboss.netty.channel.&Channels;.*;
|
||||||
...
|
...
|
||||||
@ -975,7 +974,7 @@ fireChannelDisconnected(ctx, e.getChannel());</programlisting>
|
|||||||
</calloutlist>
|
</calloutlist>
|
||||||
<para>
|
<para>
|
||||||
The last task left is to insert a <classname>TimeEncoder</classname>
|
The last task left is to insert a <classname>TimeEncoder</classname>
|
||||||
into the &ChannelPipeline; on the server side, and it's left as a
|
into the &ChannelPipeline; on the server side, and it is left as a
|
||||||
trivial exercise.
|
trivial exercise.
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
@ -985,12 +984,11 @@ fireChannelDisconnected(ctx, e.getChannel());</programlisting>
|
|||||||
Summary
|
Summary
|
||||||
</title>
|
</title>
|
||||||
<para>
|
<para>
|
||||||
In this chapter, we had a quick tour of Netty so that you can start to
|
In this chapter, we had a quick tour of Netty with a demonstration on how
|
||||||
write a network application on top of Netty right away. I believe you
|
to write a network application on top of Netty. More questions you may
|
||||||
still have a lot of questions about various topics, and they will be
|
have will be covered in the upcoming chapters and the revised version of
|
||||||
covered in the upcoming chapters and the revised version of this chapter.
|
this chapter. Please also note that <ulink url="&Community;">the Netty
|
||||||
Also, please don't forget that <ulink url="&Community;">the Netty project
|
project community</ulink> is always here to help you.
|
||||||
community</ulink> is always here to help you.
|
|
||||||
</para>
|
</para>
|
||||||
</section>
|
</section>
|
||||||
</chapter>
|
</chapter>
|
||||||
|
Loading…
Reference in New Issue
Block a user