用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<WebSocketFrame>{
	
	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;
		}
	}
	/**
	 * 发送错误消息给客户端
	 * @param ctx
	 * @param msg
	 */
	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)登录后的聊天界面:
在这里插入图片描述
在这里插入图片描述
大家也可以自己登录看看,不过需要先注册后才可以登录。
测试账号:116 密码:123456
网站地址: 炫彩聊天室

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜空下的星

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值