http://www.tuicool.com/articles/mEJvYb
一、处理器类handler
ChannelInboundHandler接口:ChannelInboundHandler 提供了许多事件处理的接口方法。
我们常用的两个实现类:
SimpleChannelInboundHandler类和ChannelInboundHandlerAdapter类,
一般客户端的业务Handler继承的 是SimpleChannelInboundHandler,而在服务器端继承的是 ChannelInboundHandlerAdapter。
两者最主要的区别就是SimpleChannelInboundHandler在接收到数据后会自动release掉数据占用的Bytebuffer资源(自动调用Bytebuffer.release())。而为何服务器端不能用呢,因为我们想让服务器把客户端请求的数据发送回去,而服务器端有可能在channelRead方法返回前还没有写完数据,因此不能让它自动release。并且SimpleChannelInboundHandler可以指定泛型。
1、handlerAdded() 事件处理方法。每当从服务端收到新的客户端连接时调用。
2、handlerRemoved() 事件处理方法。每当从服务端收到客户端断开时调用。
3、channelRead0() 事件处理方法。每当从服务端读到客户端写入信息时调用。
4、channelActive() 事件处理方法。服务端监听到客户端活动时调用。
5、channelInactive() 事件处理方法。服务端监听到客户端不活动时调用。
6、exceptionCaught() 事件处理方法。当出现 Throwable 对象才会被调用。
自定义的处理器类继承于上面两个类,用于处理业务逻辑。
例如:
package SimpleChat;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
//处理器
public class SimpleChatServerHandler extends SimpleChannelInboundHandler<String>{
//取得所有客户端的通道
public static ChannelGroup channels=new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//当从服务端收到新的客户端连接时调用
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
Channel incoming=ctx.channel();//取得当前通道
for(Channel channel:channels){//给每个在线的客户端发送信息
channel.writeAndFlush("[SERVER]-"+incoming.remoteAddress()+"加入\n");
}
channels.add(incoming);//将新加入的客户端Channel加入ChannelGroup
}
//当从服务端收到客户端断开时调用
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
Channel incoming=ctx.channel();
for(Channel channel:channels){
channel.writeAndFlush("[SERVER-]"+incoming.remoteAddress()+"离开\n");
}
channels.remove(incoming);//客户端的 Channel 移除 ChannelGroup 列表中
}
//服务端监听到客户端活动时调用
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
Channel incoming=ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"在线");
}
//当从服务端读到客户端写入信息时调用
@Override
protected void channelRead0(ChannelHandlerContext ctx, String str)
throws Exception {
// TODO Auto-generated method stub
Channel incoming=ctx.channel();
//从服务端读到客户端写入信息时,将信息转发给其他客户端的 Channel。
for(Channel channel:channels){
if(channel!=incoming){
channel.writeAndFlush("["+incoming.remoteAddress()+"]:"+str+"\n");
}else{
channel.writeAndFlush("[you]:"+str+"\n");
}
}
}
//服务端监听到客户端不活动时调用
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
// TODO Auto-generated method stub
Channel incoming=ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"下线");
}
//当出现 Throwable 对象才会被调用
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
throws Exception {
// TODO Auto-generated method stub
Channel incoming=ctx.channel();
System.out.println("SimpleChatClient:"+incoming.remoteAddress()+"异常");
cause.printStackTrace();
ctx.close();
}
}
二、配置Channel类
ChannelInitializer类:需要指定泛型SocketChannel,当一个链接建立时,我们需要知道怎么来接收或者发送数据,当然,我们有各种各样的Handler实现来处理它,那么ChannelInitializer便是用来配置这些Handler。
配置类继承于ChannelInitializer,用来增加多个的处理类到 ChannelPipeline 上,包括编码、解码、SimpleChatS
erverHandler 等。
如:
public class SimpleChatServerInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline pipeline=ch.pipeline();
// 以("\n")为结尾分割的 解码器
pipeline.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
pipeline.addLast("handler",new SimpleChatServerHandler());
System.out.println("SimpleChatClient:"+ch.remoteAddress()+"连接上");
}
}
三、服务器类
NioEventLoopGroup是用来处理I/O操作的多线程事件循环器,Netty 提供了许多不同的EventLoopGroup的
实现用来处理不同的传输。服务端有2个 NioEventLoopGroup 被使用 ,‘boss’,用来接收进来的接;
‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。
ServerBootstrap是一个启动 NIO 服务的辅助启动类。
使用NioServerSocketChannel类来举例说明一个新的 Channel 如何接收进来的连接。
option() 是提供给NioServerSocketChannel用来接收进来的连接。
childOption() 是提供给由父管道ServerChannel接收到的连接。
例如:
public class SimpleChatServer {
private int port;
public SimpleChatServer(int port) {
// TODO Auto-generated constructor stub
this.port=port;
}
public void run() {
// TODO Auto-generated method stub
//NioEventLoopGroup是用来处理I/O操作的多线程事件循环器
EventLoopGroup bossGroup=new NioEventLoopGroup();//用来接收进来的连接
EventLoopGroup workGroup=new NioEventLoopGroup();//用来处理已经被接收的连接
try {
//ServerBootstrap是一个启动 NIO 服务的辅助启动类
ServerBootstrap b=new ServerBootstrap();
b.group(bossGroup, workGroup);
b.channel(NioServerSocketChannel.class);
b.childHandler(new SimpleChatServerInitializer());//配置一个新的 Channel
//option() 是提供给NioServerSocketChannel用来接收进来的连接
b.option(ChannelOption.SO_BACKLOG, 128);
//childOption() 是提供给由父管道ServerChannel接收到的连接
b.childOption(ChannelOption.SO_KEEPALIVE, true);
System.out.println("SimpleChatServer 启动了");
// 绑定端口,开始接收进来的连接
ChannelFuture future=b.bind(port).sync();
//关闭你的服务器
future.channel().closeFuture().sync();
} catch (Exception e) {
// TODO: handle exception
System.out.println("SimpleChatServer 启动异常");
}finally{
workGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
System.out.println("SimpleChatServer 关闭了");
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int port;
if(args.length>0){
port=Integer.parseInt(args[0]);
}else{
port=8080;
}
new SimpleChatServer(port).run();
}
}
四、客户端与服务器端类似
1、处理类
public class SimpleChatClientHandler extends SimpleChannelInboundHandler<String>{
@Override
protected void channelRead0(ChannelHandlerContext ctx, String str)//str,读到的服务器传过来的信息
throws Exception {
// TODO Auto-generated method stub
System.out.println(str);//打印接受到server的信息
}
}
2、配置channel类
public class SimpleChatClientInitializer extends ChannelInitializer<SocketChannel>{
@Override
protected void initChannel(SocketChannel ch) throws Exception {
// TODO Auto-generated method stub
ChannelPipeline p=ch.pipeline();
p.addLast("framer",new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
p.addLast("decoder",new StringDecoder());
p.addLast("encoder",new StringEncoder());
p.addLast("handler",new SimpleChatClientHandler());
}
}
3、Client类
public class SimpleChatClient {
private String host;
private int port;
public SimpleChatClient(String host,int port) {
// TODO Auto-generated constructor stub
this.host=host;
this.port=port;
}
public void run() {
// TODO Auto-generated method stub
EventLoopGroup group=new NioEventLoopGroup();
try{
Bootstrap bootstrap=new Bootstrap();//配置整个Netty程序,串联起各个组件。
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new SimpleChatClientInitializer());
Channel channel=bootstrap.connect(host, port).sync().channel();
BufferedReader buff=new BufferedReader(new InputStreamReader(System.in));
while(true){
String line=buff.readLine();
if(line!=null){
channel.writeAndFlush(line+"\r\n");
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
group.shutdownGracefully();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
new SimpleChatClient("127.0.0.1", 8080).run();
}
}