第一章. 开始
这一章节将围绕Netty的核心结构展开,同时通过一些简单的例子可以让你更快的了解Netty的使用。当你读完本章,你将有能力使用Netty完成客户端和服务端的开发。
如果你更喜欢自上而下式的学习方式,你可以首先完成 第二章:架构总览 的学习,然后再回到这里。
1.1. 开始之前
运行本章示例程序的两个最低要求是:最新版本的Netty程序以及JDK 1.5或更高版本。最新版本的Netty程序可在项目下载页 下载。下载正确版本的JDK,请到你偏好的JDK站点下载。
这就已经足够了吗?实际上你会发现,这两个条件已经足够你完成任何协议的开发了。如果不是这样,请联系Netty项目社区 ,让我们知道还缺少了什么。
最终但不是至少,当你想了解本章所介绍的类的更多信息时请参考API手册。为方便你的使用,这篇文档中所有的类名均连接至在线API手册。此外,如果本篇文档中有任何错误信息,无论是语法错误,还是打印排版错误或者你有更好的建议,请不要顾虑,立即联系Netty项目社区 。
1.2. 抛弃协议服务
在这个世界上最简化的协议不是“Hello,world!”而是抛弃协议 。这是一种丢弃接收到的任何数据并不做任何回应的协议。
实现抛弃协议(DISCARD protocol),你仅需要忽略接受到的任何数据即可。让我们直接从处理器(handler)实现开始,这个处理器处理Netty的所有I/O事件。
[java] view
plaincopy
- package org.jboss.netty.example.discard;
- @ChannelPipelineCoverage("all")1
- public class DiscardServerHandler extends SimpleChannelHandler {2
- @Override
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {3
- }
- @Override
- public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {4
- e.getCause().printStackTrace();
- Channel ch = e.getChannel();
- ch.close();
- }
- }
代码说明
1)ChannelPipelineCoverage注解了一种处理器类型,这个注解标示了一个处理器是否可被多个Channel通道共享(同时关联着ChannelPipeline)。DiscardServerHandler没有处理任何有状态的信息,因此这里的注解是“all”。
2)DiscardServerHandler继承了SimpleChannelHandler,这也是一个ChannelHandler 的实现。SimpleChannelHandler提供了多种你可以重写的事件处理方法。目前直接继承SimpleChannelHandler已经足够了,并不需要你完成一个自己的处理器接口。
3)我们这里重写了messageReceived事件处理方法。这个方法由一个接收了客户端传送数据的MessageEvent事件调用。在这个例子中,我们忽略接收到的任何数据,并以此来实现一个抛弃协议(DISCARD protocol)。
4)exceptionCaught 事件处理方法由一个ExceptionEvent异常事件调用,这个异常事件起因于Netty的I/O异常或一个处理器实现的内部异常。多数情况下,捕捉到的异常应当被记录下来,并在这个方法中关闭这个channel通道。当然处理这种异常情况的方法实现可能因你的实际需求而有所不同,例如,在关闭这个连接之前你可能会发送一个包含了错误码的响应消息。
目前进展不错,我们已经完成了抛弃协议服务器的一半开发工作。下面要做的是完成一个可以启动这个包含DiscardServerHandler处理器服务的主方法。
[java] view
plaincopy
- package org.jboss.netty.example.discard;
- import java.net.InetSocketAddress;
- import java.util.concurrent.Executors;
- public class DiscardServer {
- public static void main(String[] args) throws Exception {
- ChannelFactory factory =
- new NioServerSocketChannelFactory (
- Executors.newCachedThreadPool(),
- Executors.newCachedThreadPool());
- ServerBootstrap bootstrap = new ServerBootstrap (factory);
- DiscardServerHandler handler = new DiscardServerHandler();
- ChannelPipeline pipeline = bootstrap.getPipeline();
- pipeline.addLast("handler", handler);
- bootstrap.setOption("child.tcpNoDelay", true);
- bootstrap.setOption("child.keepAlive", true);
- bootstrap.bind(new InetSocketAddress(8080));
- }
- }
代码说明
1)ChannelFactory 是一个创建和管理Channel通道及其相关资源的工厂接口,它处理所有的I/O请求并产生相应的I/O ChannelEvent通道事件。Netty 提供了多种 ChannelFactory 实现。这里我们需要实现一个服务端的例子,因此我们使用NioServerSocketChannelFactory实现。另一件需要注意的事情是这个工厂并自己不负责创建I/O线程。你应当在其构造器中指定该工厂使用的线程池,这样做的好处是你获得了更高的控制力来管理你的应用环境中使用的线程,例如一个包含了安全管理的应用服务。
2)ServerBootstrap 是一个设置服务的帮助类。你甚至可以在这个服务中直接设置一个Channel通道。然而请注意,这是一个繁琐的过程,大多数情况下并不需要这样做。
3)这里,我们将DiscardServerHandler处理器添加至默认的ChannelPipeline通道。任何时候当服务器接收到一个新的连接,一个新的ChannelPipeline管道对象将被创建,并且所有在这里添加的ChannelHandler对象将被添加至这个新的ChannelPipeline管道对象。这很像是一种浅拷贝操作(a shallow-copy operation);所有的Channel通道以及其对应的ChannelPipeline实例将分享相同的DiscardServerHandler实例。
4)你也可以设置我们在这里指定的这个通道实现的配置参数。我们正在写的是一个TCP/IP服务,因此我们运行设定一些socket选项,例如tcpNoDelay和keepAlive。请注意我们在配置选项里添加的"child."前缀。这意味着这个配置项仅适用于我们接收到的通道实例,而不是ServerSocketChannel实例。因此,你可以这样给一个ServerSocketChannel设定参数:
bootstrap.setOption("reuseAddress", true);
5)我们继续。剩下要做的是绑定这个服务使用的端口并且启动这个服务。这里,我们绑定本机所有网卡(NICs,network interface cards)上的8080端口。当然,你现在也可以对应不同的绑定地址多次调用绑定操作。
1.3. 查看接收到的数据
现在你已经完成了你的第一个服务端程序,我们需要测试它是否可以真正的工作。最简单的方法是使用telnet 命令。例如,你可以在命令行中输入“telnet localhost 8080 ”或其他类型参数。
然而,我们可以认为服务器在正常工作吗?由于这是一个丢球协议服务,所以实际上我们无法真正的知道。你最终将收不到任何回应。为了证明它在真正的工作,让我们修改代码打印其接收到的数据。
我们已经知道当完成数据的接收后将产生MessageEvent消息事件,并且也会触发messageReceived处理方法。所以让我在DiscardServerHandler处理器的messageReceived方法内增加一些代码。
[java] view
plaincopy
- @Override
- public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
- ChannelBuffer buf = (ChannelBuffer) e.getMessage();
- while(buf.readable()) {
- System.out.println((char) buf.readByte());
- }
- }
代码说明
1) 基本上我们可以假定在socket的传输中消息类型总是ChannelBuffer。ChannelBuffer是Netty的一个基本数据结构,这个数据结构存储了一个字节序列。ChannelBuffer类似于NIO的ByteBuffer,但是前者却更加的灵活和易于使用。例如,Netty允许你创建一个由多个ChannelBuffer构建的复合ChannelBuffer类型,这样就可以减少不必要的内存拷贝次数。
2) 虽然ChannelBuffer有些类似于NIO的ByteBuffer,但强烈建议你参考Netty的API手册。学会如何正确的使用ChannelBuffer是无障碍使用Netty的关键一步。
如果你再次运行telnet命令,你将会看到你所接收到的数据。
抛弃协议服务的所有源代码均存放在在分发版的org.jboss.netty.example.discard包下。
Netty中文用户手册(二)