JAVA Netty实现聊天室+私聊功能的示例代码

功能介绍

使用Netty框架实现聊天室功能,服务器可监控客户端上下限状态,消息转发。同时实现了点对点私聊功能。技术点我都在代码中做了备注,这里不再重复写了。希望能给想学习netty的同学一点参考。

服务器代码

服务器入口代码

package nio.test.netty.groupChat;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
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 io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

/**
 * netty群聊 服务器端
 * @author zhang
 *
 */
public class NettyChatServer {
	private int port;

	public NettyChatServer(int port){
		this.port = port;
	}

	//初始化 netty服务器
	private void init() throws Exception{
		EventLoopGroup boss = new NioEventLoopGroup(1);
		EventLoopGroup work = new NioEventLoopGroup(16);
		try {
			ServerBootstrap boot = new ServerBootstrap();
			boot.group(boss,work);
			boot.channel(NioServerSocketChannel.class);//设置boss selector建立channel使用的对象
			boot.option(ChannelOption.SO_BACKLOG, 128);//boss 等待连接的 队列长度
			boot.childOption(ChannelOption.SO_KEEPALIVE, true); //让客户端保持长期活动状态
			boot.childHandler(new ChannelInitializer<SocketChannel>() {

				@Override
				protected void initChannel(SocketChannel ch) throws Exception {
					//从channel中获取pipeline 并往里边添加Handler
					ChannelPipeline pipeline = ch.pipeline();
					pipeline.addLast("encoder",new StringEncoder());
					pipeline.addLast("decoder",new StringDecoder());
					pipeline.addLast(new ServerMessageHandler());//自定义Handler来处理消息
				}
			});
			System.out.println("服务器开始启动...");
			//绑定端口
			ChannelFuture channelFuture = boot.bind(port).sync();
			channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {

				@Override
				public void operationComplete(Future<? super Void> future)
						throws Exception {
					if(future.isSuccess()){
						System.out.println("服务器正在启动...");
					}
					if(future.isDone()){
						System.out.println("服务器启动成功...OK");
					}

				}
			});
			//监听channel关闭
			channelFuture.channel().closeFuture().sync();
			channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {

				@Override
				public void operationComplete(Future<? super Void> future)
						throws Exception {
					if(future.isCancelled()){
						System.out.println("服务器正在关闭..");
					}
					if(future.isCancellable()){
						System.out.println("服务器已经关闭..OK");
					}

				}
			});

		}finally{
			boss.shutdownGracefully();
			work.shutdownGracefully();
		}
	}
	/**
	 * 启动服务器 main 函数
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		new NettyChatServer(9090).init();

	}

}

服务器端消息处理Handler

package nio.test.netty.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.Date;
import java.util.HashMap;
import java.util.Map;
/**
 * 自定义 服务器端消息处理Handler
 * @author zhang
 *
 */
public class ServerMessageHandler extends SimpleChannelInboundHandler<String>{
	/**
	 * 管理全局的channel
	 * GlobalEventExecutor.INSTANCE 全局事件监听器
	 * 一旦将channel 加入 ChannelGroup 就不要用手动去
	 * 管理channel的连接失效后移除操作,他会自己移除
	 */
	private static ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
	/**
	 * 为了实现私聊功能,这里key存储用户的唯一标识,
	 * 我保存 客户端的端口号
	 * 当然 这个集合也需要自己去维护 用户的上下线 不能像 ChannelGroup那样自己去维护
	 */
	private static Map<String,Channel> all = new HashMap<String,Channel>();

