Netty版本:4.0.18
这个例子接受socket连接,并将客户端发送来的数据输出到控制台,不做任何响应。代码来自Netty的Example。
首先看源代码。运行这个例子,然后执行命令telnet localhost 8080 在命令行的任何输入,都将被服务端输出到控制台。
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.ReferenceCountUtil;
public class DiscardServerHandler extends SimpleChannelInboundHandler<Object> {
public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf in = (ByteBuf) msg;
try {
while (in.isReadable()) { // (1)
System.out.print((char) in.readByte());
System.out.flush();
}
} finally {
//ReferenceCountUtil.release(msg); // (2)
}
}
public void exceptionCaught(ChannelHandlerContext ctx,
Throwable cause) throws Exception {
// Close the connection when an exception is raised.
logger.log(
Level.WARNING,
"Unexpected exception from downstream.",
cause);
ctx.close();
}
}
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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 final int port;
public DiscardServer(int port) {
this.port = port;
}
public void run() throws Exception {
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//bossGroup只用来监听客户端的连接,可用阻塞的方式,所以只需要一个线程,故传入参数1
EventLoopGroup workerGroup = new NioEventLoopGroup();
//workerGroup用来处理客户端与服务端的各种IO操作
try {
ServerBootstrap b = new ServerBootstrap();//服务端的引导程序
b.group(bossGroup, workerGroup) //boss将赋给b的父类实例,worker赋给b
.channel(NioServerSocketChannel.class)//将通过反射机制创建该实例
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new DiscardServerHandler());
}
}); //这里的匿名类的实例将加到每个socketchannel的pipeline中。(每个客户端与服务端连接成功都将产生一个socketchannel)
// Bind and start to accept incoming connections.
ChannelFuture f = b.bind(port).sync();
//netty中所有的IO操作都是异步的,也就是说IO操作会立即返回,返回时不保证操作已经完成
//通过返回一个ChannelFuture来告诉你IO操作的结果和状态
//调用channelFuture的sync方法,为阻塞线程,等待结果的出现,该方法被打断时会抛出InterruptedException
// 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();
}
}
- 服务端的启动过程:
调用bind后,首先了调用AbstractBootstrap中的initAndRegister方法,通过反射机制实例化一个NioServerSocketChannel(调用ServerBootstrap.channel方法传入了它的class),代码中变量名为channel,为了防止迷惑,称之为serverSocketChannel。然后初始化serverSocketChannel,设置它的参数(可设置的参数见ChannelOption类),获取它的ChannelPipeline,并添加了一个ChannelHandler(实际上为ServerBootstrapAcceptor的实例,该实例包含了workerGroup,它的handler,以及一些参数),最后向bossGroup注册。返回一个ChannelFuture。到这里启动就结束了。ChannelFuture的解释在注释中。
- 客户端与服务端的连接过程
当我们使用telnet localhost 8080连接服务端时,NioServerSocketChannel通过调用accept方法完成连接,获得socketChannel。触发channelRead事件,通过回调从serverSocketChannel的ChannelPipeline中选择第一个ChannelHandler(也即head,默认情况下serverSocketChannel也只有这么一个handler,貌似也没有添加多个的必要,毕竟serverSocketChannel只是用来处理连接而已,上文说过,这个head也就是ServerBootstrapAcceptor的实例,这个类是ServerBootstrap的内部类)。然后调用head这个handler的channelRead(ChannelHandlerContext ctx, Object msg)方法。(事实上,这个msg对象就是这个socketChannel),然后获取socketChannel的ChannelPipeline,现在这个管道中是没有任何handler的,随后将我们启动服务端时,调用ServerBootstrap.childChannel()方法传入的handler加入到这个socketChannel的管道中。随后将这个socketChannel向workerGroup注册,到此为止,客户端和服务端连接成功。