1、功能介绍
和之前的NIO实现群聊机制一样,这里使用Netty实现:
- 1、当有新用户上线时,通知其他用户上线,下线同理
- 2、用户x发送一条消息,服务器端转发给其他所有在线服务器
2、代码结构

3、GroupChatServer
package groupchat;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import java.beans.Encoder;
public class GroupChatServer {
//监听端口
private int port;
public GroupChatServer(int port){
this.port = port;
}
//编写run方法,处理客户端的请求
public void run() throws Exception{
//创建两个线程组
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
//其中NioEventLoppGroup默认八个线程
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
//创建ServerBootstrap,服务器启动引导类
ServerBootstrap b = new ServerBootstrap();
//设置ServerBootstrap相关参数
b.group(bossGroup, workerGroup)//roup函数设置两个EventLoop
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 128)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.childHandler(new ChannelInitializer<SocketChannel>() {//指定相关handler
@Override
protected void initChannel(SocketChannel ch) throws Exception {
//获取pipeline
ChannelPipeline pipeline = ch.pipeline();
//向pipeline中加入解码器
pipeline.addLast("decoder", new StringDecoder());
//向pipeline中加入编码器
pipeline.addLast("encoder", new StringDecoder());
//加入自己的业务处理handler,需要自己写!关键部分
pipeline.addLast(new GroupChatServerHandler());
}
});
System.out.println("Netty服务器已启动");
ChannelFuture channelFuture = b.bind(port).sync();
//监听关闭事件
channelFuture.channel().closeFuture().sync();
}finally {
//关闭
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
//主函数,也需要抛出异常
public static void main(String[] args) throws Exception{
//启动一个服务器
new GroupChatServer(7000);
}
}
4、GroupChatServerHandler
package groupchat;
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;
import java.text.SimpleDateFormat;
import java.util.SimpleTimeZone;
public class GroupChatServerHandler extends SimpleChannelInboundHandler<String> {
//定义一个channelle组,管理所有的channel
//GlobalEventExecutor.INSTANCE是全局的时间执行器,是一个单例,帮助执行channelGroup
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
//输出接收到消息的时间用
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//当连接建立之后,第一个被执行,将当前的channel加入到channelGroup
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception{
Channel channel = ctx.channel();
//将该客户端加入聊天的消息 发送给其他的在线客户端
//writeAndFlush方法会自动遍历所有的channel,并发送括号内消息
//最后加入当前消息发送的时间
channelGroup.writeAndFlush("客户端" + channel.remoteAddress() + "加入聊天\n"
+ sdf.format(new java.util.Date()) + "\n");
//channelGroup中加入该channel
channelGroup.add(channel);
}
//断开连接,触发该通知给当前在线的客户
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
Channel channel = ctx.channel();
channelGroup.writeAndFlush("客户端" + channel.remoteAddress() + "离开聊天了\n");
System.out.println("测试用:"+"channelGroup_size=" + channelGroup.size() );
}
//channel出于活动状态,提示xxx已上线
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception{
System.out.println(ctx.channel().remoteAddress() + "上线");
}
//提示xxx离线
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception{
System.out.println(ctx.channel().remoteAddress() + "离线了");
}
//读取数据并转发数据给当前在线的其他客户端
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String s) throws Exception {
//获取当前channel
//给自己发送的消息是不一样的
final Channel channel = channelHandlerContext.channel();
//根据不同的情况,需要会送不同的消息
channelGroup.forEach(ch -> {
if(channel != ch)
ch.writeAndFlush("[客户]" + channel.remoteAddress() + "发送了消息:" + s + "\n");
else
ch.writeAndFlush("[自己]" + "发送了消息:" + s + "\n");
});
}
//发生异常时
@Override
public void exceptionCaught(ChannelHandlerContext var1, Throwable var2) throws Exception{
//关闭通道
var1.close();
}
}
5、GroupChatClient
package groupchat;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.pool.ChannelPool;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import javax.naming.event.EventContext;
import java.awt.*;
import java.util.Scanner;
public class GroupChatClient {
//主机地址
private final String host;
//端口
private final int port;
public GroupChatClient(String host, int port) {
this.host = host;
this.port = port;
}
public void run() throws Exception{
EventLoopGroup eventExecutors = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap()
.group(eventExecutors)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception{
//得到pipeline
ChannelPipeline pipeline = ch.pipeline();
//加入相关handler
pipeline.addLast("decoder",new StringDecoder());
pipeline.addLast("encoder",new StringEncoder());
//加入自定义handler
pipeline.addLast(new GroupChatServerHandler());
}
});
ChannelFuture channelFuture = bootstrap.connect(host,port).sync();
//得到当前channel
Channel channel = channelFuture.channel();
System.out.println("......" + channel.localAddress() + "......");
//客户端需要输入信息,使用Scanner扫描器
Scanner scanner = new Scanner(System.in);
while(scanner.hasNextLine()){
String msg = scanner.nextLine();
//将消息通过channel发送给服务器端
channel.writeAndFlush(msg + "\r\n");
}
}finally {
eventExecutors.shutdownGracefully();
}
}
//客户端主函数
public static void main(String[] args) throws Exception {
new GroupChatClient("127.0.0.1",7000).run();
}
}
6、GroupChatClientHandler
package groupchat;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class GroupChatClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, String msg) throws Exception {
System.out.println(msg.trim());
}
}
7、测试方法
- 1)新建一个Maven工程,导入netty依赖
- 2)按照2结构搭建代码
- 3)运行服务器端GroupChatServer
- 4)运行一个客户端GroupChatClient,再切换端口运行新的客户端,进行测试,收发消息/通知与否
8、遇到的问题
遇到Exception异常Exception in thread "main" io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection refused: no further information: /127.0.0.1:53853
应该是IP或者端口设置错了,尝试设置一下本地计算机
2万+



被折叠的 条评论
为什么被折叠?



