netty 简单聊天室

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();
    }

}






                  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值