最近自己搭建了一个在线的聊天室,利用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)登录后的聊天界面:
大家也可以自己登录看看,不过需要先注册后才可以登录。
网站地址: 炫彩聊天室



