<p align="center">
    <a target="_blank" href="https://search.maven.org/search?q=wu-lazy-cloud-network%20wu-lazy-cloud-network">
        <img src="https://img.shields.io/nexus/s/top.wu2020/wu-lazy-cloud-network?server=https%3A%2F%2Foss.sonatype.org&style=flat&logo=log" alt="Maven" />
    </a>
    <a target="_blank" href="https://search.maven.org/search?q=wu-lazy-cloud-network%20wu-lazy-cloud-network">
        <img src="https://img.shields.io/maven-central/v/top.wu2020/wu-lazy-cloud-network" alt="Maven" />
    </a>
    <a target="_blank" href="https://www.apache.org/licenses/LICENSE-2.0.txt">
		<img src="https://img.shields.io/:license-Apache2-blue.svg" alt="Apache 2" />
	</a>
    <a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html">
		<img src="https://img.shields.io/badge/JDK-11-green.svg" alt="jdk-11" />
	</a>
    <a target="_blank" href="https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html">
		<img src="https://img.shields.io/badge/JDK-17-green.svg" alt="jdk-17" />
	</a>
    <br />
        <img src="https://img.shields.io/badge/SpringBoot-v3.x-blue">
    <br />
    <a target="_blank" href='https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network'>
		<img src='https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network/badge/star.svg' alt='Gitee star'/>
	</a>
    <a target="_blank" href='https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network'>
		<img src='https://img.shields.io/github/stars/wujiawei1207537021/wu-lazy-cloud-network?style=flat&logo=github' alt='GitHub star'/>
	</a>
    <a target="_blank" href='https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network'>
		<img src='https://img.shields.io/github/forks/wujiawei1207537021/wu-lazy-cloud-network?style=flat&logo=github' alt='GitHub star'/>
	</a>

</p>

#### 项目介绍

