栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

用springboot+netty搭建在线web聊天室

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

用springboot+netty搭建在线web聊天室

最近自己搭建了一个在线的聊天室,利用netty技术开发,实现在线聊天以及群聊功能,包括好友添加等相关功能,目前还在更新中。

1.我是通过springboot+netty实现,通过使用netty4实现,运用这种方式就可以帮助我们实现websocket通信,其中核心功能如下:

public class NettyWebsocketServer implements Runnable{
	@Autowired
	NettyWebsocketChildHandlerInitializer childHandlerInitializer; 
	private static final int PORT=8088;
	private static final int RCV_ALLOCTOR_SIZE=592048;
	
	EventLoopGroup bossGroup=new NioEventLoopGroup();
	EventLoopGroup workGroup=new NioEventLoopGroup();
	ServerBootstrap serverBootstrap=new ServerBootstrap();
	private ChannelFuture channelFuture;
	public NettyWebsocketServer() {
		
	}
	@Override
	public void run() {
		build();	
	}
	
	public void build() {
		long startTm=System.currentTimeMillis();
		try {
			serverBootstrap.group(bossGroup, workGroup)
			.channel(NioServerSocketChannel.class)
			.option(ChannelOption.SO_BACKLOG, 1024) //Tcp参数,握手字符长度
			.childOption(ChannelOption.TCP_NODELAY, true) //设置TCP NO_DELAY 算法 尽量减少发送大文件包
			.childOption(ChannelOption.SO_KEEPALIVE, true) // 针对子线程的配置 保持长连接
			.childOption(ChannelOption.RCVBUF_ALLOCATOR, new FixedRecvByteBufAllocator(RCV_ALLOCTOR_SIZE)) // 配置固定长度接收缓存内存分配
			.childHandler(childHandlerInitializer);
			channelFuture = serverBootstrap.bind(PORT).sync();
			// 监听关闭
			channelFuture.channel().closeFuture().sync();
			long endTm=System.currentTimeMillis();
			log.info("服务器启动完成,耗时:[{}]毫秒,已在端口[{}]阻塞",endTm-startTm,PORT);
		}catch (Exception e) {
			bossGroup.shutdownGracefully();
			workGroup.shutdownGracefully();
		}
	}
	public void close() {
		channelFuture.channel().close();
		Future bossFuture=bossGroup.shutdownGracefully();
		Future workFuture=workGroup.shutdownGracefully();
		try {
			bossFuture.await();
			workFuture.await();
		}catch (Exception e) {
			// TODO: handle exception
		}
	}
	public ChannelHandler getChannelHandler() {
		return childHandlerInitializer;
	}
}

这个类是websocket的服务端。

2.接下来还需要websocket处理器。

@Component
@Sharable
public class NettyWebSocketServerHandler extends SimpleChannelInboundHandler{
	
	private final ChatService chatService;
	public NettyWebSocketServerHandler(ChatService chatService) {
		this.chatService=chatService;
	}
	@Override
	protected void channelRead0(ChannelHandlerContext ctx, WebSocketframe frame) throws Exception {
		doHandler(ctx, frame);
	}
	private void doHandler(ChannelHandlerContext ctx, WebSocketframe frame) {
		if(frame instanceof CloseWebSocketframe) {
			try {	
				WebSocketServerHandshaker handshaker=Constant.webSocketHandshakerMap.get(ctx.channel().id().asLongText());
				//WebSocketServerHandshaker handshaker=JSONObject.parseObject(jedis.get(ctx.channel().id().asLongText()), WebSocketServerHandshaker.class);
				if(handshaker==null) {
					sendErrorMessage(ctx, "该用户已离线或不存在该连接");
				}else {
					handshaker.close(ctx.channel(), ((CloseWebSocketframe)frame).retain()); //增加消息的引用计数
				}
			}finally {
				
			}	
			return;
		}
		//ping请求
		if(frame instanceof PingWebSocketframe) {
			ctx.channel().writeAndFlush(new PongWebSocketframe(frame.content().retain()));
			return ;
		}
		if(!(frame instanceof TextWebSocketframe)) {
			sendErrorMessage(ctx, "不支持二进制文件");
			return ;
		}
		String request=((TextWebSocketframe) frame).text();
		JSonObject params=null;
		try {
			params=JSONObject.parseObject(request);
			log.info("收到服务器消息:[{}]",params);
		}catch (Exception e) {
			sendErrorMessage(ctx, "转换出错");
			log.info("参数转换错误");
		}
		if(null==params) {
			sendErrorMessage(ctx, "参数为空");
			log.warn("参数为空");
			return ;
		}
		String msgType=(String)params.get("type");
		switch (msgType) {
			case "REGISTER":
				chatService.register(params, ctx);
				break;
			case "SINGLE_SENDING": //发消息个单个人
				chatService.sendOne(params, ctx);
				break;
			case "GROUP_SENDING": //群发
				chatService.sendGroup(params, ctx);
				break;
			case "FILE_MSG_SINGLE_SENDING": //发文件给单个人
				break;
			case "FILE_MSG_GROUP_SENDING": //群发文件
			default:
				break;
		}
	}
	
	public void sendErrorMessage(ChannelHandlerContext ctx,String msg) {
		String result=new ApiResult().failed(msg).toString();
		ctx.channel().writeAndFlush(new TextWebSocketframe(result)); //通知所有已经连接的WebSocket客户端新的客户端

	}
	@Override
	public void channelInactive(ChannelHandlerContext ctx) {
		if(ctx!=null) {
			//Constant.webSocketHandshakerMap.remove(Constant.onlineUserMap.get(ctx.channel().id().asLongText()));
			//Constant.onlineUserMap.remove(String.valueOf(user.getUserID()));   
			//jedis.del(context.channel().id().asLongText());
			//jedis.del(String.valueOf(user.getUserID())+"_online");
		}
		chatService.remove(ctx);
		//String userChannel=jedis.get(String.valueOf(user.getUserID())+"_online");
		//ChannelHandlerContext context=JSONObject.parseObject(userChannel, ChannelHandlerContext.class);
		
		log.info("已离线");
	}
	
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
		cause.printStackTrace();
        ctx.close();
    }
}

当然还有其他一些类,http请求处理器,需要将http协议升级为websocket协议,这里就不一一写了。

3.下面是我实现后的界面展示:
1)登录页

2)登录后的聊天界面:


大家也可以自己登录看看,不过需要先注册后才可以登录。

网站地址: 炫彩聊天室

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/785320.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号