	private SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	/**
	 * 处理收到的消息
	 */
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, String msg)
			throws Exception {
		Channel channel = ctx.channel();
		/**
		 * 这里简单判断 如果内容里边包含#那么就是私聊
		 */
		if(msg.contains("#")){
			String id = msg.split("#")[0];
			String body = msg.split("#")[1];
			Channel userChannel = all.get(id);
			String key = channel.remoteAddress().toString().split(":")[1];
			userChannel.writeAndFlush(sf.format(new Date())+"\n 【用户】 "+key+" 说 : "+body);
			return;
		}

		//判断当前消息是不是自己发送的
		for(Channel c : channels){
			String addr = c.remoteAddress().toString();
			if(channel !=c){
				c.writeAndFlush(sf.format(new Date())+"\n 【用户】 "+addr+" 说 : "+msg);
			}else{
				c.writeAndFlush(sf.format(new Date())+"\n 【自己】 "+addr+" 说 : "+msg);
			}
		}

	}
	/**
	 * 建立连接以后第一个调用的方法
	 */
	@Override
	public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
		Channel channel = ctx.channel();
		String addr = channel.remoteAddress().toString();
		/**
		 * 这里 ChannelGroup 底层封装会遍历给所有的channel发送消息
		 *
		 */
		channels.writeAndFlush(sf.format(new Date())+"\n 【用户】 "+addr+" 加入聊天室 ");
		channels.add(channel);
		String key = channel.remoteAddress().toString().split(":")[1];
		all.put(key, channel);
	}
	/**
	 * channel连接状态就绪以后调用
	 */
	@Override
	public void channelActive(ChannelHandlerContext ctx) throws Exception {
		String addr = ctx.channel().remoteAddress().toString();
		System.out.println(sf.format(new Date())+" \n【用户】 "+addr+" 上线 ");
	}
	/**
	 * channel连接状态断开后触发
	 */
	@Override
	public void channelInactive(ChannelHandlerContext ctx) throws Exception {
		String addr = ctx.channel().remoteAddress().toString();
		System.out.println(sf.format(new Date())+" \n【用户】 "+addr+" 下线 ");
		//下线移除
		String key = ctx.channel().remoteAddress().toString().split(":")[1];
		all.remove(key);
	}
	/**
	 * 连接发生异常时触发
	 */
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
			throws Exception {
		//System.out.println("连接发生异常!");
		ctx.close();
	}
	/**
	 * 断开连接会触发该消息
	 * 同时当前channel 也会自动从ChannelGroup中被移除
	 */
	@Override
	public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
		Channel channel = ctx.channel();
		String addr = channel.remoteAddress().toString();
		/**
		 * 这里 ChannelGroup 底层封装会遍历给所有的channel发送消息
		 *
		 */
		channels.writeAndFlush(sf.format(new Date())+"\n 【用户】 "+addr+" 离开了 ");
		//打印 ChannelGroup中的人数
		System.out.println("当前在线人数是:"+channels.size());
		System.out.println("all:"+all.size());
	}

}

客户端主方法代码

package nio.test.netty.groupChat;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

import java.util.Scanner;

public class NettyChatClient {

	private String ip;

	private int port;

	public NettyChatClient(String ip,int port){
		this.ip = ip;
		this.port = port;
	}
	/**
	 * 初始化客户
	 */
	private void init() throws Exception{
		//创建监听事件的监听器
		EventLoopGroup work = new NioEventLoopGroup();
		try {
			Bootstrap boot = new Bootstrap();
			boot.group(work);
			boot.channel(NioSocketChannel.class);
			boot.handler(new ChannelInitializer<NioSocketChannel>() {

				@Override
				protected void initChannel(NioSocketChannel ch)
						throws Exception {
					ChannelPipeline pipeline = ch.pipeline();
					pipeline.addLast("encoder",new StringEncoder());
					pipeline.addLast("decoder",new StringDecoder());
					pipeline.addLast(new ClientMessageHandler());

				}
			});

			ChannelFuture channelFuture = boot.connect(ip, port).sync();
			channelFuture.addListener(new GenericFutureListener<Future<? super Void>>() {

				@Override
				public void operationComplete(Future<? super Void> future)
						throws Exception {
					if(future.isSuccess()){
						System.out.println("客户端启动中...");
					}
					if(future.isDone()){
						System.out.println("客户端启动成功...OK!");
					}
				}
			});
			System.out.println(channelFuture.channel().localAddress().toString());
			System.out.println("#################################################");
			System.out.println("~~~~~~~~~~~~~~端口号#消息内容~~这样可以给单独一个用户发消息~~~~~~~~~~~~~~~~~~");
			System.out.println("#################################################");

			/**
			 * 这里用控制台输入数据
			 */
			Channel channel = channelFuture.channel();
			//获取channel
			Scanner scanner = new Scanner(System.in);
			while(scanner.hasNextLine()){
				String str = scanner.nextLine();
				channel.writeAndFlush(str+"\n");
			}
			channelFuture.channel().closeFuture().sync();
			scanner.close();
		} finally {
			work.shutdownGracefully();
		}
	}

	/**
	 * 主方法入口
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception{

		new NettyChatClient("127.0.0.1",9090).init();
	}

}

客户端消息处理Handler

package nio.test.netty.groupChat;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
 * 客户点消息处理 Handler
 * @author zhang
 *
 */
public class ClientMessageHandler 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 {
		ctx.close();

	}
}

测试结果

启动了四个客户端 服务器端日志效果如下:

客户端一端日志:

客户端二日志:

客户端三日志:

客户端四日志:

现在在客户端四发送消息:

每个客户端都可以收到消息:

软化关闭客户端客户端三:

服务器日志:

其他客户端日志:

发送私聊消息:

这个客户端收不到消息

