diff --git a/src/docbook/module/start.xml b/src/docbook/module/start.xml index d8e7bf0270..7bab0e257c 100644 --- a/src/docbook/module/start.xml +++ b/src/docbook/module/start.xml @@ -22,10 +22,10 @@ Before Getting Started 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 - the project download page. To get the - right version of JDK, please refer to your preferred JDK vendor's web + the project download page. To download + the right version of JDK, please refer to your preferred JDK vendor's web site. @@ -53,9 +53,9 @@ a protocol which discards any received data without any response. - What you are supposed to do to implement the DISCARD protocol is to log - the received data, and that's all. Let's start straight from the handler - implementation, which handles I/O events generated by Netty. + To implement the DISCARD protocol, you only need to log the received data. + Let us start straight from the handler implementation, which handles I/O + events generated by Netty. package org.jboss.netty.example.discard; @@ -80,8 +80,8 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {. @@ -90,7 +90,7 @@ public class DiscardServerHandler extends &SimpleChannelHandler; {messageReceived event handler method here. This method is called with a &MessageEvent;, which contains the received data, whenever new data is received - from a client. In this example, we just ignore the received data - by doing nothing to implement the DISCARD protocol. + from a client. In this example, we ignore the received data by doing + nothing to implement the DISCARD protocol. @@ -119,9 +119,9 @@ public class DiscardServerHandler extends &SimpleChannelHandler; { &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 - tedious process and you won't need to do that in most cases. + set up the server using a &Channel; directly. However, please note + that this is a tedious process and you do not need to do that in most + cases. @@ -192,18 +193,18 @@ public class DiscardServer { keepAlive. Please note that the "child." prefix was added to all options. It 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;: bootstrap.setOption("reuseAddress", true); - to set the options of the &ServerSocketChannel;. 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 8080 - of all NICs (network interface cards) in the machine. You are fine - to call the bind method as many times as - you want, with different bind addresses. + of all NICs (network interface cards) in the machine. You can now + call the bind method as many times as + you want (with different bind addresses.) @@ -215,21 +216,21 @@ public class DiscardServer {
Looking into the Received Data - Now that we wrote our first server, we need to test if it really works. - The easiest way to test it is to use the telnet + Now that we have written our first server, we need to test if it really + works. The easiest way to test it is to use the telnet command. For example, you could enter "telnet localhost 8080" in the command line and type something. - However, can we say that the server is working fine? We can't really - know that because it's a discard server. You will not going to get any - response at all. To prove it's really working, let's modify the server - to print what it has received. + However, can we say that the server is working fine? We cannot really + know that because it is a discard server. You will not get any response + at all. To prove it is really working, let us modify the server to print + what it has received. We already know that &MessageEvent; is generated whenever data is received and the messageReceived handler method - will be invoked. Let's put some code into the + will be invoked. Let us put some code into the messageReceived method of the DiscardServerHandler: @@ -246,7 +247,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { It is safe to assume the message type in socket transports is always &ChannelBuffer;. &ChannelBuffer; is a fundamental data structure which stores a sequence of bytes in Netty. It's similar to NIO - ByteBuffer, but it's easier to use and more + ByteBuffer, but it is easier to use and more flexible. For example, Netty allows you to create a composite &ChannelBuffer; which combines multiple &ChannelBuffer;s reducing the number of unnecessary memory copy. @@ -273,7 +274,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { Writing an Echo 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 ECHO protocol, where any received data is sent back. @@ -281,9 +282,8 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { The only difference from the discard server we have implemented in the previous sections is that it sends the received data back instead of - printing the received data out to the console. Therefore, it's just - enough again to modify the messageReceived - method: + printing the received data out to the console. Therefore, it is enough + again to modify the messageReceived method: @Override public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { @@ -324,10 +324,10 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { 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 messageReceived method this time. Instead, we should override the channelConnected method. - Here's the implementation: + The following is the implementation: package org.jboss.netty.example.time; @@ -361,7 +361,7 @@ public class TimeServerHandler extends &SimpleChannelHandler; { As explained, channelConnected 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. @@ -373,11 +373,11 @@ public class TimeServerHandler extends &SimpleChannelHandler; { 4 bytes. The &ChannelBuffers; helper class is used to allocate a new buffer. Besides the buffer method, &ChannelBuffers; provides a - lot of useful methods related with &ChannelBuffer;. Please refer to - the API reference. + lot of useful methods related to the &ChannelBuffer;. For more + information, please refer to the API reference. - 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;: import static org.jboss.netty.buffer.&ChannelBuffers;.*; ... @@ -392,19 +392,19 @@ public class TimeServerHandler extends &SimpleChannelHandler; { But wait, where's the flip? Didn't we used to call ByteBuffer.flip() 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 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 ends respectively. - 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 flip method. You will be in trouble when 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 makes your life much easier as you get used to it -- a life without flipping out! @@ -412,7 +412,7 @@ public class TimeServerHandler extends &SimpleChannelHandler; { Another point to note is that the write 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 are asynchronous in Netty. For example, the following code might close the connection even before a message is sent: @@ -424,7 +424,7 @@ ch.close(); Therefore, you need to call the close method after the &ChannelFuture;, which was returned by the write method, notifies you when the write - operation has been done. Also, close + operation has been done. Please note that, close might not close the connection immediately, and it returns a &ChannelFuture;. @@ -432,7 +432,7 @@ ch.close(); 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; which closes the &Channel; when the operation is done. @@ -449,9 +449,9 @@ ch.close(); Writing a Time Client 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 - calendar. Let's make sure the server works correctly and learn how to - write a client with Netty. + because a human cannot translate a 32-bit binary data into a date on a + calendar. In this section, we discuss how to make sure the server works + correctly and learn how to write a client with Netty. The biggest and only difference between a server and a client in Netty @@ -500,7 +500,7 @@ public class TimeClient { Please note that there's no "child." prefix. - A client-side &SocketChannel; doesn't have a parent. + A client-side &SocketChannel; does not have a parent. @@ -511,10 +511,10 @@ public class TimeClient { - As you saw, it's not really different from the server side startup. What - about the &ChannelHandler; implementation? It should receive a 32-bit - integer from the server, translate it into a human readable format, print - the translated time, and close the connection: + As you can see, it is not really different from the server side startup. + What about the &ChannelHandler; implementation? It should receive a + 32-bit integer from the server, translate it into a human readable format, + print the translated time, and close the connection: package org.jboss.netty.example.time; @@ -536,10 +536,10 @@ public class TimeClientHandler extends &SimpleChannelHandler; { } } - 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 - IndexOutOfBoundsException. Let's figure - out why in the next section. + IndexOutOfBoundsException. We discuss why + this happens in the next section.
@@ -554,20 +554,20 @@ public class TimeClientHandler extends &SimpleChannelHandler; { In a stream-based transport such as TCP/IP, packets can be fragmented and 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: +-----+-----+-----+ | ABC | DEF | GHI | +-----+-----+-----+ - because of the packet fragmentation, a server can receive them like the - following: + because of the packet fragmentation, a server can receive them as + follows: +----+-------+---+---+ | AB | CDEFG | H | I | +----+-------+---+---+ - 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 frames that could be easily understood by the application logic. In case of the example above, the received packets @@ -582,17 +582,17 @@ public class TimeClientHandler extends &SimpleChannelHandler; { The First Solution - 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 likely to be fragmented often. However, the problem is that it can be fragmented, and the possibility of - fragmentation will increase as the traffic goes higher. + fragmentation will increase as the traffic increases. The simplistic solution is to create an internal cumulative buffer and - wait until all 4 bytes are received into the internal buffer. Here's - the modified TimeClientHandler implementation - that fixes the problem. + wait until all 4 bytes are received into the internal buffer. The + following is the modified TimeClientHandler + implementation that fixes the problem: 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 instance of TimeClientHandler is shared by multiple &Channel;s (and consequently multiple &ChannelPipeline;s), - the content of the buf will be messed up. + the content of the buf will be corrupted. @@ -657,11 +657,11 @@ public class TimeClientHandler extends &SimpleChannelHandler; { - 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 TimeClientHandler instance to the default &ChannelPipeline; of the &ClientBootstrap;? It means one TimeClientHandler 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 TimeClientHandler instance per &Channel;, we should implement a &ChannelPipelineFactory;: @@ -676,7 +676,7 @@ public class TimeClientPipelineFactory implements &ChannelPipelineFactory; { } }
- Now let's replace the following lines of TimeClient: + Now let us replace the following lines of TimeClient: TimeClientHandler handler = new TimeClientHandler(); bootstrap.getPipeline().addLast("handler", handler); @@ -702,17 +702,17 @@ bootstrap.getPipeline().addLast("handler", handler); 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 variable length field. Your &ChannelHandler; implementation will become unmaintainable very quickly. - As you already might have noticed, you can add more than one - &ChannelHandler; to a &ChannelPipeline;, and therefore, you can - split one monolithic &ChannelHandler; into multiple modular ones to - reduce the complexity of your application. For example, you could - split TimeClientHandler into two handlers: + As you may have noticed, you can add more than one &ChannelHandler; to + a &ChannelPipeline;, and therefore, you can split one monolithic + &ChannelHandler; into multiple modular ones to reduce the complexity of + your application. For example, you could split + TimeClientHandler into two handlers: @@ -764,8 +764,8 @@ public class TimeDecoder extends &FrameDecoder; { If null is returned, it means there's not - enough data yet. &FrameDecoder; will call again when more data is - in. + enough data yet. &FrameDecoder; will call again when there is a + sufficient amount of data. @@ -781,7 +781,7 @@ public class TimeDecoder extends &FrameDecoder; { - 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 need to consult the API reference for more information though. @@ -798,10 +798,9 @@ public class TimeDecoder extends &ReplayingDecoder;<&VoidEnum;> { } 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. - You might want to take a look into the following packages for more - detailed examples: + Please refer to the following packages for more detailed examples: @@ -825,9 +824,9 @@ public class TimeDecoder extends &ReplayingDecoder;<&VoidEnum;> { Speaking in POJO instead of ChannelBuffer - All the examples we visited so far used a &ChannelBuffer; as a primary - data structure of a protocol message. In this section, we will improve - the TIME protocol client and server example to use a + All the examples we have reviewed so far used a &ChannelBuffer; as a + primary data structure of a protocol message. In this section, we will + improve the TIME protocol client and server example to use a POJO instead of a &ChannelBuffer;. @@ -836,12 +835,12 @@ public class TimeDecoder extends &ReplayingDecoder;<&VoidEnum;> { your handler becomes more maintainable and reusable by separating the code which extracts information from &ChannelBuffer; out from the 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 implement a real world protocol. - First, let's define a new type called UnixTime. + First, let us define a new type called UnixTime. package org.jboss.netty.example.time; @@ -887,7 +886,7 @@ protected Object decode( With the updated decoder, the TimeClientHandler - doesn't use &ChannelBuffer; anymore: + does not use &ChannelBuffer; anymore: @Override public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { @@ -897,7 +896,7 @@ public void messageReceived(&ChannelHandlerContext; ctx, &MessageEvent; e) { } 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 TimeServerHandler first this time: @Override @@ -963,7 +962,7 @@ public class TimeEncoder implements &SimpleChannelHandler; { in the &ChannelPipeline;. - 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;: import static org.jboss.netty.channel.&Channels;.*; ... @@ -975,7 +974,7 @@ fireChannelDisconnected(ctx, e.getChannel()); The last task left is to insert a TimeEncoder - 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. @@ -985,12 +984,11 @@ fireChannelDisconnected(ctx, e.getChannel()); Summary - In this chapter, we had a quick tour of Netty so that you can start to - write a network application on top of Netty right away. I believe you - still have a lot of questions about various topics, and they will be - covered in the upcoming chapters and the revised version of this chapter. - Also, please don't forget that the Netty project - community is always here to help you. + In this chapter, we had a quick tour of Netty with a demonstration on how + to write a network application on top of Netty. More questions you may + have will be covered in the upcoming chapters and the revised version of + this chapter. Please also note that the Netty + project community is always here to help you.