Netty

85 阅读2分钟

1.Netty搭建WS服务端

/**
 * Netty服务端
 */
@Component
@Slf4j
public class NettyServer {
​
    @Autowired
    private ServerHandler serverHandler;
​
    public static ServerSocketChannel serverSocketChannel;
​
    public void start(int port) throws Exception {
        // 连接处理group
        EventLoopGroup boss = new NioEventLoopGroup();
        // 事件处理group
        EventLoopGroup worker = new NioEventLoopGroup();
        //1.创建ServerBootStrap实例
        ServerBootstrap bootstrap = new ServerBootstrap();
        // 绑定处理group
        //2.设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel
        bootstrap.group(boss, worker)
                //3.设置并绑定服务端的channel
                .channel(NioServerSocketChannel.class)
                // 保持连接数
                .option(ChannelOption.SO_BACKLOG, 1024)
                // 保持连接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                // 处理新连接
                // 设置了客户端连接socket属性。
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        // 增加任务处理
                        ChannelPipeline pipeline = sc.pipeline();
                        pipeline.addLast(new HttpServerCodec());
                        //支持写大数据流
                        pipeline.addLast(new ChunkedWriteHandler());
                        //http聚合器
                        pipeline.addLast(new HttpObjectAggregator(1024 * 62));
                        //websocket支持,设置路由
                        pipeline.addLast(new WebSocketServerProtocolHandler("/netty/connect"));
                        pipeline.addLast(                 // 自定义的处理器
                                serverHandler);
                    }
                });
​
        // 绑定端口,同步等待成功
        ChannelFuture future;
        try {
            log.info("netty服务器在[{}]端口启动监听", port);
            //真正让netty跑起来的重点
            future = bootstrap.bind(port).sync();
            if (future.isSuccess()) {
                serverSocketChannel = (ServerSocketChannel) future.channel();
                log.info("netty服务开启成功");
            } else {
                log.info("netty服务开启失败");
            }
            // 等待服务监听端口关闭,就是由于这里会将线程阻塞,导致无法发送信息,所以我这里开了线程
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 优雅地退出,释放线程池资源
            boss.shutdownGracefully();
            worker.shutdownGracefully();
        }
    }
}
​
/**
 * Netty服务端处理类
 */@Component
@ChannelHandler.Sharable
@Slf4j
public class ServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
​
    /**
     * 在与客户端的连接已经建立之后将被调用
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //获取每个用户端连接的ip
        InetSocketAddress ipSocket = (InetSocketAddress) ctx.channel().remoteAddress();
        String clientIp = ipSocket.getAddress().getHostAddress();
        // 获取连接的id
        String channleId = ctx.channel().id().toString();
        log.info("有新的netty客户端连接:id is {},ip is {}", channleId, clientIp);
    }
​
​
    /**
     * 客户端与服务端断开连接时调用
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (ChannelMapServer.getChannelHashMap() != null && ChannelMapServer.getChannelHashMap().size() > 0) {
            Iterator<Map.Entry<String, Channel>> iterator = ChannelMapServer.getChannelHashMap().entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Channel> next = iterator.next();
                String key = next.getKey();
                Channel channel = next.getValue();
                if (channel.id().toString().equals(ctx.channel().id().toString())) {
                    log.info("netty客户端与服务端连接关闭...appKey is {}", key);
                    ChannelMapServer.removeChannelByName(key);
                }
            }
        }
    }
​
    /**
     * 在处理过程中引发异常时被调用
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
        log.error("异常信息: {}", cause.getMessage());
    }
​
​
    /**
     * 数据处理
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame textWebSocketFrame) {
        String json = textWebSocketFrame.text();
        String msg = null;
​
        if (StringUtils.isNotEmpty(json)) {
            log.info("netty服务器收到客户端消息 {}", json);
​
            // heartbeat 心跳
            if (json.contains(GloabalConstants.HEART_BEAT)) {
                NettyHeartBo nettyHeartBo = JSONUtil.toBean(json, NettyHeartBo.class);
                Channel channelByName = ChannelMapServer.getChannelByName(nettyHeartBo.getAppKey());
                msg = channelByName == null ? BusinessErrorEnum.HEART_FAIL.getMsgEn() : BusinessErrorEnum.HEART_SUCC.getMsgEn();
            } else {
                // AK/SK 注册
                NettyConfigBo nettyConfigBo = JSONUtil.toBean(json, NettyConfigBo.class);
                boolean flag = checkAppKey(nettyConfigBo);
                if (flag) {
                    // 将通道加入ChannelMap
//                    ChannelMapServer.addChannel(nettyConfigBo.getAppKey(), ctx.channel());
                    ChannelMapServer.addChannel(IdUtil.randomUUID(), ctx.channel());
                }
                msg = flag ? BusinessErrorEnum.CONNECT_SUCC.getMsgEn() : BusinessErrorEnum.CONNECT_FAIL.getMsgEn();
            }
​
        }
        // 返回消息
        ctx.channel().writeAndFlush(new TextWebSocketFrame(msg));
    }
​
    /**
     * 校验ak,sk
     *
     * @return
     */
    private boolean checkAppKey(NettyConfigBo nettyConfigBo) {
        return true;
    }
}
​
/**
 * 通道列表
 */