wu-lazy-cloud-network
是一款基于（[wu-framework-parent](https://gitee.com/wujiawei1207537021/wu-framework-parent)）孵化出的项目，内部使用Lazy
ORM操作数据库，使用环境JDK17 Spring Boot 3.0.2。
主要功能：
- 服务端渗透客户端（网络穿透），对于没有公网IP的服务进行公网IP映射
- ![NetworkPermeateServerPermeateClient.png](NetworkPermeateServerPermeateClient.png)
- 服务端渗透服务端----本地同局域网内端口映射
- ![NetworkPermeateServerPermeateServer.png](NetworkPermeateServerPermeateServer.png)
- 客户端渗透服务端----本地端口映射到另一个服务端中的局域网端口
- ![NetworkPermeateClientPermeateServer.png](NetworkPermeateClientPermeateServer.png)
- 客户端渗透客户端----本地端口映射到另一个局域网端口
- ![NetworkPermeateClientPermeateClient.png](NetworkPermeateClientPermeateClient.png)


[UI](https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network-server-ui)

#### 项目地址

[Gitee](https://gitee.com/wujiawei1207537021/wu-lazy-cloud-network)

[GitHub](https://github.com/wujiawei1207537021/wu-lazy-cloud-network)

#### 架构图

![architecture.png](architecture.png)

#### 实现原理

##### 服务端创建socket服务端绑定本地端口（用于客户端连接）

```java
package org.framework.lazy.cloud.network.heartbeat.server.netty.socket;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import filter.netty.org.framework.lazy.cloud.network.heartbeat.server.NettyServerFilter;

public class NettyOnCloudNettyServerSocket {
    private final EventLoopGroup bossGroup = new NioEventLoopGroup();
    private final EventLoopGroup workerGroup = new NioEventLoopGroup();
    private final NettyServerFilter nettyServerFilter;// 通道业务处理
    private ChannelFuture channelFuture;

    public NettyOnCloudNettyServerSocket(NettyServerFilter nettyServerFilter) {
        this.nettyServerFilter = nettyServerFilter;
    }

    /**
     * 启动服务端
     *
     * @throws Exception
     */
    public void startServer(int serverPort) throws Exception {
        try {

            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    // 给服务端channel设置属性
                    .option(ChannelOption.SO_BACKLOG, 128)

                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    .childHandler(nettyServerFilter)
            ;
            channelFuture = b.bind(serverPort).sync();

            channelFuture.addListener((ChannelFutureListener) channelFuture -> {
                // 服务器已启动
            });
            channelFuture.channel().closeFuture().sync();
        } finally {
            shutdown();
            // 服务器已关闭
        }
    }

    public void shutdown() {
        if (channelFuture != null) {
            channelFuture.channel().close().syncUninterruptibly();
        }
        if ((bossGroup != null) && (!bossGroup.isShutdown())) {
            bossGroup.shutdownGracefully();
        }
        if ((workerGroup != null) && (!workerGroup.isShutdown())) {
            workerGroup.shutdownGracefully();
        }
    }
}
```

##### 客户端通过class NettyClientSocket 连接服务端

```java
package org.framework.lazy.cloud.network.heartbeat.client.netty.socket;


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.framework.lazy.cloud.network.heartbeat.client.application.ClientChangeEvent;
import filter.netty.org.framework.lazy.cloud.heartbeat.client.NettyClientFilter;
import org.framework.lazy.cloud.network.heartbeat.common.MessageType;
import org.framework.lazy.cloud.network.heartbeat.common.NettyProxyMsg;
import adapter.org.framework.lazy.cloud.network.heartbeat.common.ChannelTypeAdapter;
import advanced.org.framework.lazy.cloud.network.heartbeat.common.HandleChannelTypeAdvanced;
import utils.org.framework.lazy.cloud.network.heartbeat.common.ChannelAttributeKeyUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 客户端连接服务端
 */
@Slf4j
public class NettyClientSocket {
    private static final EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
    /**
     * 服务端host
     */
    private final String inetHost;
    /**
     * 服务端端口
     */
    private final int inetPort;
    /**
     * 当前客户端id
     */
    @Getter
    private final String clientId;
    /**
     * nacos配置信息处理应用
     */
    @Getter
    private final ClientNettyConfigApplication clientChangeEvent;
    private final List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList; // 处理服务端发送过来的数据类型

    public NettyClientSocket(String inetHost, int inetPort, String clientId, ClientNettyConfigApplication clientChangeEvent, List<HandleChannelTypeAdvanced> handleChannelTypeAdvancedList) {
        this.inetHost = inetHost;
        this.inetPort = inetPort;
        this.clientId = clientId;
        this.clientChangeEvent = clientChangeEvent;
        this.handleChannelTypeAdvancedList = handleChannelTypeAdvancedList;
    }

    public void newConnect2Server() throws InterruptedException {
        newConnect2Server(inetHost, inetPort, clientId, clientChangeEvent);
    }

    protected void newConnect2Server(String inetHost, int inetPort, String clientId, ClientNettyConfigApplication clientChangeEvent) throws InterruptedException {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .handler(new NettyClientFilter(new ChannelTypeAdapter(handleChannelTypeAdvancedList), this))
        ;

        log.info("连接服务端IP:{},连接服务端端口:{}", inetHost, inetPort);
        ChannelFuture future = bootstrap.connect(inetHost, inetPort);
        Channel channel = future.channel();

        log.info("使用的客户端ID:" + clientId);
        future.addListener((ChannelFutureListener) futureListener -> {
            if (futureListener.isSuccess()) {

                log.info("连接服务端成功");
                // 告诉服务端这条连接是client的连接
                NettyProxyMsg nettyMsg = new NettyProxyMsg();
                nettyMsg.setType(MessageType.TCP_REPORT_CLIENT_CONNECT_SUCCESS);
                nettyMsg.setClientId(clientId);
                nettyMsg.setData((clientId).getBytes());
                ChannelAttributeKeyUtils.buildClientId(channel, clientId);
                channel.writeAndFlush(nettyMsg);
                // 在线
                clientChangeEvent.clientOnLine(clientId);
            } else {
                log.info("每隔2s重连....");
                // 离线
                clientChangeEvent.clientOffLine(clientId);
                futureListener.channel().eventLoop().schedule(() -> {
                    try {
                        newConnect2Server(inetHost, inetPort, clientId, clientChangeEvent);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }, 2, TimeUnit.SECONDS);
            }
        });
    }

    /**
     * 关闭连接
     */

    public void shutdown() {
        if ((eventLoopGroup != null) && (!eventLoopGroup.isShutdown())) {
            eventLoopGroup.shutdownGracefully();
        }
    }

}
```

##### 通过客户端与服务端建立的连接进行访客端口绑定

    上述连接会形成一个channel，我们称之为通道（本文中简单叫**心跳通道**）
    第一步 页面GUI进行新增访客端口而后将访客端口与客户端绑定（如果客户端已经启动，使用页面客户端下线触发第二步）
    第二步 客户端与访客端口绑定后使用**心跳通道** 发送客户端告诉客户端，你帮我绑定你本地真实端口
    第三步 访客访问，访客通过访客端口访问数据，此时访客通道打开截取访客发送的数据，然后将数据发送给客户真实通道，数据返回后再返回给访客通道

### 功能

    1.内网穿透
    2.服务端自主下发数据到客户端
    3.流量监控

### 项目结构

| 模块                                                                                                         | 版本                   | 描述                           |
|------------------------------------------------------------------------------------------------------------|----------------------|------------------------------| 
| [wu-lazy-cloud-heartbeat-common](wu-lazy-cloud-heartbeat-common)                                           | 1.3.0-JDK17-SNAPSHOT | 内网穿透公共模块（声明接口、枚举、常量、适配器、解析器） |
| [wu-lazy-cloud-heartbeat-client](wu-lazy-cloud-heartbeat-client)                                           | 1.3.0-JDK17-SNAPSHOT | 客户端（支持二次开发）                  |
| [wu-lazy-cloud-heartbeat-server](wu-lazy-cloud-heartbeat-server)                                           | 1.3.0-JDK17-SNAPSHOT | 服务端（支持二次开发）                  |
| [wu-lazy-cloud-heartbeat-client-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-server-start) | 1.3.0-JDK17-SNAPSHOT | 客户端样例                        |
| [wu-lazy-cloud-heartbeat-server-start](wu-lazy-cloud-heartbeat-start/wu-lazy-cloud-heartbeat-client-start) | 1.3.0-JDK17-SNAPSHOT | 服务端样例                        |

### 使用技术

| 框架                                                                                                                                                                          | 版本                   | 描述           |
|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|----------------------|--------------| 
| spring-boot                                                                                                                                                                 | 3.0.7                | springboot框架 |
| [wu-framework-web](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-framework-web)                                                                   | 1.3.0-JDK17-SNAPSHOT | web容器        |
| [Lazy -ORM](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-inner-intergration/wu-database-parent)                                                  | 1.3.0-JDK17-SNAPSHOT | ORM          |
| mysql-connector-j                                                                                                                                                           | 8.0.33               | mysql驱动      |
| [wu-authorization-server-platform-starter](https://gitee.com/wujiawei1207537021/wu-framework-parent/tree/master/wu-smart-platform/wu-authorization-server-platform-starter) | 1.3.0-JDK17-SNAPSHOT | 用户授权体系       |

### 使用环境

    IDEA
    Mac、Windows
    JAVA >=13
    MAVEN

### 启动

    docker启动
        
        docker run  -d -it -p 18080:18080 --name wu-lazy-cloud-heartbeat-server registry.cn-hangzhou.aliyuncs.com/wu-lazy/wu-lazy-cloud-heartbeat-server:1.3.0-JDK17-SNAPSHOT
        
        http://127.0.0.1:18080/swagger-ui/index.html

    源码启动

#### 页面操作

启动项目后打开服务端界面
![url_info.png](url_info.png)

默认账号密码：admin/admin
![login.png](login.png)

初始化项目
![init_menu.png](init_menu.png)
添加角色
![init_role.png](init_role.png)
为用户授权
![img.png](authRoe2User.png)

刷新页面
![img.png](cloud_client.png)

客户端管理（客户端会自动注册）
![img.png](cloud_client.png)

## 服务端渗透
- 服务端口池管理（服务器端需要开放的端口）
  ![server_permeate_port_pool.png](server_permeate_port_pool.png)

- 服务端渗透客户端(内网穿透)（修改后者新增需要映射的客户端）
![server_permeate_client_mapping.png](server_permeate_client_mapping.png)

- 服务端渗透服务端
![server_permeate_server_mapping.png](server_permeate_server_mapping.png)

## 客户端渗透
- 客户端渗透端口池管理
![client_permeate_port_pool.png](client_permeate_port_pool.png)

- 客户端渗透客户端
![client_permeate_client_mapping.png](client_permeate_client_mapping.png)
- 客户端渗透服务端
![client_permeate_server_mapping.png](client_permeate_server_mapping.png)

## 报表
流量管理（每个客户端使用的流量）
![img.png](flow.png)

 流量日统计报表
![clientPerDayFlow.png](clientPerDayFlow.png)

客户端近七天使用流量
![clientPortPerDayFlow.png](clientPortPerDayFlow.png)