Netty实现群聊

如题,只是为了实验,我将所有的客户端的channel存在静态变量ChannelGroup实例中,对消息进行群发。

当然,如果实际的环境中,我估计要将channel存在缓存数据库中,具体怎么做,后面再研究。

现在,我们来做这次简单的实验:

源代码:https://github.com/YangZhouChaoFan/netty-learn/tree/master/netty-start


1:NettyServer

package com.netty.start.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;


/**
 * 服务器类.
 */
public class NettyServer {

    public void start(int port) throws Exception {

        //创建接收者的事件循环组
        EventLoopGroup parentGroup = new NioEventLoopGroup();
        //创建访问者的事件循环组
        EventLoopGroup childGroup = new NioEventLoopGroup();

        try {
            //创建服务器引导程序
            ServerBootstrap b = new ServerBootstrap();
            //设置消息循环
            b.group(parentGroup, childGroup);
            //设置通道
            b.channel(NioServerSocketChannel.class);
            //配置通道参数:连接队列的连接数
            b.option(ChannelOption.SO_BACKLOG, 1024);
            //设置客户端请求的处理操作
            b.childHandler(new ChildChannelHandler());
            //绑定端口,并获取通道io操作的结果
            ChannelFuture f = b.bind(port).sync();
            //等待服务端监听端口关闭
            f.channel().closeFuture().sync();
        } finally {
            //关闭接收器事件循环
            parentGroup.shutdownGracefully();
            //关闭访问者的事件循环
            childGroup.shutdownGracefully();
        }

    }

}


2:ChildChannelHandler

package com.netty.start.server;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * 客户端通道处理类.
 */
public class ChildChannelHandler extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel e) throws Exception {

        ChannelPipeline pipeline = e.pipeline();
        // 以("\n")为结尾分割的 解码器
        pipeline.addLast(new LineBasedFrameDecoder(1024));
        // 字符串解码 和 编码
        pipeline.addLast(new StringDecoder());
        pipeline.addLast(new StringEncoder());
        //添加消息处理
        e.pipeline().addLast(new NettyServerHandler());

    }

}


3:NettyServerHandler

package com.netty.start.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;

/**
 * 服务器处理类.
 */
public class NettyServerHandler extends ChannelHandlerAdapter {

    static private Logger logger = LoggerFactory.getLogger(NettyServerHandler.class);

    //创建频道组
    public static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**
     * 连接通道.
     *
     * @param ctx
     * @param remoteAddress
     * @param localAddress
     * @param promise
     * @throws Exception
     */
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        logger.info(remoteAddress + ":连接通道");
        super.connect(ctx, remoteAddress, localAddress, promise);
    }

    /**
     * 活跃通道.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":通道激活");
        super.channelActive(ctx);
        ctx.writeAndFlush("欢迎访问服务器\r\n");
        channels.add(ctx.channel());
    }

    /**
     * 非活跃通道.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":通道失效");
        super.channelInactive(ctx);
        channels.remove(ctx.channel());
    }

    /**
     * 接收消息.
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":" + msg);
        Channel currentChannel = ctx.channel();
        for (Channel channel : channels) {
            if (channel != currentChannel) {
                channel.writeAndFlush("[" + currentChannel.remoteAddress() + "]" + msg + "\n");
            }
        }
    }

    /**
     * 接收完毕.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    /**
     * 关闭通道.
     *
     * @param ctx
     * @param promise
     * @throws Exception
     */
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":关闭通道");
        super.close(ctx, promise);
    }

    /**
     * 异常处理.
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.info("异常信息:" + cause.getMessage());
    }
}


4:NettyClient

package com.netty.start.client;

import com.netty.start.server.ChildChannelHandler;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * 客户端类.
 */
public class NettyClient {

    public void connect(String host, int port) throws Exception {

        //创建事件循环组
        EventLoopGroup group = new NioEventLoopGroup();

        try {

            //创建引导程序
            Bootstrap b = new Bootstrap();
            //设置消息循环
            b.group(group);
            //设置通道
            b.channel(NioSocketChannel.class);
            //配置通道参数:tcp不延迟
            b.option(ChannelOption.TCP_NODELAY, true);
            //设置通道处理
            b.handler(new ChannelHandler());
            //发起异步链接,等待输入参数
            Channel channel = b.connect(host, port).sync().channel();
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                channel.writeAndFlush(in.readLine() + "\r\n");
            }

        } finally {
            //关闭
            group.shutdownGracefully();
        }

    }

}


5:ChannelHandler

package com.netty.start.client;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.LineBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

/**
 * 通道处理类.
 */
public class ChannelHandler extends ChannelInitializer<SocketChannel> {

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {

        ChannelPipeline pipeline = socketChannel.pipeline();
        // 以("\n")为结尾分割的 解码器
        pipeline.addLast(new LineBasedFrameDecoder(1024));
        // 字符串解码 和 编码
        pipeline.addLast(new StringDecoder());
        pipeline.addLast(new StringEncoder());

        socketChannel.pipeline().addLast(new NettyClientHandler());

    }
}


6:NettyClientHandler

package com.netty.start.client;

import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;

/**
 * 客户端处理类.
 */
public class NettyClientHandler extends ChannelHandlerAdapter {

    static private Logger logger = LoggerFactory.getLogger(NettyClientHandler.class);

    /**
     * 连接通道.
     *
     * @param ctx
     * @param remoteAddress
     * @param localAddress
     * @param promise
     * @throws Exception
     */
    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        logger.info(remoteAddress + ":连接通道");
        super.connect(ctx, remoteAddress, localAddress, promise);
    }

    /**
     * 活跃通道.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":通道激活");
        super.channelActive(ctx);
    }

    /**
     * 非活跃通道.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":通道失效");
        super.channelInactive(ctx);
    }

    /**
     * 接收消息.
     *
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.info(ctx.channel().remoteAddress() + ":" + msg);
    }

    /**
     * 接收完毕.
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
    }

    /**
     * 关闭通道.
     *
     * @param ctx
     * @param promise
     * @throws Exception
     */
    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        super.close(ctx, promise);
    }

    /**
     * 异常处理.
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.info("异常信息:" + cause.getMessage());
    }
}


7:ServerTest

package com.netty.start.test;

import com.netty.start.server.NettyServer;

/**
 * Created by chenhao on 2016/3/17.
 */
public class ServerTest {

    public static void main(String[] args) throws Exception {

        NettyServer server = new NettyServer();
        server.start(3000);

    }

}


8:ClientTest

package com.netty.start.test;

import com.netty.start.client.NettyClient;

/**
 * Created by chenhao on 2016/3/17.
 */
public class ClientTest {

    public static void main(String[] args) throws Exception {

        NettyClient client = new NettyClient();
        client.connect("127.0.0.1", 3000);

    }
}


通过7和8两个类,启动测试实例。

转载于:https://my.oschina.net/chenhao901007/blog/639534

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值