public class ChannelMapServer {
​
​
    public static ConcurrentHashMap<String, Channel> channelHashMap=null;
​
    /**
     *  获取ConcurrentHashMap
     */
    public static ConcurrentHashMap<String, Channel> getChannelHashMap() {
        return channelHashMap;
    }
​
    /**
     *  获取指定name的channel
     */
    public static Channel getChannelByName(String name){
        if(channelHashMap==null||channelHashMap.isEmpty()){
            return null;
        }
        return channelHashMap.get(name);
    }
​
    /**
     *  将通道中的消息推送到每一个客户端
     */
    public static boolean pushNewsToAllClient(String obj){
        if(channelHashMap==null||channelHashMap.isEmpty()){
            return false;
        }
        for(String name: channelHashMap.keySet()) {
            Channel channel = channelHashMap.get(name);
            channel.writeAndFlush(new TextWebSocketFrame(obj));
        }
        return true;
    }
​
    /**
     *  将channel和对应的name添加到ConcurrentHashMap
     */
    public static void addChannel(String name,Channel channel){
        if(channelHashMap==null){
            channelHashMap=new ConcurrentHashMap<>(128);
        }
        channelHashMap.put(name,channel);
    }
​
    /**
     *  移除掉name对应的channel
     */
    public static void  removeChannelByName(String name){
        if(channelHashMap.containsKey(name)){
            channelHashMap.remove(name);
        }
    }
}

2.WS客户端

/**
 *  客户端启动类
 */
public class main {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 1000; i++) {
                    try {
                        BaseWebsocketClient myClient = new BaseWebsocketClient(new URI("ws://127.0.0.1:8000/netty/connect"));
                        myClient.connect();
                        while (!myClient.getReadyState().equals(ReadyState.OPEN)) {
                            System.out.println("连接中....");
                            Thread.sleep(10);
                        }
                        myClient.send("");
                        // 连接成功往websocket服务端发送数据
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}
​
/**
 * websocket客户端监听类
 */
@Slf4j
public class BaseWebsocketClient extends WebSocketClient {
​
    public BaseWebsocketClient(URI serverUri) {
        super(serverUri);
    }
​
    @Override
    public void onOpen(ServerHandshake serverHandshake) {
        log.info(">>>>>>>>>>>websocket open");
​
    }
​
    @Override
    public void onMessage(String s) {
        System.out.println("线程:【" + Thread.currentThread().getName() + "】 收到消息:" + s);
    }
​
    @Override
    public void onClose(int i, String s, boolean b) {
        log.info(">>>>>>>>>>>websocket close");
    }
​
    @Override
    public void onError(Exception e) {
        log.error(">>>>>>>>>websocket error {}", e);
    }
​
​
}
​