netty4学习笔记4-使用netty的socket完成聊天室功能的开发

需求:

1. 当有客户端连上服务器了,则通知其他的客户端,XXX连上服务器了(注:自己就不在通知自己了)

2. 当有客户端断线的时候,通知其他的客户端,XXX断线了

3. 当有客户端处于激活状态的时候,通知其他的客户端,XXX上线了(注: 上线了和连上服务器是2个概念)

4. 当有客户端处于失活状态的时候,通知其他的客户端,XXX下下了

5. 当有客户端想服务器发送消息的时候,广播给其他的客户端包括自己

编写步骤如下:

1. 编写服务器端的启动程序

package com.baidu.netty.thirdExample;

import com.sun.security.ntlm.Server;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class MyServer {
    public static void main(String[] args) throws Exception{
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup).channel(NioServerSocketChannel.class).childHandler(new MyServerInitializer());
            ChannelFuture future = bootstrap.bind(8899).sync();
            future.channel().closeFuture().sync();
        }finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

2. 编写服务器端的初始化程序

package com.baidu.netty.thirdExample;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class MyServerInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,Delimiters.lineDelimiter()));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyServerHandler());
    }
}

3. 编写服务器端处理程序

package com.baidu.netty.thirdExample;

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 MyServerHandler extends SimpleChannelInboundHandler<String> {
    /**定义一个集合来存储连上来的客户端*/
    private static ChannelGroup group = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

    /**读取客户端的数据,然后广播给其他客户端,如果是自己给自己发消息则显示: 自己对自己说:xxx 如果是别人的消息则显示:XXX对我说:xxx*/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        Channel channel = ctx.channel();
        group.forEach(en -> {
            if (en.equals(channel))
            {
                en.writeAndFlush("自己对自己说:" + msg + "\n");
            }else
            {
                en.writeAndFlush(channel.remoteAddress() +"对我说: " + msg + "\n");
            }
        });
    }
    /**客户端连上服务器的时候通知其他的客户端,XXX连上服务器了*/
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "加入了\n"); //调用集合的写方法会通知到所有在集合中的客户端
        //先调用group的写方法在加入,则可以避免对自己写入
        group.add(channel);
    }

    /**当客户端断开的时候通知其他的客户端XXX失去服务器端的连接了*/
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        //group.remove(channel); //删除某一个客户端,但是没有必要显示的调用,ChannelGroup会自动检查是否在线
        group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "离开了\n");
    }

    /**客户端上线的时候,通知其他客户端,XXX上线了*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "上线了");
    }

    /**当客户端下线的时候,通知其他的客户端,XXX下线了*/
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();
        group.writeAndFlush("【服务器端消息】:" + channel.remoteAddress() + "下线了");
    }

    /**出现异常的时候关闭客户端*/
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

4. 编写客户端启动程序

package com.baidu.netty.thirdExample;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
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 MyClient {
    public static void main(String[] args) throws Exception{
        EventLoopGroup client = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(client).channel(NioSocketChannel.class).handler(new MyClientInitializer());
            ChannelFuture future = bootstrap.connect("localhost",8899);
            Channel channel = future.channel();
            //编写一个死循环来从控制台接收数据然后发送给服务器
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            for (;;)
            {
                channel.writeAndFlush(reader.readLine() + "\r\n"); //该处必须要加上换行符,否则服务器端收不到消息,因为我们使用Delimiters.lineDelimiter()方式进行解码
            }
        }finally {
            client.shutdownGracefully();
        }
    }
}

5. 编写客户端初始化程序

package com.baidu.netty.thirdExample;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import io.netty.handler.codec.Delimiters;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.CharsetUtil;

public class MyClientInitializer extends ChannelInitializer<SocketChannel> {
    @Override
    protected void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE,Delimiters.lineDelimiter()));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new MyClientHandler());
    }
}

6. 编写客户端处理程序

package com.baidu.netty.thirdExample;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class MyClientHandler extends SimpleChannelInboundHandler<String> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
        System.out.println(msg);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}
7. 测试


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值