继续翻译:http://netty.io/wiki/user-guide-for-5.x.html
最简单的协议不是hello,而是DISCARD.
也就是说,忽略所有收到的数据,不返回任何响应。
为了完成这个协议,你仅仅需要做的是忽略所有收到的数据,让我们开始从handler实现开始,这个处理IO事件(来源于Netty).
package io.netty.example.discard;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandlerAdapter;
/**
* Handles a server-side channel.
*/
public class DiscardServerHandler extends ChannelHandlerAdapter { // (1)
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
// Discard the received data silently.
((ByteBuf) msg).release(); // (3)
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
// Close the connection when an exception is raised.
cause.printStackTrace();
ctx.close();
}
}
DiscardServerHandler extends ChannelHandlerAdapter, 是对ChannelHandler的一个实现. ChannelHandler 提供了各种各样的事件方法可覆盖.
目前,继承 ChannelHandlerAdapter足够了 而不是自己实现接口里的方法。
我们重写了channelRead() 事件方法,这个方法被接收到的消息调用,当从客户端里接收到新数据时。在这个例子当中,接收到的信息是 ByteBuf.
为了实现DISCARD协议,方法必须忽略收到的消息,ByteBuf 是一个计数器方式引用的对象,只有显式的通过release()方法才可以释放这个对象。请注意:这个handler有责任来释放任何传给自己的计数器引用的对象。
通常, channelRead() 方法用以下形式实现
@Overridepublic void channelRead(ChannelHandlerContext ctx, Object msg) { try { // Do something with msg } finally { ReferenceCountUtil.release(msg); }}
The exceptionCaught() 事件方法:
当处理事件时,IO错误或者handler实现里有异常抛出。
大多数情况下,异常应该记录下来,并且相关的channel应该被关闭,尽管这个方法的实现可以不同,这依赖于你想怎么处理。
比如,你也许想发送一个响应消息伴随错误码在关闭连接之前。
我们已经实现了一半,剩下的是写main()方法来启动这个服务器。
package io.netty.example.discard;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* Discards any incoming data.
*/
public class DiscardServer {
private int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap(); // (2)
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class) // (3)
.childHandler(new ChannelInitializer<SocketChannel>() { // (4)
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
})
.option(ChannelOption.SO_BACKLOG, 128) // (5)
.childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync(); // (7)
// Wait until the server socket is closed.
// In this example, this does not happen, but you can do that to gracefully
// shut down your server.
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws Exception {
int port;
if (args.length > 0) {
port = Integer.parseInt(args[0]);
} else {
port = 8080;
}
new DiscardServer(port).run();
}
}
1. NioEventLoopGroup 是一个多线程的事件循环,处理IO操作。
Netty提供了各种各样的 EventLoopGroup实现来应对不同种类的传输。
我们在例子中实现服务器端的工程,因此两个NioEventLoopGroup 将会被使用,第一个,经常叫做boss,接收一个进来的连接,第二个,叫做worker,处理流量(接受的连接)一旦 boss接收了连接并且注册接收的连接到worker.多少线程被使用并且它们如何映射到创建的Channels依赖于 EventLoopGroup 实现,也可以通过构造器配置。
2. ServerBootstrap 是一个帮助类来安装服务器,你可以通过Channel来直接安装。
3. 尽管如此,要知道,这是一个单调的过程,大多数情况下不需要做这个。
4. 这里,我们使用了 NioServerSocketChannel 类来初始化一个新的Channel来接收进来的连接。这个handler总是被新接受的channel调用。
5. ChannelInitializer是一个特别的handler,目的是帮助一个用户来配置一个新的Channel. 最有可能的就是你想配置 ChannelPipeline of the new Channel 通过增加新的句柄方法比如DiscardServerHandler 来实现你的网络工程。
6. 一旦工程变复杂了,你将增加更多的方法到pipeline上来,提取这个匿名类到一个底层类里。
7. 你也可以设置参数(特定于channel实现),我们在写一个TCP/IP服务器,所以我们可以设置socket的参数比如tcpNoDelay and keepAlive.
8. 请参考ChannelOption 和特定的ChannelConfig实现来获取支持的选项说明。
9. 你意识到option()和childOption()?
10. option()是为了NioServerSocketChannel ,这个接受连接。childOption()是为了channels(被父ServerChannel接受) ,在例子当中是 NioServerSocketChannel 。
11. 我们准备好了,剩下的就是绑定端口和启动服务器,这里,我们绑定到8080端口,你可以调用bind()方法任意多次只要愿意。(可以以不同的绑定定制).
祝贺,你已经完成了第一个服务器的开发。
翻译 | ok | ok | ok |
理解 | ok | ok | ok |