到此这篇关于JAVA Netty实现聊天室+私聊功能的示例代码的文章就介绍到这了,更多相关JAVA Netty聊天室内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java基于netty NIO的简单聊天室的实现

    一.为何要使用netty开发 由于之前已经用Java中的socket写过一版简单的聊天室,这里就不再对聊天室的具体架构进行细致的介绍了,主要关注于使用netty框架重构后带来的改变.对聊天室不了解的同学可以先看下我的博客(<JAVA简单聊天室的实现>) 本篇博客所使用的netty版本为4.1.36,完整工程已上传到Github(https://github.com/Alexlingl/Chatroom),其中lib文件夹下有相应的netty jar包和source包,自行导入即可. 1.为何要

  • JAVA Netty实现聊天室+私聊功能的示例代码

    功能介绍 使用Netty框架实现聊天室功能,服务器可监控客户端上下限状态,消息转发.同时实现了点对点私聊功能.技术点我都在代码中做了备注,这里不再重复写了.希望能给想学习netty的同学一点参考. 服务器代码 服务器入口代码 package nio.test.netty.groupChat; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.chann

  • Java spring boot 实现支付宝支付功能的示例代码

    一.准备工作: 1.登陆支付宝开发者中心,申请一个开发者账号. 地址:https://openhome.alipay.com/ 2.进入研发服务: 3.点击链接进入工具下载页面: 4.点击下载对应版本的RSA公钥生成器: 5.生成公钥密钥(记录你的应用私钥): 6.在支付宝配置公钥(点击保存): 二.搭建demo 1.引入jia包: <dependency> <groupId>com.alipay.sdk</groupId> <artifactId>alip

  • java后台实现支付宝对账功能的示例代码

    完成支付宝支付.查询的接口之后,我们应该还需要定时与支付宝进行对账,以确保商户系统的订单信息是正确的,想知道支付宝支付.查询接口实现过程的亲们,可移步到上一篇有详细过程. 现在我们来讲一下支付宝对账的功能,关于与支付宝交互的关键代码,其实支付宝的API已经讲得很清楚,如果亲们想直接看支付宝API,点击 支付宝对账API,当然我在文章也会进行说明解释. 实现支付宝自动对账功能 先看一下商户系统和支付宝的大概交互过程: 所谓对账,其实就是调用支付宝查询接口,跟上一篇的查询接口不同的是,上一篇的查询接

  • Java实现局域网聊天室功能(私聊、群聊)

    本文实例为大家分享了Java实现局域网聊天室功能的具体代码,供大家参考,具体内容如下 Server 服务端 import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; import java.net.Socket;   /**  * 服务端  */ public class Server {       private static final int SERVER_PORT=8080

  • Java实现NIO聊天室的示例代码(群聊+私聊)

    功能介绍 功能:群聊+私发+上线提醒+下线提醒+查询在线用户 文件 Utils 需要用maven导入下面两个包 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> </dependency> <dependency> <group

  • Java NIO实现聊天室功能

    本文实例为大家分享了Java NIO实现聊天室功能的具体代码,供大家参考,具体内容如下 代码里面已经包含了必要的注释,这里不详述了.实现了基本的聊天室功能. 常量类: public class Constant { public static final int serverPort = 44444; } 服务端: package server; import java.io.IOException; import java.net.InetSocketAddress; import java.

  • Java聊天室之实现聊天室客户端功能

    目录 一.题目描述 二.解题思路 三.代码详解 一.题目描述 题目实现:实现聊天室客户端.运行程序,用户登录服务器后,可以从用户列表中选择单个用户进行聊天,也可以选择多个用户进行聊天. 二.解题思路 创建一个服务类:ChatClientFrame,继承JFrame类.用于进行用户登录.发送聊天信息和显示聊天信息,在该类中完成窗体界面的设计. 定义createClientSocket)方法,用于创建套接字对象.输出流对象以及启动线程对象对服务器转发的信息进行处理. 定义内部线程类ClientThr

  • Java Socket实现聊天室功能

    本文实例为大家分享了Java Socket实现聊天室的具体代码,供大家参考,具体内容如下 1 创建登录判断类UserLogin import java.util.HashSet; import java.util.Set; public class UserLogin {     public static boolean login(String username) {         Set<String> set = initUser();         // set中含有该usern

  • Java实现多线程聊天室

    本文实例为大家分享了Java实现多线程聊天室的具体代码,供大家参考,具体内容如下 用多线程来实现,功能会比单线程聊天室更加齐全,也更人性化一点. 多线程版本的聊天室 1. 功能分析: 实现用户注册,上线,下线 实现群聊和私聊 统计当前在线人数 2. 服务端实现 1.维护所有的在线用户 2.注册功能:客户端名称,添加到服务器的客户端集合里 3.群聊功能:客户端发送消息,所有的客户端都能接收到 4.私聊功能:客户端与指定客户端进发送和接收消息 5.退出功能: 从服务器客户端集合中移除客户端 3. 客

  • java实现简单聊天室单人版

    本文实例为大家分享了java实现简单聊天室的具体代码,供大家参考,具体内容如下 先整理下思路: 1.创建一个通信服务端,传入端口号和相关的流后等待客户端连接,并初始化图形界面. 2.创建一个JFrame,用于写出聊天的界面,这里界面名称由其他类传入. 3.把客户端创建的方法写进JFrame(当然这里很粗糙的方法) 4.设置按钮的监听事件,发送消息和离线功能 首先创建一个服务端的类 import java.io.IOException; import java.net.ServerSocket;

随机推荐