Showing preview only (284K chars total). Download the full file or copy to clipboard to get everything.
Repository: Pluto-Whong/natcross2
Branch: master
Commit: 505da5d623c9
Files: 94
Total size: 238.6 KB
Directory structure:
gitextract_mhw2jg3q/
├── .gitignore
├── LICENSE
├── README.md
├── VERSION.md
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── person/
│ │ └── pluto/
│ │ └── natcross2/
│ │ ├── ClientApp.java
│ │ ├── CommonConstants.java
│ │ ├── ServerApp.java
│ │ ├── api/
│ │ │ ├── IBelongControl.java
│ │ │ ├── IHttpRouting.java
│ │ │ ├── passway/
│ │ │ │ ├── SecretPassway.java
│ │ │ │ └── SimplePassway.java
│ │ │ ├── secret/
│ │ │ │ ├── AESSecret.java
│ │ │ │ └── ISecret.java
│ │ │ └── socketpart/
│ │ │ ├── AbsSocketPart.java
│ │ │ ├── HttpRouteSocketPart.java
│ │ │ ├── SecretSocketPart.java
│ │ │ └── SimpleSocketPart.java
│ │ ├── channel/
│ │ │ ├── Channel.java
│ │ │ ├── InteractiveChannel.java
│ │ │ ├── JsonChannel.java
│ │ │ ├── LengthChannel.java
│ │ │ ├── SecretInteractiveChannel.java
│ │ │ ├── SocketChannel.java
│ │ │ └── StringChannel.java
│ │ ├── clientside/
│ │ │ ├── ClientControlThread.java
│ │ │ ├── adapter/
│ │ │ │ ├── IClientAdapter.java
│ │ │ │ └── InteractiveSimpleClientAdapter.java
│ │ │ ├── config/
│ │ │ │ ├── AllSecretInteractiveClientConfig.java
│ │ │ │ ├── HttpRouteClientConfig.java
│ │ │ │ ├── IClientConfig.java
│ │ │ │ ├── InteractiveClientConfig.java
│ │ │ │ └── SecretInteractiveClientConfig.java
│ │ │ ├── handler/
│ │ │ │ ├── CommonReplyHandler.java
│ │ │ │ ├── IClientHandler.java
│ │ │ │ ├── ServerHeartHandler.java
│ │ │ │ └── ServerWaitClientHandler.java
│ │ │ └── heart/
│ │ │ ├── ClientHeartThread.java
│ │ │ └── IClientHeartThread.java
│ │ ├── common/
│ │ │ ├── CommonFormat.java
│ │ │ └── Optional.java
│ │ ├── executor/
│ │ │ ├── IExecutor.java
│ │ │ ├── NatcrossExecutor.java
│ │ │ └── SimpleExecutor.java
│ │ ├── model/
│ │ │ ├── HttpRoute.java
│ │ │ ├── InteractiveModel.java
│ │ │ ├── NatcrossResultModel.java
│ │ │ ├── SecretInteractiveModel.java
│ │ │ ├── enumeration/
│ │ │ │ ├── InteractiveTypeEnum.java
│ │ │ │ └── NatcrossResultEnum.java
│ │ │ └── interactive/
│ │ │ ├── ClientConnectModel.java
│ │ │ ├── ClientControlModel.java
│ │ │ └── ServerWaitModel.java
│ │ ├── nio/
│ │ │ ├── INioProcessor.java
│ │ │ ├── NioHallows.java
│ │ │ └── ProcesserHolder.java
│ │ ├── serverside/
│ │ │ ├── client/
│ │ │ │ ├── ClientServiceThread.java
│ │ │ │ ├── adapter/
│ │ │ │ │ ├── DefaultReadAheadPassValueAdapter.java
│ │ │ │ │ ├── IClientServiceAdapter.java
│ │ │ │ │ ├── PassValueNextEnum.java
│ │ │ │ │ └── ReadAheadPassValueAdapter.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── IClientServiceConfig.java
│ │ │ │ │ ├── SecretSimpleClientServiceConfig.java
│ │ │ │ │ └── SimpleClientServiceConfig.java
│ │ │ │ ├── handler/
│ │ │ │ │ ├── DefaultInteractiveProcessHandler.java
│ │ │ │ │ ├── IPassValueHandler.java
│ │ │ │ │ └── InteractiveProcessHandler.java
│ │ │ │ └── process/
│ │ │ │ ├── ClientConnectProcess.java
│ │ │ │ ├── ClientControlProcess.java
│ │ │ │ └── IProcess.java
│ │ │ └── listen/
│ │ │ ├── IServerListen.java
│ │ │ ├── ListenServerControl.java
│ │ │ ├── ServerListenThread.java
│ │ │ ├── clear/
│ │ │ │ ├── ClearInvalidSocketPartThread.java
│ │ │ │ └── IClearInvalidSocketPartThread.java
│ │ │ ├── config/
│ │ │ │ ├── AllSecretSimpleListenServerConfig.java
│ │ │ │ ├── IListenServerConfig.java
│ │ │ │ ├── MultControlListenServerConfig.java
│ │ │ │ ├── SecretSimpleListenServerConfig.java
│ │ │ │ └── SimpleListenServerConfig.java
│ │ │ ├── control/
│ │ │ │ ├── ControlSocket.java
│ │ │ │ ├── IControlSocket.java
│ │ │ │ └── MultiControlSocket.java
│ │ │ ├── recv/
│ │ │ │ ├── ClientHeartHandler.java
│ │ │ │ ├── CommonReplyHandler.java
│ │ │ │ └── IRecvHandler.java
│ │ │ └── serversocket/
│ │ │ └── ICreateServerSocket.java
│ │ └── utils/
│ │ ├── AESUtil.java
│ │ ├── Assert.java
│ │ ├── CountWaitLatch.java
│ │ ├── MD5Signature.java
│ │ └── Tools.java
│ └── resources/
│ └── logback.xml
└── test/
└── java/
└── person/
└── pluto/
└── TestMain.java
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.*
!/.gitignore
target/
*.iml
================================================
FILE: LICENSE
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [2020] [plutoppppp]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
================================================
FILE: README.md
================================================
# natcross2
内网穿透工具
*********************
**Q群:438793541**
**群密码:natcross2**
## natcross是做什么的?
- 需要自己提供硬件支持、部署的内网穿透工具
- 提供TCP协议类型的内网穿透服务,包括但不限于http(s)、数据库连接、ssh等协议
- 支持https协议转http协议与应用交互方式
- 支持无加密、控制端口加密交互、数据加密交互方式
- 主要服务场景,需要将内网的应用开放到公网,如微信小程序开发调试、支付回调等
- 支持HTTP根据host进行反向代理;目标依然是内网应用,只是可以根据HTTP协议header中的host字段区分选择目标应用(注意:只是有人提出来了HTTP监听统一端口并用域名访问的问题,并且有做的价值才补充的该功能;没做负载功能,这个是内网穿透,不是nginx,更不建议直接用在生产上,需要负载的可以自己去实现)
## 打包使用
服务端-ServerApp打包:
```shell
# 修改ServerApp.java中serviceIp为公网服务器的IP
mvn clean compile package -PserverApp
```
客户端-ClientApp打包:
```shell
mvn clean compile package -PclientApp
```
## 安装
1. 需要一台可在公网访问的机器,并将控制端口(默认 servicePort 10010)、数据端口(如tomcat,开放公网 listenPort 8081端口,外网连接时要对应数据端口8081)开放出来
2. 将ServerApp.jar包放在公网可访问的机器并运行,打包前需要修改serviceIp为公网服务器的IP
3. 将clientApp为入口进行运行,destIp:destPort为要开放的内网应用,如本地的tomcat(127.0.0.1:8080)
4. 用浏览器访问serviceIp:listenPort便可以访问到内网的tomcat
## 参数解释
CommonConstants:
|字段|解释|
|:-:|:-|
|serviceIp|公网服务器的IP|
|servicePort|服务端的控制端口,主要用来与客户端进行指令交互|
|listenPort|服务端的监听端口,也就是部署完成后,在外网访问 serviceIp:listenPort 的方式对内网应用进行访问|
|destIp|要开放到公网的内网应用所在机器的IP|
|destPort|要开放到公网的内网应用的端口|
|aesKey|交互密钥key,保证数据的秘密性,可以查看 SecretInteractiveModel.java 中的fullMessage和checkAutograph中确认密钥的使用方式。<br>如果你使用了secretAll方式进行部署,这个key还是数据加密的key,可以在 SecretPassway.java 中确认密钥的使用方式<br>注意使用长度,windows版本的java只能用最大128长度的密钥|
|tokenKey|交互签名key,签名同aesKey|
ServerApp:
|字段|解释|
|:-:|:-|
|sslKeyStorePath|ssl证书的路径,默认方法只支持pkcs12的证书格式,使用这个证书可以做到https协议转http协议|
|sslKeyStorePassword|证书密码|
|createServerSocket|创建socket的方式,主要针对普通socket和sslSocket的方式进行封装,结合ssl证书使用|
ClientApp:
|字段|解释|
|:-:|:-|
|#secretHttpRoute:routes|http方式,根据不同host路由选择不同的目标应用|
## 内网穿透思路
因NAT网内CLIENT可以正常连接到SERVER端,并且能够保持一段时间的长连接,则由CLIENT发起连接,建立SOCKET对,在SERVER收到外部请求时,可以通过已经建立好的SOCKET将数据传输给CLIENT,CLIENT使用相同的方式将数据发送给指定的网络程序,网络程序回发数据后则按原路返回给请求方。

## 相关技术
|技术|体现点|
|:-:|:-|
|Socket|核心技术概念|
|NIO|nio.NioHallows,使用Selector作为注册监听器(多路复用),有事件唤起后会创建子线程进行异步处理|
|TCP粘包、拆包的解决|channel.LengthChannel,此处用的是一个大端序列的长度加消息内容的方式|
|线程管控|clientside.ClientControlThread、serverside.client.ClientServiceThread、serverside.listen.ServerListenThread作为独立管控及子线程异步处理的主要体现,亦可通过executor包下相关类进行追踪|
|HTTP路由|api.socketpart.HttpRouteSocketPart#routeHost,一个简单的对host头部字段的处理应用|
|消息加密|对AES、MD5联合的使用示例,channel.SecretInteractiveChannel、model.SecretInteractiveModel,对实际消息进行加密,增加辅助字段保证消息的真实性、准确性和完整性|
|计数门闩|utils.CountWaitLatch,类CountDownLatch,增加了countUp,不只受初始化的值决定,可以增加、减少,主要用来解决nio.NioHallows唤醒后批量channel注册的问题(等下,怎么感觉可以用读写锁来解决呢?)|
|线程池|虽然用了线程池,但默认的是Executors.newCachedThreadPool()来生成的,具体的还是需要根据机器来自定义线程池,主要还是对子线程的管控体现吧|
================================================
FILE: VERSION.md
================================================
# VERSION 2.3
1. 支持多客户端连接同一监听端口,从服务端进行负载分发(场景特殊,主要是面对客户端处在非稳定内网,使用多客户端进行多活保证)
# VERSION 2.2
1. socket使用channel方式,交互使用DMA策略
2. 监听端口、控制端口获得新连接后使用子线程方式进行异步操作
3. 优化了http路由算法,提高了路由选择时的速度
4. passWay使用了nio策略,统一交付NioHallows处理
5. ServerListenThread(当前长连接存在问题,故而目前使用bio形式获取)、ClientServiceThread使用了nio策略,统一交付NioHallows处理
6. 增加了CountWaitLatch(对标CountDownLatch,但增加的countUp,不再依赖初始化数量),用于NioHallows的等待注册过程
7. 修改pom.xml,去除无用的包,改为dependencyManagement方式进行版本管理,避免和引入项目发生版本冲突
# VERSION 2.1
支持http路由了,可以像nginx那样监听同一个端口,根据访问域名路由目标应用
注意http标准,需要头部有Host,并且注意换行严格要符合http的标准,且如果是长连接路由目标只会在第一次连接时选择目标
虽然上面有很多要求,但是一般使用标准的浏览器都不会出现问题的
================================================
FILE: pom.xml
================================================
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>person.pluto</groupId>
<artifactId>natcross2</artifactId>
<version>2.3.2</version>
<name>natcross2</name>
<description>natcross 2</description>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
<maven.shade.version>3.4.1</maven.shade.version>
</properties>
<dependencyManagement>
<dependencies>
<!-- base tools ... -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.26</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.13</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.18.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.9</version>
</dependency>
<!-- base tools ! -->
</dependencies>
</dependencyManagement>
<dependencies>
<!-- base tools ... -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!-- base tools ! -->
</dependencies>
<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.9.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>compile</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>install</id>
<build>
<resources>
<resource>
<filtering>false</filtering>
<directory>src/main/resources</directory>
<excludes>
<exclude>**</exclude>
</excludes>
</resource>
</resources>
</build>
</profile>
<profile>
<id>serverApp</id>
<build>
<finalName>ServerApp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven.shade.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>person.pluto.natcross2.ServerApp</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>clientApp</id>
<build>
<finalName>ClientApp</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>${maven.shade.version}</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>person.pluto.natcross2.ClientApp</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<!-- Maven 自带的中央仓库使用的Id为central 如果其他的仓库声明也是用该Id 就会覆盖中央仓库的配置 -->
<id>nexus-aliyun</id>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</repository>
</repositories>
</project>
================================================
FILE: src/main/java/person/pluto/natcross2/ClientApp.java
================================================
package person.pluto.natcross2;
import person.pluto.natcross2.CommonConstants.ListenDest;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.clientside.config.AllSecretInteractiveClientConfig;
import person.pluto.natcross2.clientside.config.HttpRouteClientConfig;
import person.pluto.natcross2.clientside.config.InteractiveClientConfig;
import person.pluto.natcross2.clientside.config.SecretInteractiveClientConfig;
import person.pluto.natcross2.model.HttpRoute;
/**
* <p>
* 客户端,放在内网侧
* </p>
*
* @author Pluto
* @since 2020-01-09 16:26:44
*/
public class ClientApp {
public static void main(String[] args) throws Exception {
// simple();
secret();
// secretAll();
// secretHttpRoute();
}
/**
* http路由
* <p>
* 默认使用交互加密、数据不加密的策略
*/
public static void secretHttpRoute() throws Exception {
HttpRoute[] routes = new HttpRoute[]{
//
HttpRoute.of("localhost", "127.0.0.1", 8080),
//
HttpRoute.of(true, "127.0.0.1", "127.0.0.1", 8080),
//
};
for (ListenDest model : CommonConstants.listenDestArray) {
SecretInteractiveClientConfig baseConfig = new SecretInteractiveClientConfig();
// 设置服务端IP和端口
baseConfig.setClientServiceIp(CommonConstants.serviceIp);
baseConfig.setClientServicePort(CommonConstants.servicePort);
// 设置对外暴露的端口,该端口的启动在服务端,这里只是表明要跟服务端的那个监听服务对接
baseConfig.setListenServerPort(model.listenPort);
// 设置交互密钥和签名key
baseConfig.setBaseAesKey(CommonConstants.aesKey);
baseConfig.setTokenKey(CommonConstants.tokenKey);
HttpRouteClientConfig config = new HttpRouteClientConfig(baseConfig);
config.addRoute(routes);
new ClientControlThread(config).createControl();
}
}
/**
* 交互、隧道都进行加密
*/
public static void secretAll() throws Exception {
for (ListenDest model : CommonConstants.listenDestArray) {
AllSecretInteractiveClientConfig config = new AllSecretInteractiveClientConfig();
// 设置服务端IP和端口
config.setClientServiceIp(CommonConstants.serviceIp);
config.setClientServicePort(CommonConstants.servicePort);
// 设置对外暴露的端口,该端口的启动在服务端,这里只是表明要跟服务端的那个监听服务对接
config.setListenServerPort(model.listenPort);
// 设置要暴露的目标IP和端口
config.setDestIp(model.destIp);
config.setDestPort(model.destPort);
// 设置交互密钥和签名key
config.setBaseAesKey(CommonConstants.aesKey);
config.setTokenKey(CommonConstants.tokenKey);
// 设置隧道交互密钥
config.setBasePasswayKey(CommonConstants.aesKey);
new ClientControlThread(config).createControl();
}
}
/**
* 交互加密,即交互验证
*/
public static void secret() throws Exception {
for (ListenDest model : CommonConstants.listenDestArray) {
SecretInteractiveClientConfig config = new SecretInteractiveClientConfig();
// 设置服务端IP和端口
config.setClientServiceIp(CommonConstants.serviceIp);
config.setClientServicePort(CommonConstants.servicePort);
// 设置对外暴露的端口,该端口的启动在服务端,这里只是表明要跟服务端的那个监听服务对接
config.setListenServerPort(model.listenPort);
// 设置要暴露的目标IP和端口
config.setDestIp(model.destIp);
config.setDestPort(model.destPort);
// 设置交互密钥和签名key
config.setBaseAesKey(CommonConstants.aesKey);
config.setTokenKey(CommonConstants.tokenKey);
new ClientControlThread(config).createControl();
}
}
/**
* 无加密、无验证
*/
public static void simple() throws Exception {
for (ListenDest model : CommonConstants.listenDestArray) {
InteractiveClientConfig config = new InteractiveClientConfig();
// 设置服务端IP和端口
config.setClientServiceIp(CommonConstants.serviceIp);
config.setClientServicePort(CommonConstants.servicePort);
// 设置对外暴露的端口,该端口的启动在服务端,这里只是表明要跟服务端的那个监听服务对接
config.setListenServerPort(model.listenPort);
// 设置要暴露的目标IP和端口
config.setDestIp(model.destIp);
config.setDestPort(model.destPort);
new ClientControlThread(config).createControl();
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/CommonConstants.java
================================================
package person.pluto.natcross2;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
/**
* <p>
* 公共参数
* </p>
*
* @author Pluto
* @since 2020-04-10 12:29:01
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CommonConstants {
// 服务端地址,支持IP或域名,这个根据服务端放的网络位置进行设置
public static final String serviceIp = "127.0.0.1";
// 客户端服务的端口
public static final int servicePort = 10010;
// 映射对
public static ListenDest[] listenDestArray = new ListenDest[]{
//
ListenDest.of(8081, "127.0.0.1", 8080),
//
};
// 交互密钥 AES
public static final String aesKey = "8AUWlb+IWD+Fhbs0xnXCCg==";
// 交互签名key
public static final String tokenKey = "tokenKey";
/**
* <p>
* 监听、映射对
* </p>
*/
static class ListenDest {
public static ListenDest of(int listenPort, String destIp, int destPort) {
ListenDest model = new ListenDest();
model.listenPort = listenPort;
model.destIp = destIp;
model.destPort = destPort;
return model;
}
// 服务端监听的端口,外网访问服务端IP:listengPort即可进行穿透
public int listenPort;
// 穿透的目标,即要暴露在外网的内网IP
public String destIp;
// 要暴露的内网端口
public int destPort;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/ServerApp.java
================================================
package person.pluto.natcross2;
import org.apache.commons.lang3.StringUtils;
import person.pluto.natcross2.CommonConstants.ListenDest;
import person.pluto.natcross2.serverside.client.ClientServiceThread;
import person.pluto.natcross2.serverside.client.config.SecretSimpleClientServiceConfig;
import person.pluto.natcross2.serverside.client.config.SimpleClientServiceConfig;
import person.pluto.natcross2.serverside.listen.ListenServerControl;
import person.pluto.natcross2.serverside.listen.config.AllSecretSimpleListenServerConfig;
import person.pluto.natcross2.serverside.listen.config.MultControlListenServerConfig;
import person.pluto.natcross2.serverside.listen.config.SecretSimpleListenServerConfig;
import person.pluto.natcross2.serverside.listen.config.SimpleListenServerConfig;
import person.pluto.natcross2.serverside.listen.serversocket.ICreateServerSocket;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLServerSocketFactory;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
/**
* <p>
* 服务端,放在外网侧
* </p>
*
* @author Pluto
* @since 2020-01-09 16:27:03
*/
public class ServerApp {
// 你的p12格式的证书路径
private static final String sslKeyStorePath = System.getenv("sslKeyStorePath");
// 你的证书密码
private static final String sslKeyStorePassword = System.getenv("sslKeyStorePassword");
public static ICreateServerSocket createServerSocket;
public static void main(String[] args) throws Exception {
// 如果需要HTTPS协议的支持,则填写sslKeyStorePath、sslKeyStorePassword或在环境变量中定义
if (StringUtils.isNoneBlank(sslKeyStorePath, sslKeyStorePassword)) {
createServerSocket = listenPort -> {
KeyStore keystore = KeyStore.getInstance("PKCS12");
keystore.load(Files.newInputStream(Paths.get(sslKeyStorePath)), sslKeyStorePassword.toCharArray());
KeyManagerFactory keyFactory = KeyManagerFactory.getInstance("sunx509");
keyFactory.init(keystore, sslKeyStorePassword.toCharArray());
SSLContext ctx = SSLContext.getInstance("TLSv1.2");
ctx.init(keyFactory.getKeyManagers(), null, null);
SSLServerSocketFactory serverSocketFactory = ctx.getServerSocketFactory();
return serverSocketFactory.createServerSocket(listenPort);
};
}
// simple();
secret();
// secretAll();
// multControlSecret();
}
/**
* 多客户端,控制通道加密
*/
public static void multControlSecret() throws Exception {
// 设置并启动客户端服务线程
SecretSimpleClientServiceConfig config = new SecretSimpleClientServiceConfig(CommonConstants.servicePort);
// 设置交互aes密钥和签名密钥
config.setBaseAesKey(CommonConstants.aesKey);
config.setTokenKey(CommonConstants.tokenKey);
new ClientServiceThread(config).start();
for (ListenDest model : CommonConstants.listenDestArray) {
// 设置并启动一个穿透端口
SecretSimpleListenServerConfig baseListengConfig = new SecretSimpleListenServerConfig(model.listenPort);
// 设置交互aes密钥和签名密钥,这里使用和客户端服务相同的密钥,可以根据需要设置不同的
baseListengConfig.setBaseAesKey(CommonConstants.aesKey);
baseListengConfig.setTokenKey(CommonConstants.tokenKey);
baseListengConfig.setCreateServerSocket(createServerSocket);
MultControlListenServerConfig listengConfig = new MultControlListenServerConfig(baseListengConfig);
ListenServerControl.createNewListenServer(listengConfig);
}
}
/**
* 交互、隧道都进行加密
*/
public static void secretAll() throws Exception {
// 设置并启动客户端服务线程
SecretSimpleClientServiceConfig config = new SecretSimpleClientServiceConfig(CommonConstants.servicePort);
// 设置交互aes密钥和签名密钥
config.setBaseAesKey(CommonConstants.aesKey);
config.setTokenKey(CommonConstants.tokenKey);
new ClientServiceThread(config).start();
for (ListenDest model : CommonConstants.listenDestArray) {
AllSecretSimpleListenServerConfig listengConfig = new AllSecretSimpleListenServerConfig(model.listenPort);
// 设置交互aes密钥和签名密钥,这里使用和客户端服务相同的密钥,可以根据需要设置不同的
listengConfig.setBaseAesKey(CommonConstants.aesKey);
listengConfig.setTokenKey(CommonConstants.tokenKey);
// 设置隧道密钥
listengConfig.setBasePasswayKey(CommonConstants.aesKey);
listengConfig.setCreateServerSocket(createServerSocket);
ListenServerControl.createNewListenServer(listengConfig);
}
}
/**
* 交互加密,即交互验证
*/
public static void secret() throws Exception {
// 设置并启动客户端服务线程
SecretSimpleClientServiceConfig config = new SecretSimpleClientServiceConfig(CommonConstants.servicePort);
// 设置交互aes密钥和签名密钥
config.setBaseAesKey(CommonConstants.aesKey);
config.setTokenKey(CommonConstants.tokenKey);
new ClientServiceThread(config).start();
for (ListenDest model : CommonConstants.listenDestArray) {
// 设置并启动一个穿透端口
SecretSimpleListenServerConfig listengConfig = new SecretSimpleListenServerConfig(model.listenPort);
// 设置交互aes密钥和签名密钥,这里使用和客户端服务相同的密钥,可以根据需要设置不同的
listengConfig.setBaseAesKey(CommonConstants.aesKey);
listengConfig.setTokenKey(CommonConstants.tokenKey);
listengConfig.setCreateServerSocket(createServerSocket);
ListenServerControl.createNewListenServer(listengConfig);
}
}
/**
* 无加密、无验证
*/
public static void simple() throws Exception {
// 设置并启动客户端服务线程
SimpleClientServiceConfig config = new SimpleClientServiceConfig(CommonConstants.servicePort);
new ClientServiceThread(config).start();
for (ListenDest model : CommonConstants.listenDestArray) {
// 设置并启动一个穿透端口
SimpleListenServerConfig listengConfig = new SimpleListenServerConfig(model.listenPort);
listengConfig.setCreateServerSocket(createServerSocket);
ListenServerControl.createNewListenServer(listengConfig);
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/IBelongControl.java
================================================
package person.pluto.natcross2.api;
/**
* <p>
* 通知上次停止的统一类,为适应不同的类型进行不同的函数封装
* </p>
*
* @author Pluto
* @since 2019-07-12 08:39:25
*/
public interface IBelongControl {
/**
* 无标记通知
*/
default void noticeStop() {
// do nothing
}
/**
* 有标记通知
*
* @param socketPartKey
* @return
*/
default boolean stopSocketPart(String socketPartKey) {
return true;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/IHttpRouting.java
================================================
package person.pluto.natcross2.api;
import person.pluto.natcross2.model.HttpRoute;
/**
* <p>
* http 路由器
* </p>
*
* @author Pluto
* @since 2021-04-26 08:54:44
*/
public interface IHttpRouting {
/**
* 获取有效路由
*
* @param host
* @return
*/
HttpRoute pickEffectiveRoute(String host);
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/passway/SecretPassway.java
================================================
package person.pluto.natcross2.api.passway;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.IBelongControl;
import person.pluto.natcross2.api.secret.ISecret;
import person.pluto.natcross2.channel.LengthChannel;
import person.pluto.natcross2.executor.NatcrossExecutor;
import person.pluto.natcross2.nio.NioHallows;
import person.pluto.natcross2.utils.Tools;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Objects;
/**
* <p>
* 加密型隧道,一侧加密,一侧原样输入、输出
* </p>
*
* @author Pluto
* @since 2020-01-08 15:55:29
*/
@Data
@Slf4j
public class SecretPassway implements Runnable {
/**
* 通道模式
*/
public enum Mode {
/**
* 从无加密接受到加密输出
*/
noToSecret,
/**
* 从加密接受到无加密输出
*/
secretToNo
}
private boolean alive = false;
private ISecret secret;
private Mode mode;
private IBelongControl belongControl;
private int streamCacheSize = 8192;
private Socket recvSocket;
private Socket sendSocket;
@Override
public void run() {
try {
if (Mode.noToSecret.equals(this.mode)) {
noToSecret();
} else {
secretToNo();
}
} catch (Exception e) {
//
}
log.debug("one InputToOutputThread closed");
// 传输完成后退出
this.cancel();
}
/**
* 从加密侧输入,解密后输出到无加密侧
*
* @throws Exception
*/
private void secretToNo() throws Exception {
LengthChannel recvChannel = new LengthChannel(this.recvSocket);
OutputStream outputStream = this.sendSocket.getOutputStream();
SocketChannel outputChannel = this.sendSocket.getChannel();
ISecret secret = this.secret;
byte[] read;
byte[] decrypt;
while (this.alive) {
read = recvChannel.read();
if (read == null) {
break;
}
decrypt = secret.decrypt(read);
if (Objects.isNull(outputChannel)) {
outputStream.write(decrypt);
outputStream.flush();
} else {
Tools.channelWrite(outputChannel, ByteBuffer.wrap(decrypt));
}
}
}
/**
* 从无加密侧,经过加密后输出到加密侧
*
* @throws Exception
*/
private void noToSecret() throws Exception {
InputStream inputStream = this.recvSocket.getInputStream();
LengthChannel sendChannel = new LengthChannel(this.sendSocket);
// 字段赋值局部变量,入栈
boolean alive = this.alive;
ISecret secret = this.secret;
int len;
byte[] arrayTemp = new byte[this.streamCacheSize];
byte[] encrypt;
while (alive && (len = inputStream.read(arrayTemp)) > 0) {
encrypt = secret.encrypt(arrayTemp, 0, len);
sendChannel.writeAndFlush(encrypt);
}
}
/**
* 判断是否有效
*
* @return
*/
public boolean isValid() {
return this.alive;
}
/**
* 退出
*
* @author Pluto
* @since 2020-01-08 15:57:48
*/
public void cancel() {
if (!this.alive) {
return;
}
this.alive = false;
NioHallows.release(this.recvSocket.getChannel());
try {
Socket sendSocket;
if ((sendSocket = this.sendSocket) != null) {
// TCP 挥手步骤,对方调用 shutdownOutput 后等价完成 socket.close
sendSocket.shutdownOutput();
}
} catch (IOException e) {
// do nothing
}
IBelongControl belong;
if ((belong = this.belongControl) != null) {
this.belongControl = null;
belong.noticeStop();
}
}
/**
* 启动
*/
public void start() {
if (this.alive) {
return;
}
this.alive = true;
NatcrossExecutor.executePassway(this);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/passway/SimplePassway.java
================================================
package person.pluto.natcross2.api.passway;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.IBelongControl;
import person.pluto.natcross2.executor.NatcrossExecutor;
import person.pluto.natcross2.nio.INioProcessor;
import person.pluto.natcross2.nio.NioHallows;
import person.pluto.natcross2.utils.Tools;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Objects;
/**
* <p>
* 简单的隧道,无任何处理,只从输入侧原样输出到输出侧
* </p>
*
* @author Pluto
* @since 2020-01-08 15:58:11
*/
@Slf4j
public class SimplePassway implements Runnable, INioProcessor {
private boolean alive = false;
/**
* 所属对象,完成后通知
*/
@Setter
private IBelongControl belongControl;
/**
* 缓存大小
*/
@Setter
private int streamCacheSize = 8192;
@Setter
private Socket recvSocket;
@Setter
private Socket sendSocket;
private OutputStream outputStream;
private SocketChannel outputChannel;
private OutputStream getOutputStream() throws IOException {
OutputStream outputStream = this.outputStream;
if (Objects.isNull(outputStream)) {
outputStream = this.sendSocket.getOutputStream();
this.outputStream = outputStream;
}
return outputStream;
}
private SocketChannel getOutputChannel() {
SocketChannel outputChannel = this.outputChannel;
if (Objects.isNull(outputChannel)) {
outputChannel = this.sendSocket.getChannel();
this.outputChannel = outputChannel;
}
return outputChannel;
}
/**
* 向输出通道输出数据
* <p>
* 这里不只是为了DMA而去用DMA,而是这里有奇葩问题
* <p>
* 如能采用了SocketChannel,而去用outputStream的时候,不管输入输出,都会有奇怪的问题,比如输出会莫名的阻塞住
* <p>
* 整体就是如果能用nio的方法,但是用了bio形式都会各种什么 NullPointException、IllageSateException 等等错误
* <p>
*
* @param byteBuffer
* @throws IOException
*/
private void write(ByteBuffer byteBuffer) throws IOException {
SocketChannel outputChannel = this.getOutputChannel();
if (Objects.nonNull(outputChannel)) {
Tools.channelWrite(outputChannel, byteBuffer);
} else {
OutputStream outputStream = this.getOutputStream();
outputStream.write(byteBuffer.array(), byteBuffer.position(), byteBuffer.limit());
outputStream.flush();
}
}
@Override
public void run() {
try {
InputStream inputStream = this.recvSocket.getInputStream();
int len;
byte[] arrayTemp = new byte[this.streamCacheSize];
while (this.alive && (len = inputStream.read(arrayTemp)) > 0) {
this.write(ByteBuffer.wrap(arrayTemp, 0, len));
}
} catch (IOException e) {
// do nothing
}
log.debug("one InputToOutputThread closed");
// 传输完成后退出
this.cancel();
}
// ============== nio =================
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private ByteBuffer byteBuffer;
private ByteBuffer obtainByteBuffer() {
ByteBuffer byteBuffer = this.byteBuffer;
if (Objects.isNull(byteBuffer)) {
if (Objects.isNull(this.getOutputChannel())) {
byteBuffer = ByteBuffer.allocate(this.streamCacheSize);
} else {
// 输入输出可以使用channel,此处则使用DirectByteBuffer,这时候才真正体现出了DMA
byteBuffer = ByteBuffer.allocateDirect(this.streamCacheSize);
}
this.byteBuffer = byteBuffer;
}
return byteBuffer;
}
@Override
public void process(SelectionKey key) {
if (this.alive && key.isValid()) {
ByteBuffer buffer = this.obtainByteBuffer();
SocketChannel inputChannel = (SocketChannel) key.channel();
try {
int len;
do {
buffer.clear();
len = inputChannel.read(buffer);
if (len > 0) {
buffer.flip();
if (buffer.hasRemaining()) {
this.write(buffer);
}
}
} while (len > 0);
// 如果不是负数,则还没有断开连接,返回继续等待
if (len == 0) {
return;
}
} catch (IOException e) {
//
}
}
log.debug("one InputToOutputThread closed");
this.cancel();
}
/**
* 判断是否有效
*
* @return
*/
public boolean isValid() {
return this.alive;
}
/**
* 退出
*/
public void cancel() {
if (!this.alive) {
return;
}
this.alive = false;
NioHallows.release(this.recvSocket.getChannel());
try {
Socket sendSocket = this.sendSocket;
if (Objects.nonNull(sendSocket)) {
// TCP 挥手步骤,对方调用 shutdownOutput 后等价完成 socket.close
sendSocket.shutdownOutput();
}
} catch (IOException e) {
// do nothing
}
IBelongControl belong;
if ((belong = this.belongControl) != null) {
this.belongControl = null;
belong.noticeStop();
}
}
/**
* 启动
*/
public void start() {
if (this.alive) {
return;
}
this.alive = true;
SocketChannel recvChannel = this.recvSocket.getChannel();
if (Objects.isNull(recvChannel)) {
NatcrossExecutor.executePassway(this);
} else {
try {
NioHallows.register(recvChannel, SelectionKey.OP_READ, this);
} catch (IOException e) {
log.error("nio register failed", e);
this.cancel();
}
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/secret/AESSecret.java
================================================
package person.pluto.natcross2.api.secret;
import lombok.Data;
import person.pluto.natcross2.utils.AESUtil;
import java.security.Key;
/**
* <p>
* AES加密方式
* </p>
*
* @author Pluto
* @since 2020-01-08 16:01:40
*/
@Data
public class AESSecret implements ISecret {
private Key aesKey;
@Override
public byte[] encrypt(byte[] content, int offset, int len) throws Exception {
return AESUtil.encrypt(this.aesKey, content, offset, len);
}
@Override
public byte[] decrypt(byte[] result) throws Exception {
return AESUtil.decrypt(this.aesKey, result);
}
/**
* 设置密钥
*
* @param aesKey
*/
public void setBaseAesKey(String aesKey) {
this.aesKey = AESUtil.createKeyByBase64(aesKey);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/secret/ISecret.java
================================================
package person.pluto.natcross2.api.secret;
/**
* <p>
* 加密方法
* </p>
*
* @author Pluto
* @since 2020-01-08 16:01:28
*/
public interface ISecret {
/**
* 加密数据
*
* @param content
* @param offset
* @param len
* @return
* @throws Exception
*/
byte[] encrypt(byte[] content, int offset, int len) throws Exception;
/**
* 解密数据
*
* @param result
* @return
* @throws Exception
*/
byte[] decrypt(byte[] result) throws Exception;
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/socketpart/AbsSocketPart.java
================================================
package person.pluto.natcross2.api.socketpart;
import lombok.AccessLevel;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import person.pluto.natcross2.api.IBelongControl;
import java.net.Socket;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* <p>
* socketPart抽象类
* </p>
*
* @author Pluto
* @since 2020-01-08 16:01:56
*/
@Data
public abstract class AbsSocketPart {
@Setter(AccessLevel.PRIVATE)
protected volatile boolean isAlive = false;
@Setter(AccessLevel.PRIVATE)
protected volatile boolean canceled = false;
@Setter(AccessLevel.PRIVATE)
protected LocalDateTime createTime;
/**
* 等待连接有效时间,ms
*/
@Getter
@Setter
private long invalidMillis = 60000L;
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.PROTECTED)
protected IBelongControl belongThread;
protected String socketPartKey;
/**
* 接受数据的socket;接受与发送的区分主要是主动发送方
*/
protected Socket recvSocket;
/**
* 发送的socket
*/
protected Socket sendSocket;
protected AbsSocketPart(IBelongControl belongThread) {
this.belongThread = belongThread;
this.createTime = LocalDateTime.now();
}
/**
* 是否有效
*/
public boolean isValid() {
if (this.canceled) {
return false;
}
if (this.isAlive) {
return true;
}
long millis = Duration.between(this.createTime, LocalDateTime.now()).toMillis();
return millis < this.invalidMillis;
}
/**
* 退出
*/
public abstract void cancel();
/**
* 打通隧道
*
* @return
*/
public abstract boolean createPassWay();
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/socketpart/HttpRouteSocketPart.java
================================================
package person.pluto.natcross2.api.socketpart;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.IBelongControl;
import person.pluto.natcross2.api.IHttpRouting;
import person.pluto.natcross2.api.passway.SimplePassway;
import person.pluto.natcross2.model.HttpRoute;
import person.pluto.natcross2.utils.Assert;
import person.pluto.natcross2.utils.Tools;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* <p>
* http路由socket对
* </p>
*
* @author Pluto
* @since 2020-04-23 16:57:28
*/
@Slf4j
public class HttpRouteSocketPart extends SimpleSocketPart {
private static final Charset httpCharset = StandardCharsets.ISO_8859_1;
// 这里的 : 好巧不巧的 0x20 位是1,可以利用一波
private static final byte colonByte = ':';
private static final byte[] hostMatcher = new byte[]{'h', 'o', 's', 't', colonByte};
private static final int colonIndex = hostMatcher.length - 1;
private final IHttpRouting httpRouting;
public HttpRouteSocketPart(IBelongControl belongThread, IHttpRouting httpRouting) {
super(belongThread);
this.httpRouting = httpRouting;
}
/**
* 选择路由并连接至目标
*
* @throws Exception
* @author Pluto
* @since 2020-04-24 11:01:24
*/
protected void routeHost() throws Exception {
String host = null;
BufferedInputStream inputStream = new BufferedInputStream(this.sendSocket.getInputStream());
// 缓存数据,不能我们处理了就不给实际应用
ByteArrayOutputStream headerBufferStream = new ByteArrayOutputStream(1024);
// 临时输出列,用于读取一整行后进行字符串判断
ByteArrayOutputStream lineBufferStream = new ByteArrayOutputStream();
for (int flag = 0, lineCount = 0, matchFlag = 0; ; lineCount++) {
// 依次读取
int read = inputStream.read();
lineBufferStream.write(read);
if (read < 0) {
break;
}
// 记录换行状态
if (read == '\r' || read == '\n') {
flag++;
} else {
flag = 0;
if (
// 这里matchFlag与lineCount不相等的频次比例较大,先比较
matchFlag == lineCount
// 肯定要小于了呀
&& matchFlag < hostMatcher.length
// 大写转小写,如果是冒号的位置,需要完全相等
&& hostMatcher[matchFlag] == (read | 0x20) &&
(matchFlag != colonIndex || colonByte == read)
//
) {
matchFlag++;
}
}
// 如果大于等于4则就表示http头结束了
if (flag >= 4) {
break;
}
// 等于2表示一行结束了,需要进行处理
if (flag == 2) {
boolean isHostLine = (matchFlag == hostMatcher.length);
// for循环特性,设置-1,营造line为0
lineCount = -1;
matchFlag = 0;
// 省去一次toByteArray拷贝的可能
lineBufferStream.writeTo(headerBufferStream);
if (isHostLine) {
byte[] byteArray = lineBufferStream.toByteArray();
// 重置行输出流
lineBufferStream.reset();
int left, right;
byte rightByte;
for (left = right = hostMatcher.length; right < byteArray.length; right++) {
if (byteArray[left] == ' ') {
// 左边先去掉空白,去除期间right不用判断
left++;
} else if (
//
(rightByte = byteArray[right]) == colonByte
//
|| rightByte == ' ' || rightByte == '\r' || rightByte == '\n') {
// right位置到left位置必有字符,遇到空白或 : 则停下,与left中间的组合为host地址
break;
}
}
// 将缓存中的数据进行字符串化,根据http标准,字符集为 ISO-8859-1
host = new String(byteArray, left, right - left, httpCharset);
break;
} else {
// 重置临时输出流
lineBufferStream.reset();
}
}
}
// 将最后残留的输出
lineBufferStream.writeTo(headerBufferStream);
Socket recvSocket = this.recvSocket;
HttpRoute willConnect = this.httpRouting.pickEffectiveRoute(host);
InetSocketAddress destAddress = new InetSocketAddress(willConnect.getDestIp(), willConnect.getDestPort());
recvSocket.connect(destAddress);
OutputStream outputStream = recvSocket.getOutputStream();
headerBufferStream.writeTo(outputStream);
// emmm.... 用bufferedStream每次read不用单字节从硬件缓存里读呀,快了些呢,咋地了,不就是再拷贝一次嘛!
Tools.streamCopy(inputStream, outputStream);
// flush的原因,不排除这里全部读完了,导致缓存中没有数据,那即使创建了passway也不会主动flush而是挂在那里,防止遇到lazy的自动刷新特性
outputStream.flush();
}
@Override
public boolean createPassWay() {
Assert.state(!this.canceled, "不得重启已退出的socketPart");
if (this.isAlive) {
return true;
}
this.isAlive = true;
try {
this.routeHost();
SimplePassway outToInPassway = this.outToInPassway = new SimplePassway();
outToInPassway.setBelongControl(this);
outToInPassway.setSendSocket(this.sendSocket);
outToInPassway.setRecvSocket(this.recvSocket);
outToInPassway.setStreamCacheSize(this.getStreamCacheSize());
SimplePassway inToOutPassway = this.inToOutPassway = new SimplePassway();
inToOutPassway.setBelongControl(this);
inToOutPassway.setSendSocket(this.recvSocket);
inToOutPassway.setRecvSocket(this.sendSocket);
inToOutPassway.setStreamCacheSize(this.getStreamCacheSize());
outToInPassway.start();
inToOutPassway.start();
} catch (Exception e) {
log.error("socketPart [" + this.socketPartKey + "] 隧道建立异常", e);
this.stop();
return false;
}
return true;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/socketpart/SecretSocketPart.java
================================================
package person.pluto.natcross2.api.socketpart;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.IBelongControl;
import person.pluto.natcross2.api.passway.SecretPassway;
import person.pluto.natcross2.api.passway.SecretPassway.Mode;
import person.pluto.natcross2.api.secret.ISecret;
import person.pluto.natcross2.utils.Assert;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
/**
* <p>
* 加密-无加密socket对
* </p>
*
* @author Pluto
* @since 2020-01-08 16:05:25
*/
@Slf4j
public class SecretSocketPart extends AbsSocketPart implements IBelongControl {
@Getter
@Setter
private ISecret secret;
private SecretPassway noToSecretPassway;
private SecretPassway secretToNoPassway;
private final CountDownLatch cancelLatch = new CountDownLatch(2);
@Getter
@Setter
private int streamCacheSize = 8192;
public SecretSocketPart(IBelongControl belongThread) {
super(belongThread);
}
@Override
public void cancel() {
if (this.canceled) {
return;
}
this.canceled = true;
this.isAlive = false;
log.debug("socketPart {} will cancel", this.socketPartKey);
SecretPassway noToSecretPassway;
if ((noToSecretPassway = this.noToSecretPassway) != null) {
this.noToSecretPassway = null;
noToSecretPassway.cancel();
}
SecretPassway secretToNoPassway;
if ((secretToNoPassway = this.secretToNoPassway) != null) {
this.secretToNoPassway = null;
secretToNoPassway.cancel();
}
Socket recvSocket;
if ((recvSocket = this.recvSocket) != null) {
this.recvSocket = null;
try {
recvSocket.close();
} catch (IOException e) {
log.debug("socketPart [{}] 监听端口 关闭异常", socketPartKey);
}
}
Socket sendSocket;
if ((sendSocket = this.sendSocket) != null) {
this.sendSocket = null;
try {
sendSocket.close();
} catch (IOException e) {
log.debug("socketPart [{}] 发送端口 关闭异常", socketPartKey);
}
}
log.debug("socketPart {} is cancelled", this.socketPartKey);
}
@Override
public boolean createPassWay() {
Assert.state(!this.canceled, "不得重启已退出的socketPart");
if (this.isAlive) {
return true;
}
this.isAlive = true;
try {
// 主要面向服务端-客户端过程加密
SecretPassway noToSecretPassway = this.noToSecretPassway = new SecretPassway();
noToSecretPassway.setBelongControl(this);
noToSecretPassway.setMode(Mode.noToSecret);
noToSecretPassway.setRecvSocket(this.recvSocket);
noToSecretPassway.setSendSocket(this.sendSocket);
noToSecretPassway.setStreamCacheSize(getStreamCacheSize());
noToSecretPassway.setSecret(this.secret);
SecretPassway secretToNoPassway = this.secretToNoPassway = new SecretPassway();
secretToNoPassway.setBelongControl(this);
secretToNoPassway.setMode(Mode.secretToNo);
secretToNoPassway.setRecvSocket(this.sendSocket);
secretToNoPassway.setSendSocket(this.recvSocket);
secretToNoPassway.setSecret(this.secret);
noToSecretPassway.start();
secretToNoPassway.start();
} catch (Exception e) {
log.error("socketPart [" + this.socketPartKey + "] 隧道建立异常", e);
this.stop();
return false;
}
return true;
}
/**
* 停止
*/
public void stop() {
this.cancel();
IBelongControl belong;
if ((belong = this.belongThread) != null) {
this.belongThread = null;
belong.noticeStop();
}
}
@Override
public void noticeStop() {
CountDownLatch cancelLatch = this.cancelLatch;
cancelLatch.countDown();
if (cancelLatch.getCount() <= 0) {
this.stop();
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/api/socketpart/SimpleSocketPart.java
================================================
package person.pluto.natcross2.api.socketpart;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.IBelongControl;
import person.pluto.natcross2.api.passway.SimplePassway;
import person.pluto.natcross2.utils.Assert;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.CountDownLatch;
/**
* <p>
* socket匹配对
* </p>
*
* @author Pluto
* @since 2019-07-12 08:36:30
*/
@Slf4j
public class SimpleSocketPart extends AbsSocketPart implements IBelongControl {
protected SimplePassway outToInPassway;
protected SimplePassway inToOutPassway;
private final CountDownLatch cancelLatch = new CountDownLatch(2);
@Getter
@Setter
private int streamCacheSize = 8192;
public SimpleSocketPart(IBelongControl belongThread) {
super(belongThread);
}
/**
* 停止,并告知上层处理掉
*/
public void stop() {
this.cancel();
IBelongControl belong;
if ((belong = this.belongThread) != null) {
this.belongThread = null;
belong.stopSocketPart(this.socketPartKey);
}
}
@Override
public void cancel() {
if (this.canceled) {
return;
}
this.canceled = true;
this.isAlive = false;
log.debug("socketPart {} will cancel", this.socketPartKey);
SimplePassway outToInPassway;
if ((outToInPassway = this.outToInPassway) != null) {
this.outToInPassway = null;
outToInPassway.cancel();
}
SimplePassway inToOutPassway;
if ((inToOutPassway = this.inToOutPassway) != null) {
this.inToOutPassway = null;
inToOutPassway.cancel();
}
Socket recvSocket;
if ((recvSocket = this.recvSocket) != null) {
this.recvSocket = null;
try {
recvSocket.close();
} catch (IOException e) {
log.debug("socketPart [{}] 监听端口 关闭异常", socketPartKey);
}
}
Socket sendSocket;
if ((sendSocket = this.sendSocket) != null) {
this.sendSocket = null;
try {
sendSocket.close();
} catch (IOException e) {
log.debug("socketPart [{}] 发送端口 关闭异常", socketPartKey);
}
}
log.debug("socketPart {} is cancelled", this.socketPartKey);
}
@Override
public boolean createPassWay() {
Assert.state(!this.canceled, "不得重启已退出的socketPart");
if (this.isAlive) {
return true;
}
this.isAlive = true;
try {
SimplePassway outToInPassway = this.outToInPassway = new SimplePassway();
outToInPassway.setBelongControl(this);
outToInPassway.setRecvSocket(this.recvSocket);
outToInPassway.setSendSocket(this.sendSocket);
outToInPassway.setStreamCacheSize(getStreamCacheSize());
SimplePassway inToOutPassway = this.inToOutPassway = new SimplePassway();
inToOutPassway.setBelongControl(this);
inToOutPassway.setRecvSocket(this.sendSocket);
inToOutPassway.setSendSocket(this.recvSocket);
inToOutPassway.setStreamCacheSize(getStreamCacheSize());
outToInPassway.start();
inToOutPassway.start();
} catch (Exception e) {
log.error("socketPart [" + this.socketPartKey + "] 隧道建立异常", e);
this.stop();
return false;
}
return true;
}
@Override
public void noticeStop() {
CountDownLatch cancellLatch = this.cancelLatch;
cancellLatch.countDown();
if (cancellLatch.getCount() <= 0) {
this.stop();
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/Channel.java
================================================
package person.pluto.natcross2.channel;
import java.io.Closeable;
import java.nio.charset.Charset;
/**
* <p>
* 读写通道
* </p>
*
* @param <R> 读取返回的类型
* @param <W> 写入的类型
* @author Pluto
* @since 2020-01-03 15:40:28
*/
public interface Channel<R, W> extends Closeable {
/**
* 简单的读取方式
*
* @return
* @throws Exception
*/
R read() throws Exception;
/**
* 简单的写入
*
* @param value
* @throws Exception
*/
void write(W value) throws Exception;
/**
* 刷新
*
* @throws Exception
*/
void flush() throws Exception;
/**
* 简单的写入并刷新
*
* @param value
* @throws Exception
*/
void writeAndFlush(W value) throws Exception;
/**
* 设置交互编码
*
* @param charset
*/
default void setCharset(Charset charset) {
throw new UnsupportedOperationException("不支持的操作");
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/InteractiveChannel.java
================================================
package person.pluto.natcross2.channel;
import com.alibaba.fastjson.JSONObject;
import person.pluto.natcross2.model.InteractiveModel;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;
/**
* <p>
* InteractiveModel 模式读写
* </p>
*
* @author Pluto
* @since 2020-01-08 16:11:50
*/
public class InteractiveChannel extends SocketChannel<InteractiveModel, InteractiveModel> {
/**
* 实际通道
*/
private final JsonChannel channel;
public InteractiveChannel() {
this.channel = new JsonChannel();
}
public InteractiveChannel(Socket socket) throws IOException {
this.channel = new JsonChannel(socket);
}
@Override
public InteractiveModel read() throws Exception {
JSONObject read = this.channel.read();
return read.toJavaObject(InteractiveModel.class);
}
@Override
public void write(InteractiveModel value) throws Exception {
this.channel.write(value);
}
@Override
public void flush() throws Exception {
this.channel.flush();
}
@Override
public void writeAndFlush(InteractiveModel value) throws Exception {
this.channel.writeAndFlush(value);
}
/**
* 获取charset
*
* @return
*/
public Charset getCharset() {
return this.channel.getCharset();
}
@Override
public void setCharset(Charset charset) {
this.channel.setCharset(charset);
}
@Override
public Socket getSocket() {
return this.channel.getSocket();
}
@Override
public void setSocket(Socket socket) throws IOException {
this.channel.setSocket(socket);
}
@Override
public void closeSocket() throws IOException {
this.channel.closeSocket();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/JsonChannel.java
================================================
package person.pluto.natcross2.channel;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONObject;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;
/**
* <p>
* json方式读写
* </p>
*
* @author Pluto
* @since 2020-01-08 16:13:02
*/
public class JsonChannel extends SocketChannel<JSONObject, Object> {
/**
* 实际通道
*/
private final StringChannel channel;
public JsonChannel() {
this.channel = new StringChannel();
}
public JsonChannel(Socket socket) throws IOException {
this.channel = new StringChannel(socket);
}
@Override
public JSONObject read() throws Exception {
String read = this.channel.read();
return JSON.parseObject(read);
}
private String valueConvert(Object value) {
String string;
if (value instanceof JSONAware) {
string = ((JSONAware) value).toJSONString();
} else {
string = JSON.toJSONString(value);
}
return string;
}
@Override
public void write(Object value) throws Exception {
this.channel.write(this.valueConvert(value));
}
@Override
public void flush() throws Exception {
this.channel.flush();
}
@Override
public void writeAndFlush(Object value) throws Exception {
this.channel.writeAndFlush(this.valueConvert(value));
}
/**
* 获取charset
*
* @return
*/
public Charset getCharset() {
return this.channel.getCharset();
}
@Override
public void setCharset(Charset charset) {
this.channel.setCharset(charset);
}
@Override
public Socket getSocket() {
return this.channel.getSocket();
}
@Override
public void setSocket(Socket socket) throws IOException {
this.channel.setSocket(socket);
}
@Override
public void closeSocket() throws IOException {
this.channel.closeSocket();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/LengthChannel.java
================================================
package person.pluto.natcross2.channel;
import person.pluto.natcross2.utils.Tools;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.util.Objects;
import java.util.concurrent.locks.ReentrantLock;
/**
* <p>
* 长度限定读写通道
* </p>
*
* @author Pluto
* @since 2021-04-08 12:42:38
*/
public class LengthChannel extends SocketChannel<byte[], byte[]> {
private Socket socket;
private java.nio.channels.SocketChannel socketChannel;
private InputStream inputStream;
private OutputStream outputStream;
private final ReentrantLock readLock = new ReentrantLock(true);
private final ReentrantLock writerLock = new ReentrantLock(true);
private final byte[] lenBytes = new byte[4];
public LengthChannel() {
}
public LengthChannel(Socket socket) throws IOException {
this.setSocket(socket);
}
@Override
public byte[] read() throws Exception {
ReentrantLock readLock = this.readLock;
byte[] lenBytes = this.lenBytes;
readLock.lock();
try {
int offset = 0;
InputStream is = getInputSteam();
int len;
while (offset < lenBytes.length) {
len = is.read(lenBytes, offset, lenBytes.length - offset);
if (len < 0) {
// 如果-1,提前关闭了,又没有获得足够的数据,那么就抛出异常
throw new IOException("Insufficient byte length[" + lenBytes.length + "] when io closed");
}
offset += len;
}
int length = Tools.bytes2int(lenBytes);
offset = 0;
byte[] b = new byte[length];
while (offset < length) {
len = is.read(b, offset, length - offset);
if (len < 0) {
// 如果-1,提前关闭了,又没有获得足够的数据,那么就抛出异常
throw new IOException("Insufficient byte length[" + length + "] when io closed");
}
offset += len;
}
return b;
} finally {
readLock.unlock();
}
}
@Override
public void write(byte[] value) throws Exception {
ReentrantLock writerLock = this.writerLock;
writerLock.lock();
try {
java.nio.channels.SocketChannel socketChannel;
if (Objects.nonNull(socketChannel = this.socketChannel)) {
Tools.channelWrite(socketChannel, ByteBuffer.wrap(Tools.intToBytes(value.length)));
Tools.channelWrite(socketChannel, ByteBuffer.wrap(value));
} else {
OutputStream os = getOutputStream();
os.write(Tools.intToBytes(value.length));
os.write(value);
}
} finally {
writerLock.unlock();
}
}
@Override
public void flush() throws Exception {
ReentrantLock writerLock = this.writerLock;
writerLock.lock();
try {
getOutputStream().flush();
} finally {
writerLock.unlock();
}
}
@Override
public void writeAndFlush(byte[] value) throws Exception {
ReentrantLock writerLock = this.writerLock;
writerLock.lock();
try {
this.write(value);
this.flush();
} finally {
writerLock.unlock();
}
}
@Override
public Socket getSocket() {
return this.socket;
}
@Override
public void setSocket(Socket socket) throws IOException {
if (Objects.nonNull(this.socket)) {
throw new UnsupportedOperationException("socket cannot be set repeatedly");
}
this.socket = socket;
this.socketChannel = socket.getChannel();
this.inputStream = socket.getInputStream();
this.outputStream = socket.getOutputStream();
}
@Override
public void closeSocket() throws IOException {
this.socket.close();
}
/**
* 惰性获取输入流
*
* @return
* @throws IOException
* @author Pluto
* @since 2020-01-08 16:15:32
*/
private InputStream getInputSteam() throws IOException {
InputStream inputStream;
if ((inputStream = this.inputStream) == null) {
inputStream = this.inputStream = this.socket.getInputStream();
}
return inputStream;
}
/**
* 惰性获取输出流
*
* @return
* @throws IOException
* @author Pluto
* @since 2020-01-08 16:15:48
*/
private OutputStream getOutputStream() throws IOException {
OutputStream outputStream;
if ((outputStream = this.outputStream) == null) {
outputStream = this.outputStream = this.getSocket().getOutputStream();
}
return outputStream;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/SecretInteractiveChannel.java
================================================
package person.pluto.natcross2.channel;
import com.alibaba.fastjson.JSONObject;
import lombok.*;
import lombok.EqualsAndHashCode.Exclude;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.SecretInteractiveModel;
import person.pluto.natcross2.utils.AESUtil;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;
import java.security.Key;
/**
* <p>
* InteractiveModel 加密型通道,AES加密
* </p>
*
* @author Pluto
* @since 2020-01-08 16:16:12
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class SecretInteractiveChannel extends SocketChannel<InteractiveModel, InteractiveModel> {
@Exclude
@Setter(AccessLevel.NONE)
@Getter(AccessLevel.NONE)
private JsonChannel channel;
/**
* 签名混淆key
*/
private String tokenKey;
/**
* aes密钥
*/
private Key aesKey;
/**
* 超时时间,毫秒
*/
private Long overtimeMills = 5000L;
public SecretInteractiveChannel() {
this.channel = new JsonChannel();
}
public SecretInteractiveChannel(Socket socket) throws IOException {
this.channel = new JsonChannel(socket);
}
@Override
public InteractiveModel read() throws Exception {
JSONObject read = this.channel.read();
SecretInteractiveModel secretInteractiveModel = read.toJavaObject(SecretInteractiveModel.class);
if (Math.abs(System.currentTimeMillis() - secretInteractiveModel.getTimestamp()) > this.overtimeMills) {
throw new IllegalStateException("超时");
}
boolean checkAutograph = secretInteractiveModel.checkAutograph(this.tokenKey);
if (!checkAutograph) {
throw new IllegalStateException("签名错误");
}
secretInteractiveModel.decryptMsg(this.aesKey);
return secretInteractiveModel;
}
/**
* 统一数据转换方法,使 {@link #write(InteractiveModel)} 与
* {@link #writeAndFlush(InteractiveModel)} 转换结果保持一致
*
* @param value
* @return
* @throws Exception
*/
private Object valueConvert(InteractiveModel value) throws Exception {
SecretInteractiveModel secretInteractiveModel = new SecretInteractiveModel(value);
secretInteractiveModel.setCharset(this.getCharset().name());
secretInteractiveModel.fullMessage(this.aesKey, this.tokenKey);
return secretInteractiveModel;
}
@Override
public void write(InteractiveModel value) throws Exception {
this.channel.write(this.valueConvert(value));
}
@Override
public void flush() throws Exception {
this.channel.flush();
}
@Override
public void writeAndFlush(InteractiveModel value) throws Exception {
this.channel.writeAndFlush(this.valueConvert(value));
}
/**
* 获取charset
*
* @return
*/
public Charset getCharset() {
return this.channel.getCharset();
}
@Override
public void setCharset(Charset charset) {
this.channel.setCharset(charset);
}
@Override
public Socket getSocket() {
return this.channel.getSocket();
}
@Override
public void setSocket(Socket socket) throws IOException {
this.channel.setSocket(socket);
}
@Override
public void closeSocket() throws IOException {
this.channel.closeSocket();
}
/**
* 使用base64格式设置aes密钥
*
* @param aesKey
*/
public void setBaseAesKey(String aesKey) {
this.aesKey = AESUtil.createKeyByBase64(aesKey);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/SocketChannel.java
================================================
package person.pluto.natcross2.channel;
import java.io.IOException;
import java.net.Socket;
/**
* <p>
* socket通道
* </p>
*
* @param <R> 读取返回的类型
* @param <W> 写入的类型
* @author Pluto
* @since 2020-01-08 16:19:51
*/
public abstract class SocketChannel<R, W> implements Channel<R, W> {
/**
* 获取socket
*
* @return
*/
public abstract Socket getSocket();
/**
* 设置socket
*
* @param socket
* @throws IOException
*/
public abstract void setSocket(Socket socket) throws IOException;
/**
* 关闭socket
*
* @throws IOException
*/
public abstract void closeSocket() throws IOException;
@Override
public void close() throws IOException {
this.closeSocket();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/channel/StringChannel.java
================================================
package person.pluto.natcross2.channel;
import java.io.IOException;
import java.net.Socket;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* <p>
* 字符型通道
* </p>
*
* @author Pluto
* @since 2020-01-08 16:21:31
*/
public class StringChannel extends SocketChannel<String, String> {
private final LengthChannel channel;
private Charset charset = StandardCharsets.UTF_8;
public StringChannel() {
this.channel = new LengthChannel();
}
public StringChannel(Socket socket) throws IOException {
this.channel = new LengthChannel(socket);
}
@Override
public String read() throws Exception {
byte[] read = this.channel.read();
return new String(read, this.charset);
}
/**
* 统一数据转换方法
*
* @param value
* @return
*/
private byte[] valueConvert(String value) {
return value.getBytes(this.charset);
}
@Override
public void write(String value) throws Exception {
this.channel.write(this.valueConvert(value));
}
@Override
public void flush() throws Exception {
this.channel.flush();
}
@Override
public void writeAndFlush(String value) throws Exception {
this.channel.writeAndFlush(this.valueConvert(value));
}
/**
* 获取charset
*
* @return
*/
public Charset getCharset() {
return this.charset;
}
@Override
public void setCharset(Charset charset) {
this.charset = charset;
}
@Override
public Socket getSocket() {
return this.channel.getSocket();
}
@Override
public void setSocket(Socket socket) throws IOException {
this.channel.setSocket(socket);
}
@Override
public void closeSocket() throws IOException {
this.channel.closeSocket();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/ClientControlThread.java
================================================
package person.pluto.natcross2.clientside;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.IBelongControl;
import person.pluto.natcross2.api.socketpart.AbsSocketPart;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.clientside.config.IClientConfig;
import person.pluto.natcross2.clientside.heart.IClientHeartThread;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>
* 客户端控制服务
* </p>
*
* @author Pluto
* @since 2019-07-05 10:53:33
*/
@Slf4j
public final class ClientControlThread implements Runnable, IBelongControl {
private volatile Thread myThread = null;
private volatile boolean isAlive = false;
@Getter
private volatile boolean cancelled = false;
private final Map<String, AbsSocketPart> socketPartMap = new ConcurrentHashMap<>();
private final IClientConfig<?, ?> config;
private volatile IClientHeartThread clientHeartThread;
private volatile IClientAdapter<?, ?> clientAdapter;
public ClientControlThread(IClientConfig<?, ?> config) {
this.config = config;
}
/**
* 触发控制服务
*
* @return
* @throws Exception
*/
public boolean createControl() throws Exception {
this.stopClient();
if (this.clientAdapter == null) {
this.clientAdapter = this.config.newCreateControlAdapter(this);
}
boolean flag = this.clientAdapter.createControlChannel();
if (!flag) {
return false;
}
this.start();
return true;
}
@Override
public void run() {
while (this.isAlive) {
try {
// 使用适配器代理执行
this.clientAdapter.waitMessage();
} catch (Exception e) {
log.warn("client control [{}] to server is exception,will stopClient",
this.config.getListenServerPort());
this.stopClient();
}
}
}
@Override
public boolean stopSocketPart(String socketPartKey) {
log.debug("stopSocketPart[{}]", socketPartKey);
AbsSocketPart socketPart = this.socketPartMap.remove(socketPartKey);
if (socketPart == null) {
return false;
}
socketPart.cancel();
return true;
}
/**
* * 启动
*/
private void start() {
this.isAlive = true;
this.cancelled = false;
Thread myThread = this.myThread;
if (Objects.isNull(myThread) || !myThread.isAlive()) {
IClientHeartThread clientHeartThread = this.clientHeartThread;
if (Objects.isNull(clientHeartThread) || !clientHeartThread.isAlive()) {
clientHeartThread = this.clientHeartThread = this.config.newClientHeartThread(this);
if (Objects.nonNull(clientHeartThread)) {
clientHeartThread.start();
}
}
myThread = this.myThread = new Thread(this);
myThread.setName("client-" + this.formatInfo());
myThread.start();
}
}
/**
* * 停止客户端监听
*/
public void stopClient() {
this.isAlive = false;
Thread myThread = this.myThread;
if (myThread != null) {
this.myThread = null;
myThread.interrupt();
}
IClientAdapter<?, ?> clientAdapter = this.clientAdapter;
if (Objects.nonNull(clientAdapter)) {
try {
clientAdapter.close();
} catch (Exception e) {
// do nothing
}
}
}
/**
* 全部退出
*/
public void cancell() {
if (this.cancelled) {
return;
}
this.cancelled = true;
this.stopClient();
IClientHeartThread clientHeartThread;
if ((clientHeartThread = this.clientHeartThread) != null) {
this.clientHeartThread = null;
try {
clientHeartThread.cancel();
} catch (Exception e) {
// do no thing
}
}
IClientAdapter<?, ?> clientAdapter;
if ((clientAdapter = this.clientAdapter) != null) {
this.clientAdapter = null;
try {
clientAdapter.close();
} catch (Exception e) {
// do no thing
}
}
String[] array = this.socketPartMap.keySet().toArray(new String[0]);
for (String key : array) {
this.stopSocketPart(key);
}
}
/**
* 服务端监听的端口
*
* @return
*/
public Integer getListenServerPort() {
return this.config.getListenServerPort();
}
/**
* 重设目标端口
*
* @param destIp
* @param destPort
*/
public void setDestIpPort(String destIp, Integer destPort) {
this.config.setDestIpPort(destIp, destPort);
}
/**
* 检测是否还活着
*
* @return
*/
public boolean isAlive() {
return this.isAlive;
}
/**
* 发送心跳测试
*
* @throws Exception
*/
public void sendHeartTest() throws Exception {
// 无需判空,空指针异常也是异常
this.clientAdapter.sendHeartTest();
}
/**
* 获取服务端上次心跳测试成功时间
*
* @return
*/
public LocalDateTime obtainServerHeartLastRecvTime() {
return this.clientAdapter.obtainServerHeartLastRecvTime();
}
/**
* 设置隧道伙伴
*
* @param socketPartKey
* @param socketPart
*/
public void putSocketPart(String socketPartKey, AbsSocketPart socketPart) {
this.socketPartMap.put(socketPartKey, socketPart);
}
/**
* 格式化信息
*
* @return
*/
public String formatInfo() {
return String.valueOf(this.getListenServerPort());
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/adapter/IClientAdapter.java
================================================
package person.pluto.natcross2.clientside.adapter;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.model.interactive.ServerWaitModel;
import java.time.LocalDateTime;
/**
* <p>
* 客户端适配器
* </p>
*
* @param <R> 处理的对象
* @param <W> 可写的对象
* @author Pluto
* @since 2020-01-08 16:22:43
*/
public interface IClientAdapter<R, W> {
/**
* 请求建立控制器
*
* @return
* @throws Exception
*/
boolean createControlChannel() throws Exception;
/**
* 请求建立隧道连接
*
* @param serverWaitModel
* @return
*/
boolean clientConnect(ServerWaitModel serverWaitModel);
/**
* 等待消息处理
*
* @throws Exception
*/
void waitMessage() throws Exception;
/**
* 关闭
*
* @throws Exception
*/
void close() throws Exception;
/**
* 向控制器发送心跳
*
* @throws Exception
*/
void sendHeartTest() throws Exception;
/**
* 获取服务端最后心跳测试/回复的时间
*
* @return
*/
LocalDateTime obtainServerHeartLastRecvTime();
/**
* 重设服务端最后心跳测试/回复时间
* <p>
* 取 {@link LocalDateTime#now()} 为 设定值
*
* @return
*/
default LocalDateTime resetServerHeartLastRecvTime() {
return this.resetServerHeartLastRecvTime(LocalDateTime.now());
}
/**
* 重设服务端最后心跳测试/回复时间
*
* @param time 自有设置
* @return
*/
LocalDateTime resetServerHeartLastRecvTime(LocalDateTime time);
/**
* 获取socket读写通道
*
* @return
*/
SocketChannel<? extends R, ? super W> getSocketChannel();
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/adapter/InteractiveSimpleClientAdapter.java
================================================
package person.pluto.natcross2.clientside.adapter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import person.pluto.natcross2.api.socketpart.AbsSocketPart;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.clientside.config.IClientConfig;
import person.pluto.natcross2.clientside.handler.IClientHandler;
import person.pluto.natcross2.executor.NatcrossExecutor;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.NatcrossResultModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import person.pluto.natcross2.model.enumeration.NatcrossResultEnum;
import person.pluto.natcross2.model.interactive.ClientControlModel;
import person.pluto.natcross2.model.interactive.ServerWaitModel;
import java.io.IOException;
import java.net.Socket;
import java.time.LocalDateTime;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
/**
* <p>
* 基于InteractiveModel的客户端适配器
* </p>
*
* @author Pluto
* @since 2020-01-08 16:24:07
*/
@Slf4j
public class InteractiveSimpleClientAdapter implements IClientAdapter<InteractiveModel, InteractiveModel> {
/**
* 所属的客户端线程
*/
private final ClientControlThread clientControlThread;
/**
* 客户端设置
*/
private final IClientConfig<InteractiveModel, InteractiveModel> config;
/**
* 适配器通道
*/
private SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel;
private LocalDateTime serverHeartLastRecvTime = LocalDateTime.now();
/**
* 客户端消息接收处理链
*/
protected List<IClientHandler<? super InteractiveModel, ? extends InteractiveModel>> messageHandlerList = new LinkedList<>();
public InteractiveSimpleClientAdapter(ClientControlThread clientControlThread,
IClientConfig<InteractiveModel, InteractiveModel> clientConfig) {
this.clientControlThread = clientControlThread;
this.config = clientConfig;
}
/**
* 创建客户端通道
*
* @return
*/
protected SocketChannel<? extends InteractiveModel, ? super InteractiveModel> newClientChannel() {
return this.config.newClientChannel();
}
/**
* 向穿透目标socket
*
* @return
* @throws Exception
*/
protected Socket newDestSocket() throws Exception {
return this.config.newDestSocket();
}
/**
* 向服务端和暴露目标创建socketPart
*
* @return
*/
protected AbsSocketPart newSocketPart() {
return this.config.newSocketPart(this.clientControlThread);
}
@Override
public boolean createControlChannel() throws Exception {
SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel = this.newClientChannel();
if (socketChannel == null) {
log.error("向服务端[{}:{}]建立控制通道失败", this.config.getClientServiceIp(),
this.config.getClientServicePort());
return false;
}
InteractiveModel interactiveModel = InteractiveModel.of(InteractiveTypeEnum.CLIENT_CONTROL,
new ClientControlModel(this.config.getListenServerPort()));
socketChannel.writeAndFlush(interactiveModel);
InteractiveModel recv = socketChannel.read();
log.info("建立控制端口回复:{}", recv);
NatcrossResultModel javaObject = recv.getData().toJavaObject(NatcrossResultModel.class);
if (StringUtils.equals(NatcrossResultEnum.SUCCESS.getCode(), javaObject.getRetCod())) {
// 使用相同的
this.socketChannel = socketChannel;
this.resetServerHeartLastRecvTime();
return true;
}
return false;
}
/**
* 建立连接
*
* @param serverWaitModel
*/
@Override
public boolean clientConnect(ServerWaitModel serverWaitModel) {
// 首先向暴露目标建立socket
Socket destSocket;
try {
destSocket = this.newDestSocket();
} catch (Exception e) {
log.error("向目标建立连接失败 {}:{}", this.config.getDestIp(), this.config.getDestPort());
return false;
}
SocketChannel<? extends InteractiveModel, ? super InteractiveModel> passwayClientChannel = null;
try {
// 向服务端请求建立隧道
passwayClientChannel = this.newClientChannel();
InteractiveModel model = InteractiveModel.of(InteractiveTypeEnum.CLIENT_CONNECT,
new ServerWaitModel(serverWaitModel.getSocketPartKey()));
passwayClientChannel.writeAndFlush(model);
InteractiveModel recv = passwayClientChannel.read();
log.info("建立隧道回复:{}", recv);
NatcrossResultModel javaObject = recv.getData().toJavaObject(NatcrossResultModel.class);
if (!StringUtils.equals(NatcrossResultEnum.SUCCESS.getCode(), javaObject.getRetCod())) {
throw new RuntimeException("绑定失败");
}
} catch (Exception e) {
log.error(
"打通隧道发生异常 " + this.config.getClientServiceIp() + ":" + this.config.getClientServicePort() +
"<->" + this.config.getDestIp() + ":" + this.config.getDestPort(), e);
try {
destSocket.close();
} catch (IOException e1) {
// do nothing
}
if (passwayClientChannel != null) {
try {
passwayClientChannel.closeSocket();
} catch (IOException e1) {
// do nothing
}
}
return false;
}
// 将两个socket建立伙伴关系
AbsSocketPart socketPart = this.newSocketPart();
socketPart.setSocketPartKey(serverWaitModel.getSocketPartKey());
socketPart.setSendSocket(passwayClientChannel.getSocket());
socketPart.setRecvSocket(destSocket);
// 尝试打通隧道
boolean createPassWay = socketPart.createPassWay();
if (!createPassWay) {
socketPart.cancel();
return false;
}
// 将socket伙伴放入客户端线程进行统一管理
this.clientControlThread.putSocketPart(serverWaitModel.getSocketPartKey(), socketPart);
return true;
}
@Override
public void waitMessage() throws Exception {
InteractiveModel read = this.socketChannel.read();
// 只要有有效信息推送过来,则重置心跳时间
this.resetServerHeartLastRecvTime();
NatcrossExecutor.executeClientMessageProc(() -> this.procMethod(read));
}
/**
* 消息处理方法
*
* @param recvInteractiveModel
*/
protected void procMethod(InteractiveModel recvInteractiveModel) {
log.info("接收到新的指令: {}", recvInteractiveModel);
try {
boolean proceedFlag = false;
for (IClientHandler<? super InteractiveModel, ? extends InteractiveModel> handler : this.messageHandlerList) {
proceedFlag = handler.proc(recvInteractiveModel, this);
if (proceedFlag) {
break;
}
}
if (!proceedFlag) {
log.warn("无处理方法的信息:[{}]", recvInteractiveModel);
InteractiveModel result = InteractiveModel.of(recvInteractiveModel.getInteractiveSeq(),
InteractiveTypeEnum.COMMON_REPLY, NatcrossResultEnum.UNKNOWN_INTERACTIVE_TYPE.toResultModel());
this.getSocketChannel().writeAndFlush(result);
}
} catch (Exception e) {
// 只记录,其他的交给心跳之类的去把控
log.error("读取或写入异常", e);
}
}
@Override
public SocketChannel<? extends InteractiveModel, ? super InteractiveModel> getSocketChannel() {
return this.socketChannel;
}
/**
* 添加消息处理器
*
* @param handler
* @return
*/
public InteractiveSimpleClientAdapter addMessageHandler(
IClientHandler<? super InteractiveModel, ? extends InteractiveModel> handler) {
this.messageHandlerList.add(handler);
return this;
}
@Override
public void close() throws Exception {
SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel = this.socketChannel;
if (Objects.nonNull(socketChannel)) {
this.socketChannel = null;
socketChannel.closeSocket();
}
}
@Override
public void sendHeartTest() throws Exception {
InteractiveModel interactiveModel = InteractiveModel.of(InteractiveTypeEnum.HEART_TEST, null);
this.socketChannel.writeAndFlush(interactiveModel);
}
@Override
public LocalDateTime obtainServerHeartLastRecvTime() {
return this.serverHeartLastRecvTime;
}
@Override
public LocalDateTime resetServerHeartLastRecvTime(LocalDateTime time) {
LocalDateTime tempTime = this.serverHeartLastRecvTime;
this.serverHeartLastRecvTime = time;
return tempTime;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/config/AllSecretInteractiveClientConfig.java
================================================
package person.pluto.natcross2.clientside.config;
import lombok.Getter;
import lombok.Setter;
import person.pluto.natcross2.api.secret.AESSecret;
import person.pluto.natcross2.api.socketpart.AbsSocketPart;
import person.pluto.natcross2.api.socketpart.SecretSocketPart;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.utils.AESUtil;
import java.security.Key;
/**
* <p>
* 交互及隧道都加密
* </p>
*
* @author Pluto
* @since 2020-01-08 15:01:44
*/
public class AllSecretInteractiveClientConfig extends SecretInteractiveClientConfig {
@Setter
@Getter
private Key passwayKey;
@Override
public AbsSocketPart newSocketPart(ClientControlThread clientControlThread) {
AESSecret secret = new AESSecret();
secret.setAesKey(this.passwayKey);
SecretSocketPart secretSocketPart = new SecretSocketPart(clientControlThread);
secretSocketPart.setSecret(secret);
return secretSocketPart;
}
/**
* base64格式设置密钥
*
* @param key
*/
public void setBasePasswayKey(String key) {
this.passwayKey = AESUtil.createKeyByBase64(key);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/config/HttpRouteClientConfig.java
================================================
package person.pluto.natcross2.clientside.config;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import person.pluto.natcross2.api.IHttpRouting;
import person.pluto.natcross2.api.socketpart.AbsSocketPart;
import person.pluto.natcross2.api.socketpart.HttpRouteSocketPart;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.clientside.adapter.InteractiveSimpleClientAdapter;
import person.pluto.natcross2.clientside.handler.CommonReplyHandler;
import person.pluto.natcross2.clientside.handler.ServerHeartHandler;
import person.pluto.natcross2.clientside.handler.ServerWaitClientHandler;
import person.pluto.natcross2.clientside.heart.IClientHeartThread;
import person.pluto.natcross2.model.HttpRoute;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.utils.Assert;
import java.net.Socket;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.locks.StampedLock;
/**
* <p>
* http路由客户端配置
* </p>
*
* @author Pluto
* @since 2020-04-24 10:09:46
*/
@Slf4j
public class HttpRouteClientConfig extends InteractiveClientConfig implements IHttpRouting {
private final InteractiveClientConfig baseConfig;
@Getter
private HttpRoute masterRoute = null;
// 不可对routeMap进行修改,只得以重新赋值方式重设
private Map<String, HttpRoute> routeMap = Collections.emptyMap();
private final StampedLock routeLock = new StampedLock();
public HttpRouteClientConfig() {
this.baseConfig = new InteractiveClientConfig();
}
public HttpRouteClientConfig(InteractiveClientConfig baseConfig) {
this.baseConfig = baseConfig;
}
/**
* 预设置
*
* @param masterRoute
* @param routeMap
*/
public void presetRoute(HttpRoute masterRoute, LinkedHashMap<String, HttpRoute> routeMap) {
Objects.requireNonNull(masterRoute, "主路由不得为空");
Objects.requireNonNull(routeMap, "路由表不得为null");
LinkedHashMap<String, HttpRoute> routeMapTemp = new LinkedHashMap<>(routeMap);
StampedLock routeLock = this.routeLock;
long stamp = routeLock.writeLock();
try {
this.masterRoute = masterRoute;
this.routeMap = routeMapTemp;
} finally {
routeLock.unlockWrite(stamp);
}
}
/**
* 获取路由表
*
* @return {@link Collections#unmodifiableMap(Map)} 不得对对象进行修改
*/
public Map<String, HttpRoute> getRouteMap() {
return Collections.unmodifiableMap(this.routeMap);
}
/**
* 增加路由
*
* @param httpRoutes
*/
public void addRoute(HttpRoute... httpRoutes) {
if (httpRoutes == null || httpRoutes.length < 1) {
return;
}
StampedLock routeLock = this.routeLock;
long stamp = routeLock.writeLock();
try {
if (Objects.isNull(this.masterRoute)) {
this.masterRoute = httpRoutes[0];
}
LinkedHashMap<String, HttpRoute> routeMap = new LinkedHashMap<>(this.routeMap);
for (HttpRoute model : httpRoutes) {
routeMap.put(model.getHost(), model);
if (model.isMaster()) {
this.masterRoute = model;
}
}
this.routeMap = routeMap;
} finally {
routeLock.unlockWrite(stamp);
}
}
/**
* 清理路由
*
* @param hosts
*/
public void clearRoute(String... hosts) {
if (Objects.isNull(hosts) || hosts.length < 1) {
return;
}
StampedLock routeLock = this.routeLock;
long stamp = routeLock.writeLock();
try {
LinkedHashMap<String, HttpRoute> routeMap = new LinkedHashMap<>(this.routeMap);
HttpRoute masterRoute = this.masterRoute;
String masterRouteHost = masterRoute.getHost();
for (String host : hosts) {
routeMap.remove(host);
if (StringUtils.equals(masterRouteHost, host)) {
masterRoute = null;
// 减少string比较复杂度
masterRouteHost = null;
}
}
if (Objects.isNull(masterRoute)) {
Iterator<HttpRoute> iterator = routeMap.values().iterator();
if (iterator.hasNext()) {
// 先将第一个设置为主路由,再遍历所有,如果是主标志则设置为主,以最后的主为准
masterRoute = iterator.next();
while (iterator.hasNext()) {
HttpRoute model = iterator.next();
if (model.isMaster()) {
// 因使用的LinkedHashMap,就是让其符合初期定义,将后续加入有isMaster标志的路由设置为masterRoute
masterRoute = model;
}
}
} else {
log.warn("{}:{} 路由是空的,若需要重新设置,请使用preset进行设置", this.getClientServiceIp(),
this.getClientServicePort());
}
}
this.masterRoute = masterRoute;
this.routeMap = routeMap;
} finally {
routeLock.unlockWrite(stamp);
}
}
@Override
public void setDestIpPort(String destIp, Integer destPort) {
// do nothing
}
@Override
public IClientHeartThread newClientHeartThread(ClientControlThread clientControlThread) {
return this.baseConfig.newClientHeartThread(clientControlThread);
}
@Override
public IClientAdapter<InteractiveModel, InteractiveModel> newCreateControlAdapter(
ClientControlThread clientControlThread) {
InteractiveSimpleClientAdapter simpleClientAdapter = new InteractiveSimpleClientAdapter(clientControlThread,
this);
simpleClientAdapter.addMessageHandler(CommonReplyHandler.INSTANCE);
simpleClientAdapter.addMessageHandler(ServerHeartHandler.INSTANCE);
simpleClientAdapter.addMessageHandler(ServerWaitClientHandler.INSTANCE);
return simpleClientAdapter;
}
@Override
public SocketChannel<? extends InteractiveModel, ? super InteractiveModel> newClientChannel() {
return this.baseConfig.newClientChannel();
}
@Override
public AbsSocketPart newSocketPart(ClientControlThread clientControlThread) {
HttpRouteSocketPart httpRouteSocketPart = new HttpRouteSocketPart(clientControlThread, this);
httpRouteSocketPart.setStreamCacheSize(this.getStreamCacheSize());
return httpRouteSocketPart;
}
@Override
public HttpRoute pickEffectiveRoute(String host) {
StampedLock routeLock = this.routeLock;
long stamp = routeLock.tryOptimisticRead();
Map<String, HttpRoute> routeMap = this.routeMap;
HttpRoute masterRoute = this.masterRoute;
if (!routeLock.validate(stamp)) {
stamp = routeLock.readLock();
try {
routeMap = this.routeMap;
masterRoute = this.masterRoute;
} finally {
routeLock.unlockRead(stamp);
}
}
HttpRoute httpRoute = routeMap.get(host);
if (Objects.isNull(httpRoute)) {
httpRoute = masterRoute;
}
Assert.state(Objects.nonNull(httpRoute), "未能获取有效的路由");
return httpRoute;
}
@Override
public Socket newDestSocket() throws Exception {
java.nio.channels.SocketChannel openSocketChannel = SelectorProvider.provider().openSocketChannel();
return openSocketChannel.socket();
// return new Socket();
}
@Override
public String getClientServiceIp() {
return this.baseConfig.getClientServiceIp();
}
@Override
public void setClientServiceIp(String clientServiceIp) {
this.baseConfig.setClientServiceIp(clientServiceIp);
}
@Override
public Integer getClientServicePort() {
return this.baseConfig.getClientServicePort();
}
@Override
public void setClientServicePort(Integer clientServicePort) {
this.baseConfig.setClientServicePort(clientServicePort);
}
@Override
public Integer getListenServerPort() {
return this.baseConfig.getListenServerPort();
}
@Override
public void setListenServerPort(Integer listenServerPort) {
this.baseConfig.setListenServerPort(listenServerPort);
}
@Override
public String getDestIp() {
return null;
}
@Override
public void setDestIp(String destIp) {
// do nothing
}
@Override
public Integer getDestPort() {
return null;
}
@Override
public void setDestPort(Integer destPort) {
// do nothing
}
@Override
public Charset getCharset() {
return this.baseConfig.getCharset();
}
@Override
public void setCharset(Charset charset) {
this.baseConfig.setCharset(charset);
}
@Override
public int getStreamCacheSize() {
return this.baseConfig.getStreamCacheSize();
}
@Override
public void setStreamCacheSize(int streamCacheSize) {
this.baseConfig.setStreamCacheSize(streamCacheSize);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/config/IClientConfig.java
================================================
package person.pluto.natcross2.clientside.config;
import person.pluto.natcross2.api.socketpart.AbsSocketPart;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.clientside.heart.IClientHeartThread;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.spi.SelectorProvider;
/**
* <p>
* 客户端配置接口
* </p>
*
* @param <R> 通道读取的类型
* @param <W> 通道写入的类型
* @author Pluto
* @since 2020-01-08 16:30:04
*/
public interface IClientConfig<R, W> {
/**
* 获取服务端IP
*
* @return
*/
String getClientServiceIp();
/**
* 获取服务端端口
*
* @return
*/
Integer getClientServicePort();
/**
* 对应的监听端口
*
* @return
*/
Integer getListenServerPort();
/**
* 目标IP
*
* @return
*/
String getDestIp();
/**
* 目标端口
*
* @return
*/
Integer getDestPort();
/**
* 设置目标IP
*
* @param destIp
*/
void setDestIpPort(String destIp, Integer destPort);
/**
* 新建心跳测试线程
*
* @param clientControlThread
* @return
*/
IClientHeartThread newClientHeartThread(ClientControlThread clientControlThread);
/**
* 新建适配器
*
* @param clientControlThread
* @return
*/
IClientAdapter<R, W> newCreateControlAdapter(ClientControlThread clientControlThread);
/**
* 新建与服务端的交互线程
*
* @return
*/
SocketChannel<? extends R, ? super W> newClientChannel();
/**
* 创建新的socketPart
*
* @return
*/
AbsSocketPart newSocketPart(ClientControlThread clientControlThread);
/**
* 创建目标端口
*
* @return
* @throws Exception
*/
default Socket newDestSocket() throws Exception {
java.nio.channels.SocketChannel openSocketChannel = SelectorProvider.provider().openSocketChannel();
openSocketChannel.connect(new InetSocketAddress(this.getDestIp(), this.getDestPort()));
return openSocketChannel.socket();
// return new Socket(this.getDestIp(), this.getDestPort());
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/config/InteractiveClientConfig.java
================================================
package person.pluto.natcross2.clientside.config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.api.socketpart.AbsSocketPart;
import person.pluto.natcross2.api.socketpart.SimpleSocketPart;
import person.pluto.natcross2.channel.InteractiveChannel;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.clientside.adapter.InteractiveSimpleClientAdapter;
import person.pluto.natcross2.clientside.handler.CommonReplyHandler;
import person.pluto.natcross2.clientside.handler.ServerHeartHandler;
import person.pluto.natcross2.clientside.handler.ServerWaitClientHandler;
import person.pluto.natcross2.clientside.heart.ClientHeartThread;
import person.pluto.natcross2.clientside.heart.IClientHeartThread;
import person.pluto.natcross2.model.InteractiveModel;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* <p>
* 简单的以InteractiveModel为交互模型的配置
* </p>
*
* @author Pluto
* @since 2020-01-08 16:30:53
*/
@Slf4j
@Data
public class InteractiveClientConfig implements IClientConfig<InteractiveModel, InteractiveModel> {
private String clientServiceIp;
private Integer clientServicePort;
private Integer listenServerPort;
private String destIp;
private Integer destPort;
private Charset charset = StandardCharsets.UTF_8;
private int streamCacheSize = 8196;
/**
* 心跳检测间隔(s)
*/
private long heartIntervalSeconds = 10L;
/**
* 尝试重连次数,若超过则中断链接
*/
private int tryRecipientCount = 10;
@Override
public void setDestIpPort(String destIp, Integer destPort) {
this.destIp = destIp;
this.destPort = destPort;
}
@Override
public IClientHeartThread newClientHeartThread(ClientControlThread clientControlThread) {
ClientHeartThread clientHeartThread = new ClientHeartThread(clientControlThread);
clientHeartThread.setHeartIntervalSeconds(this.heartIntervalSeconds);
clientHeartThread.setTryRecipientCount(this.tryRecipientCount);
return clientHeartThread;
}
@Override
public IClientAdapter<InteractiveModel, InteractiveModel> newCreateControlAdapter(
ClientControlThread clientControlThread) {
InteractiveSimpleClientAdapter simpleClientAdapter = new InteractiveSimpleClientAdapter(clientControlThread,
this);
simpleClientAdapter.addMessageHandler(CommonReplyHandler.INSTANCE);
simpleClientAdapter.addMessageHandler(ServerHeartHandler.INSTANCE);
simpleClientAdapter.addMessageHandler(ServerWaitClientHandler.INSTANCE);
return simpleClientAdapter;
}
@Override
public SocketChannel<? extends InteractiveModel, ? super InteractiveModel> newClientChannel() {
InteractiveChannel interactiveChannel = new InteractiveChannel();
try {
java.nio.channels.SocketChannel openSocketChannel = SelectorProvider.provider().openSocketChannel();
openSocketChannel.connect(new InetSocketAddress(this.getClientServiceIp(), this.getClientServicePort()));
Socket socket = openSocketChannel.socket();
// Socket socket = new Socket(this.getClientServiceIp(), this.getClientServicePort());
interactiveChannel.setSocket(socket);
} catch (IOException e) {
log.debug("connect client service exception", e);
throw new RuntimeException(e);
}
interactiveChannel.setCharset(this.charset);
return interactiveChannel;
}
@Override
public AbsSocketPart newSocketPart(ClientControlThread clientControlThread) {
SimpleSocketPart simpleSocketPart = new SimpleSocketPart(clientControlThread);
simpleSocketPart.setStreamCacheSize(this.getStreamCacheSize());
return simpleSocketPart;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/config/SecretInteractiveClientConfig.java
================================================
package person.pluto.natcross2.clientside.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.channel.SecretInteractiveChannel;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.utils.AESUtil;
import java.io.IOException;
import java.net.Socket;
import java.security.Key;
/**
* <p>
* 交互加密的配置方案(AES加密)
* </p>
*
* @author Pluto
* @since 2020-01-08 16:32:18
*/
@Slf4j
@Data
@EqualsAndHashCode(callSuper = false)
public class SecretInteractiveClientConfig extends InteractiveClientConfig {
private String tokenKey;
private Key aesKey;
@Override
public SocketChannel<? extends InteractiveModel, ? super InteractiveModel> newClientChannel() {
SecretInteractiveChannel channel = new SecretInteractiveChannel();
channel.setCharset(this.getCharset());
channel.setTokenKey(this.tokenKey);
channel.setAesKey(this.aesKey);
try {
Socket socket = new Socket(this.getClientServiceIp(), this.getClientServicePort());
channel.setSocket(socket);
} catch (IOException e) {
log.debug("connect client service exception", e);
throw new RuntimeException(e);
}
return channel;
}
/**
* 设置交互密钥
*
* @param aesKey
*/
public void setBaseAesKey(String aesKey) {
this.aesKey = AESUtil.createKeyByBase64(aesKey);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/handler/CommonReplyHandler.java
================================================
package person.pluto.natcross2.clientside.handler;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import java.util.Objects;
/**
* <p>
* 统一回复 处理器
* </p>
*
* @author Pluto
* @since 2020-04-15 13:02:09
*/
@Slf4j
public class CommonReplyHandler implements IClientHandler<InteractiveModel, InteractiveModel> {
public static final CommonReplyHandler INSTANCE = new CommonReplyHandler();
@Getter
@Setter
private IClientHandler<InteractiveModel, InteractiveModel> handler;
@Override
public boolean proc(InteractiveModel model,
IClientAdapter<? extends InteractiveModel, ? super InteractiveModel> clientAdapter) throws Exception {
InteractiveTypeEnum interactiveTypeEnum = InteractiveTypeEnum.getEnumByName(model.getInteractiveType());
if (!InteractiveTypeEnum.COMMON_REPLY.equals(interactiveTypeEnum)) {
return false;
}
IClientHandler<InteractiveModel, InteractiveModel> handler;
if (Objects.isNull(handler = this.handler)) {
log.info("common reply: {}", model);
return true;
}
return handler.proc(model, clientAdapter);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/handler/IClientHandler.java
================================================
package person.pluto.natcross2.clientside.handler;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
/**
* <p>
* 接收处理器
* </p>
*
* @author Pluto
* @since 2020-04-15 11:13:20
*/
public interface IClientHandler<R, W> {
/**
* 执行方法
*
* @param model
* @param clientAdapter
* @return
* @throws Exception
*/
boolean proc(R model, IClientAdapter<? extends R, ? super W> clientAdapter) throws Exception;
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/handler/ServerHeartHandler.java
================================================
package person.pluto.natcross2.clientside.handler;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import person.pluto.natcross2.model.enumeration.NatcrossResultEnum;
/**
* <p>
* 心跳检测
* </p>
*
* @author Pluto
* @since 2020-04-15 13:02:09
*/
@Slf4j
public class ServerHeartHandler implements IClientHandler<InteractiveModel, InteractiveModel> {
public static final ServerHeartHandler INSTANCE = new ServerHeartHandler();
@Override
public boolean proc(InteractiveModel model,
IClientAdapter<? extends InteractiveModel, ? super InteractiveModel> clientAdapter) throws Exception {
InteractiveTypeEnum interactiveTypeEnum = InteractiveTypeEnum.getEnumByName(model.getInteractiveType());
if (InteractiveTypeEnum.HEART_TEST.equals(interactiveTypeEnum)) {
InteractiveModel sendModel = InteractiveModel.of(model.getInteractiveSeq(), InteractiveTypeEnum.HEART_TEST_REPLY,
NatcrossResultEnum.SUCCESS.toResultModel());
clientAdapter.getSocketChannel().writeAndFlush(sendModel);
return true;
} else if (InteractiveTypeEnum.HEART_TEST_REPLY.equals(interactiveTypeEnum)) {
log.debug("HEART_TEST_REPLY ignore");
return true;
}
return false;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/handler/ServerWaitClientHandler.java
================================================
package person.pluto.natcross2.clientside.handler;
import person.pluto.natcross2.clientside.adapter.IClientAdapter;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import person.pluto.natcross2.model.interactive.ServerWaitModel;
/**
* <p>
* 心跳检测
* </p>
*
* @author Pluto
* @since 2020-04-15 13:02:09
*/
public class ServerWaitClientHandler implements IClientHandler<InteractiveModel, InteractiveModel> {
public static final ServerWaitClientHandler INSTANCE = new ServerWaitClientHandler();
@Override
public boolean proc(InteractiveModel model,
IClientAdapter<? extends InteractiveModel, ? super InteractiveModel> clientAdapter) throws Exception {
InteractiveTypeEnum interactiveTypeEnum = InteractiveTypeEnum.getEnumByName(model.getInteractiveType());
if (!InteractiveTypeEnum.SERVER_WAIT_CLIENT.equals(interactiveTypeEnum)) {
return false;
}
ServerWaitModel serverWaitModel = model.getData().toJavaObject(ServerWaitModel.class);
clientAdapter.clientConnect(serverWaitModel);
return true;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/heart/ClientHeartThread.java
================================================
package person.pluto.natcross2.clientside.heart;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.clientside.ClientControlThread;
import person.pluto.natcross2.executor.NatcrossExecutor;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
/**
* <p>
* 心跳检测线程
* </p>
*
* @author Pluto
* @since 2021-04-25 17:53:44
*/
@Slf4j
public class ClientHeartThread implements IClientHeartThread, Runnable {
private final ClientControlThread clientControlThread;
private volatile boolean isAlive = false;
@Setter
@Getter
private long heartIntervalSeconds = 10L;
@Setter
@Getter
private int tryRecipientCount = 10;
@Setter
@Getter
private long serverHeartMaxMissDurationSeconds = 25L;
private volatile ScheduledFuture<?> scheduledFuture;
private int failCount = 0;
public ClientHeartThread(ClientControlThread clientControlThread) {
this.clientControlThread = clientControlThread;
}
/**
* 检查服务端是否心跳超时
*
* @param clientControlThread
* @param timeoutSeconds
* @return
*/
private boolean lastServerHeartEffective(ClientControlThread clientControlThread, Long timeoutSeconds) {
return Duration.between(clientControlThread.obtainServerHeartLastRecvTime(), LocalDateTime.now())
.get(ChronoUnit.SECONDS) < timeoutSeconds;
}
@Override
public void run() {
ClientControlThread clientControlThread = this.clientControlThread;
if (clientControlThread.isCancelled() || !this.isAlive()) {
this.cancel();
}
log.debug("send client heart data to {}", clientControlThread.getListenServerPort());
try {
// 如果服务端心跳超时则直接判定为失败,否则进行心跳检查
if (this.lastServerHeartEffective(clientControlThread, this.serverHeartMaxMissDurationSeconds)) {
clientControlThread.sendHeartTest();
this.failCount = 0;
return;
}
} catch (Exception e) {
log.warn("{} 心跳异常", clientControlThread.getListenServerPort());
clientControlThread.stopClient();
}
if (!this.isAlive) {
return;
}
this.failCount++;
boolean createControl = false, logFlag = true;
try {
createControl = clientControlThread.createControl();
} catch (Exception reClientException) {
log.warn("重新建立连接" + clientControlThread.getListenServerPort() + "失败第 " + this.failCount + " 次",
reClientException);
logFlag = false;
}
if (createControl) {
log.info("重新建立连接 {} 成功,在第 {} 次", clientControlThread.getListenServerPort(), this.failCount);
this.failCount = 0;
return;
}
if (logFlag) {
log.warn("重新建立连接" + clientControlThread.getListenServerPort() + "失败第 " + this.failCount + " 次");
}
if (this.failCount >= this.tryRecipientCount) {
log.error("尝试重新连接 {} 超过最大次数,关闭客户端", clientControlThread.getListenServerPort());
clientControlThread.cancell();
this.cancel();
}
}
@Override
public synchronized void start() {
this.isAlive = true;
ScheduledFuture<?> scheduledFuture = this.scheduledFuture;
if (Objects.isNull(scheduledFuture) || scheduledFuture.isCancelled()) {
this.failCount = 0;
this.scheduledFuture = NatcrossExecutor.scheduledClientHeart(this, this.heartIntervalSeconds);
}
}
@Override
public void cancel() {
if (!this.isAlive) {
return;
}
this.isAlive = false;
ScheduledFuture<?> scheduledFuture = this.scheduledFuture;
if (Objects.nonNull(scheduledFuture) && !scheduledFuture.isCancelled()) {
this.scheduledFuture = null;
scheduledFuture.cancel(false);
}
}
@Override
public boolean isAlive() {
return this.isAlive;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/clientside/heart/IClientHeartThread.java
================================================
package person.pluto.natcross2.clientside.heart;
/**
* <p>
* 心跳测试线程
* </p>
*
* @author Pluto
* @since 2020-01-08 16:33:03
*/
public interface IClientHeartThread {
/**
* 是否还活着
*
* @return
*/
boolean isAlive();
/**
* 退出
*/
void cancel();
/**
* 开始
*/
void start();
}
================================================
FILE: src/main/java/person/pluto/natcross2/common/CommonFormat.java
================================================
package person.pluto.natcross2.common;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.RandomStringUtils;
import java.text.DecimalFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* <p>
* 公用的格式化类
* </p>
*
* @author Pluto
* @since 2019-07-05 13:35:04
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class CommonFormat {
/**
* 获取socket匹配对key
*
* @param listenPort
* @return
*/
public static String generateSocketPartKey(Integer listenPort) {
DecimalFormat fiveLenFormat = new DecimalFormat("00000");
String dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
String randomNum = RandomStringUtils.randomNumeric(4);
return String.join("-", "SK", fiveLenFormat.format(listenPort), dateTime, randomNum);
}
/**
* 根据socketPartKey获取端口号
*
* @param socketPartKey
* @return
*/
public static Integer getSocketPortByPartKey(String socketPartKey) {
String[] split = socketPartKey.split("-");
return Integer.valueOf(split[1]);
}
/**
* 获取交互流水号
*
* @return
*/
public static String generateInteractiveSeq() {
String dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS"));
String randomNum = RandomStringUtils.randomNumeric(4);
return String.join("-", "IS", dateTime, randomNum);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/common/Optional.java
================================================
package person.pluto.natcross2.common;
/**
* <p>
* 操作对象,主要是让值能够通过引用进行传递
* </p>
*
* @param <T> 存放的类型
* @author Pluto
* @since 2020-01-08 16:35:46
*/
public class Optional<T> {
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
public Optional(T value) {
this.value = value;
}
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/executor/IExecutor.java
================================================
package person.pluto.natcross2.executor;
import java.util.concurrent.ScheduledFuture;
/**
* <p>
* 执行器实现
* </p>
*
* @author Pluto
* @since 2021-04-08 14:38:23
*/
public interface IExecutor {
/**
* 关闭
*
* @throws Exception
*/
void shutdown();
/**
* 默认执行方法
*
* @param runnable
*/
void execute(Runnable runnable);
/**
* 服务监听线程任务执行器
* <p>
* For {@link person.pluto.natcross2.serverside.listen.ServerListenThread}
*
* @param runnable
*/
default void executeServerListenAccept(Runnable runnable) {
execute(runnable);
}
/**
* 客户端监听线程任务执行器
* <p>
* For {@link person.pluto.natcross2.serverside.client.ClientServiceThread}
*
* @param runnable
*/
default void executeClientServiceAccept(Runnable runnable) {
execute(runnable);
}
/**
* 客户端消息处理任务执行器
* <p>
* For
* {@link person.pluto.natcross2.clientside.adapter.InteractiveSimpleClientAdapter#waitMessage()}
*
* @param runnable
*/
default void executeClientMessageProc(Runnable runnable) {
execute(runnable);
}
/**
* 隧道线程执行器
* <p>
* For {@link person.pluto.natcross2.api.passway}
*
* @param runnable
*/
default void executePassway(Runnable runnable) {
execute(runnable);
}
/**
* nio事件任务执行器
* <p>
* For {@link person.pluto.natcross2.nio.NioHallows#run()}
*
* @param runnable
*/
default void executeNioAction(Runnable runnable) {
execute(runnable);
}
/**
* 心跳检测定时循环任务执行
*
* @param runnable
* @param delaySeconds
*/
ScheduledFuture<?> scheduledClientHeart(Runnable runnable, long delaySeconds);
/**
* 服务监听清理无效socket对
*
* @param runnable
* @param delaySeconds
* @return
*/
ScheduledFuture<?> scheduledClearInvalidSocketPart(Runnable runnable, long delaySeconds);
}
================================================
FILE: src/main/java/person/pluto/natcross2/executor/NatcrossExecutor.java
================================================
package person.pluto.natcross2.executor;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import java.util.Objects;
import java.util.concurrent.ScheduledFuture;
/**
* <p>
* 线程执行器
* <p>
* 主要是为了统一位置,方便管理
* </p>
*
* @author Pluto
* @since 2021-04-08 14:37:52
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class NatcrossExecutor {
private static volatile IExecutor INSTANCE = new SimpleExecutor();
public static void shutdown() {
INSTANCE.shutdown();
}
/**
* 重设执行器
* <p>
* 会将旧的执行器进行执行 {@link IExecutor#shutdown()} 方法,建议重设执行器的操作在初始化程序时
*
* @param executor
*/
public static void resetExecutor(IExecutor executor) {
IExecutor oldExecutor = INSTANCE;
if (Objects.nonNull(oldExecutor)) {
try {
oldExecutor.shutdown();
} catch (Exception e) {
//
}
}
INSTANCE = executor;
}
/**
* 服务监听线程任务执行器
* <p>
* For {@link person.pluto.natcross2.serverside.listen.ServerListenThread}
*
* @param runnable
*/
public static void executeServerListenAccept(Runnable runnable) {
INSTANCE.executeServerListenAccept(runnable);
}
/**
* 客户端监听线程任务执行器
* <p>
* For {@link person.pluto.natcross2.serverside.client.ClientServiceThread}
*
* @param runnable
*/
public static void executeClientServiceAccept(Runnable runnable) {
INSTANCE.executeClientServiceAccept(runnable);
}
/**
* 客户端消息处理任务执行器
* <p>
* For
* {@link person.pluto.natcross2.clientside.adapter.InteractiveSimpleClientAdapter#waitMessage()}
*
* @param runnable
*/
public static void executeClientMessageProc(Runnable runnable) {
INSTANCE.executeClientMessageProc(runnable);
}
/**
* 隧道线程执行器
* <p>
* For {@link person.pluto.natcross2.api.passway}
*
* @param runnable
*/
public static void executePassway(Runnable runnable) {
INSTANCE.executePassway(runnable);
}
/**
* nio事件任务执行器
* <p>
* For {@link person.pluto.natcross2.nio.NioHallows#run()}
*
* @param runnable
*/
public static void executeNioAction(Runnable runnable) {
INSTANCE.executeNioAction(runnable);
}
/**
* 心跳检测定时循环任务执行
*
* @param runnable
* @param delaySeconds
*/
public static ScheduledFuture<?> scheduledClientHeart(Runnable runnable, long delaySeconds) {
return INSTANCE.scheduledClientHeart(runnable, delaySeconds);
}
/**
* 服务监听清理无效socket对
*
* @param runnable
* @param delaySeconds
* @return
*/
public static ScheduledFuture<?> scheduledClearInvalidSocketPart(Runnable runnable, long delaySeconds) {
return INSTANCE.scheduledClearInvalidSocketPart(runnable, delaySeconds);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/executor/SimpleExecutor.java
================================================
package person.pluto.natcross2.executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* <p>
* 线程执行器
* </p>
*
* @author Pluto
* @since 2021-04-09 12:46:36
*/
public class SimpleExecutor implements IExecutor {
private final ExecutorService executor = Executors.newCachedThreadPool();
private final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
@Override
public void shutdown() {
this.executor.shutdownNow();
this.scheduledExecutor.shutdownNow();
}
@Override
public void execute(Runnable runnable) {
this.executor.execute(runnable);
}
@Override
public ScheduledFuture<?> scheduledClientHeart(Runnable runnable, long delaySeconds) {
return this.scheduledExecutor.scheduleWithFixedDelay(runnable, delaySeconds, delaySeconds, TimeUnit.SECONDS);
}
@Override
public ScheduledFuture<?> scheduledClearInvalidSocketPart(Runnable runnable, long delaySeconds) {
return this.scheduledExecutor.scheduleWithFixedDelay(runnable, delaySeconds, delaySeconds, TimeUnit.SECONDS);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/HttpRoute.java
================================================
package person.pluto.natcross2.model;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
* http路由表
* </p>
*
* @author Pluto
* @since 2020-04-24 09:31:51
*/
@Getter
@Setter(AccessLevel.PRIVATE)
public class HttpRoute {
public static HttpRoute of(String host, String destIp, Integer destPort) {
return HttpRoute.of(false, host, destIp, destPort);
}
public static HttpRoute of(boolean master, String host, String destIp, Integer destPort) {
HttpRoute model = new HttpRoute();
model.setMaster(master);
model.setHost(host);
model.setDestIp(destIp);
model.setDestPort(destPort);
return model;
}
// 主路由,如果是多个则会去队列最后设置的那个
private boolean master;
// 请求时的域名host
private String host;
// 目标IP
private String destIp;
// 目标端口
private Integer destPort;
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/InteractiveModel.java
================================================
package person.pluto.natcross2.model;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONObject;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import person.pluto.natcross2.common.CommonFormat;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
/**
* <p>
* 交互基础类型
* </p>
*
* @author Pluto
* @since 2019-07-18 18:16:34
*/
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class InteractiveModel implements JSONAware {
public static InteractiveModel of(InteractiveTypeEnum interactiveTypeEnum, String key, String value) {
JSONObject jsonObject = new JSONObject();
jsonObject.put(key, value);
return new InteractiveModel(CommonFormat.generateInteractiveSeq(), interactiveTypeEnum.name(), jsonObject);
}
public static InteractiveModel of(String interactiveSeq, InteractiveTypeEnum interactiveTypeEnum, Object data) {
return new InteractiveModel(interactiveSeq, interactiveTypeEnum.name(),
data == null ? null : JSON.parseObject(JSON.toJSONString(data)));
}
public static InteractiveModel of(InteractiveTypeEnum interactiveTypeEnum, Object data) {
return new InteractiveModel(CommonFormat.generateInteractiveSeq(), interactiveTypeEnum.name(),
data == null ? null : JSON.parseObject(JSON.toJSONString(data)));
}
public static InteractiveModel of(String interactiveType, Object data) {
return new InteractiveModel(CommonFormat.generateInteractiveSeq(), interactiveType,
JSON.parseObject(JSON.toJSONString(data)));
}
public InteractiveModel(InteractiveModel model) {
this.fullValue(model);
}
public void fullValue(InteractiveModel model) {
this.setInteractiveSeq(model.getInteractiveSeq());
this.setInteractiveType(model.getInteractiveType());
this.setData(model.getData());
}
/**
* 交互序列,用于异步通信
*/
private String interactiveSeq;
/**
* 交互类型
*/
private String interactiveType;
/**
* 交互实体内容
*/
private JSONObject data;
@Override
public String toJSONString() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("interactiveSeq", this.interactiveSeq);
jsonObject.put("interactiveType", this.interactiveType);
jsonObject.put("data", this.data);
return jsonObject.toJSONString();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/NatcrossResultModel.java
================================================
package person.pluto.natcross2.model;
import com.alibaba.fastjson.JSONAware;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.model.enumeration.NatcrossResultEnum;
import java.lang.reflect.Field;
/**
* <p>
* 常规类型的前后端返回model
* </p>
*
* @author Pluto
* @since 2019-03-28 10:45:41
*/
@Data
@Slf4j
public class NatcrossResultModel implements JSONAware {
public static NatcrossResultModel of(String retCode, String retMsg, Object object) {
return new NatcrossResultModel(retCode, retMsg, object);
}
public static NatcrossResultModel of(NatcrossResultEnum resultEnum, Object data) {
return new NatcrossResultModel(resultEnum.getCode(), resultEnum.getName(), data);
}
public static NatcrossResultModel of(NatcrossResultEnum resultEnum) {
return new NatcrossResultModel(resultEnum.getCode(), resultEnum.getName(), null);
}
public static NatcrossResultModel ofFail(Object data) {
return new NatcrossResultModel(NatcrossResultEnum.FAIL.getCode(), NatcrossResultEnum.FAIL.getName(), data);
}
public static NatcrossResultModel ofFail() {
return new NatcrossResultModel(NatcrossResultEnum.FAIL.getCode(), NatcrossResultEnum.FAIL.getName(), null);
}
public static NatcrossResultModel ofSuccess(Object data) {
return new NatcrossResultModel(NatcrossResultEnum.SUCCESS.getCode(), NatcrossResultEnum.SUCCESS.getName(),
data);
}
public static NatcrossResultModel ofSuccess() {
return new NatcrossResultModel(NatcrossResultEnum.SUCCESS.getCode(), NatcrossResultEnum.SUCCESS.getName(),
null);
}
private String retCod;
private String retMsg;
private Object data;
public NatcrossResultModel(String retCod, String retMsg, Object data) {
this.retCod = retCod;
this.retMsg = retMsg;
this.data = data;
}
/**
* 反射方式修改值
*
* @param fieldStr
* @param object
* @return
* @author Pluto
* @since 2019-05-10 14:04:48
*/
public NatcrossResultModel set(String fieldStr, Object object) {
Field field;
try {
field = this.getClass().getDeclaredField(fieldStr);
} catch (NoSuchFieldException | SecurityException e) {
log.warn("ResultModel get field failed!", e);
return this;
}
field.setAccessible(true);
try {
field.set(this, object);
} catch (IllegalArgumentException | IllegalAccessException e) {
log.warn("ResultModel set field failed!", e);
return this;
}
return this;
}
@Override
public String toString() {
return this.toJSONString();
}
@Override
public String toJSONString() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("retCod", this.retCod);
jsonObject.put("retMsg", this.retMsg);
jsonObject.put("data", this.data);
return jsonObject.toJSONString();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/SecretInteractiveModel.java
================================================
package person.pluto.natcross2.model;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;
import org.apache.commons.lang3.StringUtils;
import person.pluto.natcross2.utils.AESUtil;
import person.pluto.natcross2.utils.MD5Signature;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Key;
/**
* <p>
* 基于InteractiveModel模型的加密交互模型
* </p>
*
* @author Pluto
* @since 2020-01-08 16:38:52
*/
@Data
@NoArgsConstructor
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public class SecretInteractiveModel extends InteractiveModel {
public SecretInteractiveModel(InteractiveModel model) {
super(model);
}
/**
* 时间戳
*/
private Long timestamp;
/**
* 签名
*/
private String autograph;
/**
* InteractiveModel模型jsonString加密值
*/
private String encrypt;
/**
* 字符编码
*/
private String charset = StandardCharsets.UTF_8.name();
/**
* 加密消息
*
* @param key
* @throws Exception
* @author Pluto
* @since 2020-01-08 16:39:46
*/
public void encryptMsg(Key key) throws Exception {
this.encrypt = AESUtil.encryptBase64(key, super.toJSONString().getBytes(this.charset));
}
/**
* 解密消息
*
* @param key
* @throws Exception
* @author Pluto
* @since 2020-01-08 16:39:53
*/
public void decryptMsg(Key key) throws Exception {
byte[] decryptBase64 = AESUtil.decryptBase64(key, this.encrypt);
String interactiveJsonString = new String(decryptBase64, this.charset);
InteractiveModel model = JSON.parseObject(interactiveJsonString, InteractiveModel.class);
super.fullValue(model);
}
/**
* 签名模型
*
* @param tokenKey
* @author Pluto
* @since 2020-01-08 16:39:59
*/
public void autographMsg(String tokenKey) {
this.autograph = MD5Signature.getSignature(Charset.forName(this.charset), tokenKey, this.timestamp.toString(),
this.encrypt, this.charset);
}
/**
* 检查签名
*
* @param tokenKey
* @return
* @author Pluto
* @since 2020-01-08 16:40:09
*/
public boolean checkAutograph(String tokenKey) {
String signature = MD5Signature.getSignature(Charset.forName(this.charset), tokenKey, this.timestamp.toString(),
this.encrypt, this.charset);
return StringUtils.equals(this.autograph, signature);
}
/**
* 填充消息
*
* @param key
* @param tokenKey
* @throws Exception
* @author Pluto
* @since 2020-01-08 16:40:17
*/
public void fullMessage(Key key, String tokenKey) throws Exception {
this.timestamp = System.currentTimeMillis();
this.encryptMsg(key);
this.autographMsg(tokenKey);
}
@Override
public String toJSONString() {
JSONObject jsonObject = new JSONObject();
jsonObject.put("charset", this.charset);
jsonObject.put("timestamp", this.timestamp);
jsonObject.put("encrypt", this.encrypt);
jsonObject.put("autograph", this.autograph);
return jsonObject.toJSONString();
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/enumeration/InteractiveTypeEnum.java
================================================
package person.pluto.natcross2.model.enumeration;
import lombok.Getter;
import org.apache.commons.lang3.StringUtils;
/**
* <p>
* 交互类型enum
* </p>
*
* @author Pluto
* @since 2019-07-17 09:50:33
*/
@Getter
public enum InteractiveTypeEnum {
//
UNKNOWN("未知"),
//
COMMON_REPLY("通用回复标签"),
//
HEART_TEST("发送心跳"),
//
HEART_TEST_REPLY("心跳测试回复"),
//
SERVER_WAIT_CLIENT("需求客户端建立连接"),
//
CLIENT_CONNECT("客户端建立通道连接"),
//
CLIENT_CONTROL("客户端控制端口建立连接"),
//
;
private final String describe;
InteractiveTypeEnum(String describe) {
this.describe = describe;
}
public static InteractiveTypeEnum getEnumByName(String name) {
if (StringUtils.isBlank(name)) {
return null;
}
for (InteractiveTypeEnum e : InteractiveTypeEnum.values()) {
if (e.name().equals(name)) {
return e;
}
}
return null;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/enumeration/NatcrossResultEnum.java
================================================
package person.pluto.natcross2.model.enumeration;
import org.apache.commons.lang3.StringUtils;
import person.pluto.natcross2.model.NatcrossResultModel;
/**
* <p>
* 客户端服务端返回码
* </p>
*
* @author Pluto
* @since 2019-03-28 10:59:53
*/
public enum NatcrossResultEnum {
// 成功
SUCCESS("1000", "成功"),
//
UNKNOWN_INTERACTIVE_TYPE("3001", "未知的通信类型"),
//
NO_HAS_SERVER_LISTEN("3002", "不存在请求的监听接口"),
// 未知错误
FAIL("9999", "未知错误");
private final String code;
private final String name;
NatcrossResultEnum(String code, String name) {
this.code = code;
this.name = name;
}
public static NatcrossResultEnum getEnumByCode(String code) {
if (StringUtils.isBlank(code)) {
return null;
}
for (NatcrossResultEnum e : NatcrossResultEnum.values()) {
if (e.code.equals(code)) {
return e;
}
}
return null;
}
public NatcrossResultModel toResultModel() {
return NatcrossResultModel.of(this);
}
public String getCode() {
return code;
}
public String getName() {
return name;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/interactive/ClientConnectModel.java
================================================
package person.pluto.natcross2.model.interactive;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* 请求建立隧道模型
* </p>
*
* @author Pluto
* @since 2020-01-08 16:36:58
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClientConnectModel {
private String socketPartKey;
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/interactive/ClientControlModel.java
================================================
package person.pluto.natcross2.model.interactive;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* 请求建立控制器模型
* </p>
*
* @author Pluto
* @since 2020-01-08 16:37:12
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ClientControlModel {
private Integer listenPort;
}
================================================
FILE: src/main/java/person/pluto/natcross2/model/interactive/ServerWaitModel.java
================================================
package person.pluto.natcross2.model.interactive;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* 服务端等待建立隧道模型
* </p>
*
* @author Pluto
* @since 2020-01-08 16:37:26
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ServerWaitModel {
private String socketPartKey;
}
================================================
FILE: src/main/java/person/pluto/natcross2/nio/INioProcessor.java
================================================
package person.pluto.natcross2.nio;
import java.nio.channels.SelectionKey;
/**
* <p>
* nio 执行器
* </p>
*
* @author Pluto
* @since 2021-04-12 17:51:37
*/
@FunctionalInterface
public interface INioProcessor {
/**
* 需要执行的方法
*
* @param key
*/
void process(SelectionKey key);
}
================================================
FILE: src/main/java/person/pluto/natcross2/nio/NioHallows.java
================================================
package person.pluto.natcross2.nio;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.executor.NatcrossExecutor;
import person.pluto.natcross2.utils.Assert;
import person.pluto.natcross2.utils.CountWaitLatch;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* <p>
* nio 容器
* </p>
*
* @author Pluto
* @since 2021-04-13 09:25:51
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class NioHallows implements Runnable {
public static final NioHallows INSTANCE = new NioHallows();
/**
* 注册监听动作
* <p>
* 要注意这里只拿最后的一次注册为准,即 {@code channel} 只能与一个 {@code proccesser} 动作对应
*
* @param channel
* @param ops 依据以下值进行或运算进行最后结果设定,并且 {@code channel} 要支持相应的动作
* <p>
* - {@link SelectionKey#OP_ACCEPT}
* <p>
* - {@link SelectionKey#OP_CONNECT}
* <p>
* - {@link SelectionKey#OP_READ}
* <p>
* - {@link SelectionKey#OP_WRITE}
* @param processor 要执行的动作
* @throws IOException
* @author Pluto
* @since 2021-04-26 15:55:38
*/
public static void register(SelectableChannel channel, int ops, INioProcessor processor) throws IOException {
INSTANCE.register0(channel, ops, processor);
}
/**
* 根据 {@link SelectionKey} 恢复监听事件的注册
*
* @param key 原始的key
* @param ops 要与通过 {@link #register(SelectableChannel, int, INioProcessor)}
* 注册的事件统一
* @throws IOException
* @author Pluto
* @since 2021-05-07 13:21:49
*/
public static boolean reRegisterByKey(SelectionKey key, int ops) {
return INSTANCE.reRegisterByKey0(key, ops);
}
/**
* 释放注册
*
* @param channel
* @author Pluto
* @since 2021-04-26 16:03:51
*/
public static void release(SelectableChannel channel) {
INSTANCE.release0(channel);
}
private volatile Thread myThread = null;
private volatile boolean alive = false;
private volatile boolean canceled = false;
private volatile Selector selector;
private final Object selectorLock = new Object();
private final CountWaitLatch countWaitLatch = new CountWaitLatch();
private final Map<SelectableChannel, ProcesserHolder> channelProcesserMap = new ConcurrentHashMap<>();
@Setter
@Getter
private long selectTimeout = 10L;
@Setter
@Getter
private long wakeupSleepNanos = 1000000L;
/**
* 获取 {@link #selector}
* <p>
* 若 {@link #selector} 未有值,则会进行初始化:打开selector,并执行 {@link #start()}
*
* @return
* @throws IOException
* @author Pluto
* @since 2021-04-26 16:04:30
*/
public Selector getSelector() throws IOException {
// 判空、返回逻辑,按第一次取值进行,缺点是不能判断是否已经关闭,但与 this.cancel()
// 方法中的执行顺序来看,会先被设置为null,再去close,所以可以大概率认为若不为null即为没有关闭
Selector selector = this.selector;
if (Objects.isNull(selector)) {
synchronized (this.selectorLock) {
// 二次校验
// 若是主动退出,则不在创建,避免退出时有新任务而被重启,若要重新启用,则需要主动调用 start() 方法来启动
if (Objects.isNull(this.selector) && !this.canceled) {
this.selector = Selector.open();
this.start();
}
}
selector = this.selector;
if (Objects.isNull(selector)) {
throw new IOException("NioHallows's selector is closed");
}
}
return selector;
}
/**
* 获取唤醒后的 {@link #selector}
* <p>
* 注意,若 {@link #run()} 快于你的任务,还是会被再次阻塞,只是执行了一次 {@link Selector#wakeup()}
*
* @return
* @throws IOException
* @author Pluto
* @since 2021-04-26 16:07:00
*/
public Selector getWakeupSelector() throws IOException {
return this.getSelector().wakeup();
}
/**
* 注册监听动作
* <p>
* 要注意这里只拿最后的一次注册为准,即 {@code channel} 只能与一个 {@code proccesser} 动作对应
*
* @param channel
* @param ops 依据以下值进行或运算进行最后结果设定,并且 {@code channel} 要支持相应的动作
* <p>
* - {@link SelectionKey#OP_ACCEPT}
* <p>
* - {@link SelectionKey#OP_CONNECT}
* <p>
* - {@link SelectionKey#OP_READ}
* <p>
* - {@link SelectionKey#OP_WRITE}
* @param proccesser 要执行的动作
* @throws IOException
* @author Pluto
* @since 2021-04-26 15:55:38
*/
public void register0(SelectableChannel channel, int ops, INioProcessor proccesser) throws IOException {
Objects.requireNonNull(channel, "channel non null");
try {
this.channelProcesserMap.put(channel, ProcesserHolder.of(channel, ops, proccesser));
channel.configureBlocking(false);
this.countWaitLatch.countUp();
// 这里有个坑点,如果在select中,这里会被阻塞
channel.register(this.getWakeupSelector(), ops);
} catch (Throwable e) {
this.channelProcesserMap.remove(channel);
throw e;
} finally {
this.countWaitLatch.countDown();
}
}
/**
* 根据 {@link SelectionKey} 恢复监听事件的注册
*
* @param key 原始的key
* @param ops 要与通过 {@link #register0(SelectableChannel, int, INioProcessor)}
* 注册的事件统一
* @throws IOException
* @author Pluto
* @since 2021-05-07 13:21:49
*/
public boolean reRegisterByKey0(SelectionKey key, int ops) {
Objects.requireNonNull(key, "key non null");
Assert.state(key.selector() == this.selector, "this SelectionKey is not belong NioHallows's selector");
if (!key.isValid()) {
return false;
}
// 通过事件和源码分析,恢复注册是通过updateKeys.addLast进行,虽然没有被阻塞,但是需要进行一次唤醒才可以成功恢复事件监听
// 因无法获知是否成功注入selector,所以必须要进行一次唤醒操作,并且没有阻塞的问题,所以这里不通过countWaitLatch进行同步
key.interestOps(ops);
try {
this.getWakeupSelector();
} catch (IOException e) {
// 出错了交给其他的流程逻辑,这里只进行一次唤醒
}
return true;
}
/**
* 释放注册
*
* @param channel
* @author Pluto
* @since 2021-04-26 16:03:51
*/
public void release0(SelectableChannel channel) {
if (Objects.isNull(channel)) {
return;
}
this.channelProcesserMap.remove(channel);
SelectionKey key = channel.keyFor(this.selector);
if (Objects.nonNull(key)) {
key.cancel();
}
}
@Override
public void run() {
CountWaitLatch countWaitLatch = this.countWaitLatch;
while (this.alive) {
// 给注册事务一个时间,如果等待时间太长(可能需要注入的太多),就跳出再去获取新事件,防止饿死
try {
countWaitLatch.await(this.getWakeupSleepNanos(), TimeUnit.NANOSECONDS);
} catch (InterruptedException e) {
log.warn("selector wait register timeout");
}
Selector selector;
try {
selector = getSelector();
// 采用有期限的监听,以免线程太快,没有来的及注册,就永远阻塞在那里了
int select = selector.select(this.getSelectTimeout());
if (select <= 0) {
continue;
}
} catch (IOException e) {
log.error("NioHallows run exception", e);
continue;
}
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
iterator.remove();
try {
key.interestOps(0);
} catch (Exception e) {
// do nothing
}
ProcesserHolder processerHolder = this.channelProcesserMap.get(key.channel());
if (Objects.isNull(processerHolder)) {
key.cancel();
continue;
}
NatcrossExecutor.executeNioAction(() -> processerHolder.process(key));
}
}
}
/**
* 启动nio事件监听
*
* @author Pluto
* @since 2021-04-26 16:33:07
*/
public synchronized void start() {
this.canceled = false;
this.alive = true;
Thread myThread = this.myThread;
if (myThread == null || !myThread.isAlive()) {
myThread = this.myThread = new Thread(this);
myThread.setName("nio-hallows");
myThread.start();
log.info("NioHallows is started!");
}
}
/**
* 退出nio事件监听
*
* @author Pluto
* @since 2021-04-26 16:33:33
*/
public void cancel() {
// 假设A线程执行到了 this.selector = Selector.open() 但是调用 this.cancel()
// 方法的B线程抢占cpu成功,并一直到执行完成,此时A线程抢占CPU继续执行,又会进行重启,与关停项目时的关停期望不同。
//
// 此处锁定 this.selectorLock 后再去设置 this.canceled,形成与 this.getSelector()
// 的线程同步,同时避免了被动调用 this.start() 时与 this.cancel() 的同步问题,最终可关闭。
// 虽与主动调用 this.start() 有不同步的风险,但 this.start() 、 this.cancel()
// 主动调用的场景有极大对立性,所以不进行过多的关照。
//
// 注意:若 this.cancel() 添加了synchronized,存在死锁的可能!!!
synchronized (this.selectorLock) {
this.canceled = true;
}
log.info("NioHallows cancel");
this.alive = false;
Selector selector;
if ((selector = this.selector) != null) {
this.selector = null;
try {
selector.close();
} catch (IOException e) {
// do nothing
}
}
Thread myThread;
if ((myThread = this.myThread) != null) {
this.myThread = null;
myThread.interrupt();
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/nio/ProcesserHolder.java
================================================
package person.pluto.natcross2.nio;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
/**
* <p>
* 执行器暂存
* </p>
*
* @author Pluto
* @since 2021-04-13 09:53:54
*/
@Data
@AllArgsConstructor(staticName = "of")
public class ProcesserHolder {
private SelectableChannel channel;
private int interestOps;
private INioProcessor processor;
/**
* 执行事件的任务
*
* @param key
* @author Pluto
* @since 2021-04-26 16:35:36
*/
public void process(SelectionKey key) {
this.processor.process(key);
if (!NioHallows.reRegisterByKey(key, this.interestOps)) {
NioHallows.release(this.channel);
}
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/ClientServiceThread.java
================================================
package person.pluto.natcross2.serverside.client;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.executor.NatcrossExecutor;
import person.pluto.natcross2.nio.INioProcessor;
import person.pluto.natcross2.nio.NioHallows;
import person.pluto.natcross2.serverside.client.config.IClientServiceConfig;
import person.pluto.natcross2.utils.Assert;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Objects;
/**
* <p>
* 客户端服务进程
* </p>
*
* @author Pluto
* @since 2019-07-05 10:53:33
*/
@Slf4j
public final class ClientServiceThread implements Runnable, INioProcessor {
private volatile Thread myThread = null;
private volatile boolean isAlive = false;
private volatile boolean canceled = false;
private final ServerSocket listenServerSocket;
private final IClientServiceConfig<?, ?> config;
public ClientServiceThread(IClientServiceConfig<?, ?> config) throws Exception {
this.config = config;
// 启动时配置,若启动失败则执行cancell并再次抛出异常让上级处理
this.listenServerSocket = config.createServerSocket();
log.info("client service [{}] is created!", this.config.getListenPort());
}
@Override
public void run() {
while (this.isAlive) {
try {
Socket listenSocket = this.listenServerSocket.accept();
this.procMethod(listenSocket);
} catch (Exception e) {
log.warn("客户端服务进程 轮询等待出现异常", e);
this.cancel();
}
}
}
@Override
public void process(SelectionKey key) {
if (!key.isValid()) {
this.cancel();
}
try {
ServerSocketChannel channel = (ServerSocketChannel) key.channel();
SocketChannel accept = channel.accept();
for (; Objects.nonNull(accept); accept = channel.accept()) {
this.procMethod(accept.socket());
}
} catch (IOException e) {
log.warn("客户端服务进程 轮询等待出现异常", e);
this.cancel();
}
}
/**
* 处理客户端发来的消息
*
* @param listenSocket
* @author Pluto
* @since 2020-01-03 14:46:36
*/
public void procMethod(Socket listenSocket) {
NatcrossExecutor.executeClientServiceAccept(() -> {
try {
this.config.getClientServiceAdapter().procMethod(listenSocket);
} catch (Exception e) {
log.error("处理socket异常", e);
try {
listenSocket.close();
} catch (IOException sce) {
log.warn("处理新socket时异常,并在关闭socket时异常", e);
}
}
});
}
/**
* 启动
*
* @author Pluto
* @since 2020-01-03 14:05:59
*/
public synchronized void start() {
Assert.state(!this.canceled, "已退出,不得重新启动");
log.info("client service [{}] starting ...", this.config.getListenPort());
this.isAlive = true;
ServerSocketChannel channel = this.listenServerSocket.getChannel();
if (Objects.nonNull(channel)) {
try {
NioHallows.register(channel, SelectionKey.OP_ACCEPT, this);
} catch (IOException e) {
log.error("register clientService channel[{}] faild!", config.getListenPort());
this.cancel();
throw new RuntimeException("nio注册时异常", e);
}
} else {
if (this.myThread == null || !this.myThread.isAlive()) {
this.myThread = new Thread(this);
this.myThread.setName("client-server-" + this.formatInfo());
this.myThread.start();
}
}
log.info("client service [{}] start success", this.config.getListenPort());
}
/**
* 退出
*
* @author Pluto
* @since 2019-07-18 18:32:03
*/
public synchronized void cancel() {
if (this.canceled) {
return;
}
this.canceled = true;
log.info("client service [{}] will cancell", this.config.getListenPort());
this.isAlive = false;
ServerSocket listenServerSocket;
if ((listenServerSocket = this.listenServerSocket) != null) {
NioHallows.release(listenServerSocket.getChannel());
try {
listenServerSocket.close();
} catch (IOException e) {
log.warn("监听端口关闭异常", e);
}
}
Thread myThread;
if ((myThread = this.myThread) != null) {
myThread.interrupt();
}
log.info("client service [{}] cancell success", this.config.getListenPort());
}
/**
* * 是否激活状态
*
* @return
* @author Pluto
* @since 2020-01-07 09:43:28
*/
public boolean isAlive() {
return this.isAlive;
}
/**
* 是否已退出
*
* @return
* @author Pluto
* @since 2021-04-13 13:39:11
*/
public boolean isCanceled() {
return this.canceled;
}
/**
* 获取监听端口
*
* @return
* @author Pluto
* @since 2019-07-18 18:32:40
*/
public Integer getListenPort() {
return this.config.getListenPort();
}
/**
* 格式化为短小的可识别信息
*
* @return
* @author Pluto
* @since 2020-04-15 14:17:41
*/
public String formatInfo() {
return String.valueOf(this.getListenPort());
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/DefaultReadAheadPassValueAdapter.java
================================================
package person.pluto.natcross2.serverside.client.adapter;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.serverside.client.config.IClientServiceConfig;
import person.pluto.natcross2.serverside.client.handler.DefaultInteractiveProcessHandler;
/**
* <p>
* 默认的预读后处理适配器
* </p>
*
* @author Pluto
* @since 2021-04-26 17:06:25
*/
public class DefaultReadAheadPassValueAdapter extends ReadAheadPassValueAdapter<InteractiveModel, InteractiveModel> {
public DefaultReadAheadPassValueAdapter(IClientServiceConfig<InteractiveModel, InteractiveModel> config) {
super(config);
this.addLast(DefaultInteractiveProcessHandler.INSTANCE);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/IClientServiceAdapter.java
================================================
package person.pluto.natcross2.serverside.client.adapter;
import java.net.Socket;
/**
* <p>
* 客户端服务适配器
* </p>
*
* @author Pluto
* @since 2020-01-08 16:40:35
*/
public interface IClientServiceAdapter {
/**
* 处理方法
*
* @param listenSocket
* @throws Exception
*/
void procMethod(Socket listenSocket) throws Exception;
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/PassValueNextEnum.java
================================================
package person.pluto.natcross2.serverside.client.adapter;
import lombok.Getter;
/**
* <p>
* 传值适配器的handler回复信息
* </p>
*
* @author Pluto
* @since 2020-01-08 16:40:54
*/
@Getter
public enum PassValueNextEnum {
// 停止并关闭
STOP_CLOSE(false, true),
// 停止但不关闭
STOP_KEEP(false, false),
// 继续执行,默认关闭
NEXT(true, true),
// 继续执行,但不要关闭
NEXT_KEEP(true, false),
//
;
private final boolean nextFlag;
private final boolean closeFlag;
PassValueNextEnum(boolean nextFlag, boolean closeFlag) {
this.nextFlag = nextFlag;
this.closeFlag = closeFlag;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/ReadAheadPassValueAdapter.java
================================================
package person.pluto.natcross2.serverside.client.adapter;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.common.Optional;
import person.pluto.natcross2.serverside.client.config.IClientServiceConfig;
import person.pluto.natcross2.serverside.client.handler.IPassValueHandler;
import java.io.IOException;
import java.net.Socket;
import java.util.LinkedList;
import java.util.List;
/**
* <p>
* 预读后处理适配器
* </p>
*
* @param <R>
* @param <W>
* @author Pluto
* @since 2020-01-06 08:56:22
*/
@Slf4j
public class ReadAheadPassValueAdapter<R, W> implements IClientServiceAdapter {
/**
* 处理器队列
*/
private List<IPassValueHandler<? super R, ? extends W>> handlerList = new LinkedList<>();
/**
* 客户端服务配置
*/
private final IClientServiceConfig<R, W> config;
public ReadAheadPassValueAdapter(IClientServiceConfig<R, W> config) {
this.config = config;
}
@Override
public void procMethod(Socket listenSocket) throws Exception {
// 建立交互通道
SocketChannel<? extends R, ? super W> socketChannel;
try {
socketChannel = this.config.newSocketChannel(listenSocket);
} catch (Exception e) {
log.error("创建socket通道异常", e);
throw e;
}
Optional<R> optional;
try {
R read = socketChannel.read();
optional = Optional.of(read);
} catch (Exception e) {
log.error("读取数据异常", e);
throw e;
}
boolean closeFlag = true;
for (IPassValueHandler<? super R, ? extends W> handler : handlerList) {
// 按照队列进行执行
PassValueNextEnum proc = handler.proc(socketChannel, optional);
// 只要有一个需要不关闭通道则就不得关闭
if (!proc.isCloseFlag()) {
closeFlag = false;
}
// 如果无需继续向下则退出
if (!proc.isNextFlag()) {
break;
}
}
if (closeFlag) {
try {
socketChannel.closeSocket();
} catch (IOException e) {
log.error("关闭socket异常", e);
}
}
}
/**
* 添加处理器到最后一个
*
* @param handler
* @return
* @author Pluto
* @since 2020-01-06 09:06:55
*/
public ReadAheadPassValueAdapter<R, W> addLast(IPassValueHandler<? super R, ? extends W> handler) {
this.handlerList.add(handler);
return this;
}
/**
* 设置handlerList
*
* @param handlerList
* @return
* @author Pluto
* @since 2020-01-06 09:07:53
*/
public ReadAheadPassValueAdapter<R, W> setHandlerList(List<IPassValueHandler<? super R, ? extends W>> handlerList) {
this.handlerList = handlerList;
return this;
}
/**
* 获取处理器,可以自行更改
*
* @return
* @author Pluto
* @since 2020-01-06 09:06:28
*/
public List<IPassValueHandler<? super R, ? extends W>> getHandlerList() {
return this.handlerList;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/config/IClientServiceConfig.java
================================================
package person.pluto.natcross2.serverside.client.config;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.serverside.client.adapter.IClientServiceAdapter;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.Charset;
/**
* <p>
* 客户端服务配置
* </p>
*
* @param <R> 交互通道读取的类
* @param <W> 交互通道可写的类
* @author Pluto
* @since 2020-01-08 16:43:17
*/
public interface IClientServiceConfig<R, W> {
/**
* 监听端口
*
* @return
*/
Integer getListenPort();
/**
* 创建监听端口
*
* @return
* @throws Exception
*/
ServerSocket createServerSocket() throws Exception;
/**
* 客户端适配器
*
* @return
*/
IClientServiceAdapter getClientServiceAdapter();
/**
* 交互通道
*
* @param listenSocket
* @return
* @throws Exception
*/
SocketChannel<? extends R, ? super W> newSocketChannel(Socket listenSocket) throws Exception;
/**
* 字符集
*
* @return
*/
Charset getCharset();
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/config/SecretSimpleClientServiceConfig.java
================================================
package person.pluto.natcross2.serverside.client.config;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import person.pluto.natcross2.channel.SecretInteractiveChannel;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.utils.AESUtil;
import java.net.Socket;
import java.security.Key;
/**
* <p>
* 隧道过程加密的配置类
* </p>
*
* @author Pluto
* @since 2020-01-08 16:44:42
*/
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class SecretSimpleClientServiceConfig extends SimpleClientServiceConfig {
/**
* 签名混淆key
*/
private String tokenKey;
/**
* 隧道过程加密key AES
*/
private Key aesKey;
public SecretSimpleClientServiceConfig(Integer listenPort) {
super(listenPort);
}
@Override
public SocketChannel<? extends InteractiveModel, ? super InteractiveModel> newSocketChannel(Socket listenSocket)
throws Exception {
SecretInteractiveChannel channel = new SecretInteractiveChannel();
channel.setCharset(this.getCharset());
channel.setTokenKey(this.tokenKey);
channel.setAesKey(this.aesKey);
channel.setSocket(listenSocket);
return channel;
}
/**
* BASE64格式设置隧道加密密钥
*
* @param aesKey
*/
public void setBaseAesKey(String aesKey) {
this.aesKey = AESUtil.createKeyByBase64(aesKey);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/config/SimpleClientServiceConfig.java
================================================
package person.pluto.natcross2.serverside.client.config;
import lombok.NoArgsConstructor;
import person.pluto.natcross2.channel.InteractiveChannel;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.serverside.client.adapter.DefaultReadAheadPassValueAdapter;
import person.pluto.natcross2.serverside.client.adapter.IClientServiceAdapter;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
/**
* <p>
* 简单交互的客户端服务配置
* </p>
*
* @author Pluto
* @since 2020-01-08 16:45:47
*/
@NoArgsConstructor
public class SimpleClientServiceConfig implements IClientServiceConfig<InteractiveModel, InteractiveModel> {
private Integer listenPort;
private IClientServiceAdapter clientServiceAdapter = new DefaultReadAheadPassValueAdapter(this);
private Charset charset = StandardCharsets.UTF_8;
public SimpleClientServiceConfig(Integer listenPort) {
this.listenPort = listenPort;
}
public void setListenPort(int listenPort) {
this.listenPort = listenPort;
}
@Override
public Integer getListenPort() {
return this.listenPort;
}
@Override
public ServerSocket createServerSocket() throws Exception {
ServerSocketChannel openServerSocketChannel = SelectorProvider.provider().openServerSocketChannel();
openServerSocketChannel.bind(new InetSocketAddress(this.getListenPort()));
return openServerSocketChannel.socket();
// return new ServerSocket(this.getListenPort());
}
/**
* 设置适配器
*
* @param clientServiceAdapter
*/
public void setClientServiceAdapter(IClientServiceAdapter clientServiceAdapter) {
this.clientServiceAdapter = clientServiceAdapter;
}
@Override
public IClientServiceAdapter getClientServiceAdapter() {
return this.clientServiceAdapter;
}
@Override
public SocketChannel<? extends InteractiveModel, ? super InteractiveModel> newSocketChannel(Socket listenSocket)
throws Exception {
InteractiveChannel channel = new InteractiveChannel();
channel.setSocket(listenSocket);
channel.setCharset(this.charset);
return channel;
}
@Override
public Charset getCharset() {
return this.charset;
}
/**
* 设置字符编码
*
* @param charset
*/
public void setCharset(Charset charset) {
this.charset = charset;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/handler/DefaultInteractiveProcessHandler.java
================================================
package person.pluto.natcross2.serverside.client.handler;
import person.pluto.natcross2.serverside.client.process.ClientConnectProcess;
import person.pluto.natcross2.serverside.client.process.ClientControlProcess;
/**
* <p>
* 默认的接收处理handler
* </p>
*
* @author Pluto
* @since 2021-04-26 17:22:31
*/
public class DefaultInteractiveProcessHandler extends InteractiveProcessHandler {
public static final DefaultInteractiveProcessHandler INSTANCE = new DefaultInteractiveProcessHandler();
public DefaultInteractiveProcessHandler() {
this.addLast(ClientControlProcess.INSTANCE);
this.addLast(ClientConnectProcess.INSTANCE);
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/handler/IPassValueHandler.java
================================================
package person.pluto.natcross2.serverside.client.handler;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.common.Optional;
import person.pluto.natcross2.serverside.client.adapter.PassValueNextEnum;
/**
* <p>
* 传值方式客户端是配置的处理接口
* </p>
*
* @param <R>
* @param <W>
* @author Pluto
* @since 2020-01-08 16:47:40
*/
public interface IPassValueHandler<R, W> {
/**
* 处理方法
*
* @param socketChannel 交互通道
* @param optional 可以重设值
* @return
*/
PassValueNextEnum proc(SocketChannel<? extends R, ? super W> socketChannel, Optional<? extends R> optional);
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/handler/InteractiveProcessHandler.java
================================================
package person.pluto.natcross2.serverside.client.handler;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.common.Optional;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import person.pluto.natcross2.model.enumeration.NatcrossResultEnum;
import person.pluto.natcross2.serverside.client.adapter.PassValueNextEnum;
import person.pluto.natcross2.serverside.client.process.IProcess;
import java.util.LinkedList;
import java.util.List;
/**
* <p>
* 常规接收处理handler
* </p>
*
* @author Pluto
* @since 2020-01-06 10:20:11
*/
@Slf4j
public class InteractiveProcessHandler implements IPassValueHandler<InteractiveModel, InteractiveModel> {
/**
* 处理链表
*/
private List<IProcess> processList = new LinkedList<>();
@Override
public PassValueNextEnum proc(SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel,
Optional<? extends InteractiveModel> optional) {
InteractiveModel value = optional.getValue();
log.info("接收到新消息:[ {} ]", value);
for (IProcess process : this.processList) {
boolean wouldProc = process.wouldProc(value);
if (wouldProc) {
boolean processMethod;
try {
processMethod = process.processMethod(socketChannel, value);
} catch (Exception e) {
log.error("处理任务时发生异常", e);
return PassValueNextEnum.STOP_CLOSE;
}
if (processMethod) {
return PassValueNextEnum.STOP_KEEP;
} else {
return PassValueNextEnum.STOP_CLOSE;
}
}
}
try {
socketChannel.writeAndFlush(InteractiveModel.of(value.getInteractiveSeq(), InteractiveTypeEnum.COMMON_REPLY,
NatcrossResultEnum.UNKNOWN_INTERACTIVE_TYPE.toResultModel()));
} catch (Exception e) {
log.error("发送消息时异常", e);
}
return PassValueNextEnum.STOP_CLOSE;
}
/**
* 将处理方法加入后面
*
* @param process
* @return
*/
public InteractiveProcessHandler addLast(IProcess process) {
this.processList.add(process);
return this;
}
/**
* 获取处理链表,可以代理维护
*
* @return
*/
public List<IProcess> getProcessList() {
return this.processList;
}
/**
* 设置处理链表
*
* @param processList
* @return
*/
public InteractiveProcessHandler setProcessList(List<IProcess> processList) {
this.processList = processList;
return this;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/process/ClientConnectProcess.java
================================================
package person.pluto.natcross2.serverside.client.process;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.common.CommonFormat;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.NatcrossResultModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import person.pluto.natcross2.model.enumeration.NatcrossResultEnum;
import person.pluto.natcross2.model.interactive.ClientConnectModel;
import person.pluto.natcross2.serverside.listen.ListenServerControl;
import person.pluto.natcross2.serverside.listen.ServerListenThread;
/**
* <p>
* 请求建立隧道处理器
* </p>
*
* @author Pluto
* @since 2020-01-08 16:48:25
*/
public class ClientConnectProcess implements IProcess {
public static final ClientConnectProcess INSTANCE = new ClientConnectProcess();
@Override
public boolean wouldProc(InteractiveModel recvInteractiveModel) {
InteractiveTypeEnum interactiveTypeEnum = InteractiveTypeEnum.getEnumByName(
recvInteractiveModel.getInteractiveType());
return InteractiveTypeEnum.CLIENT_CONNECT.equals(interactiveTypeEnum);
}
@Override
public boolean processMethod(SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel,
InteractiveModel recvInteractiveModel) throws Exception {
ClientConnectModel clientConnectModel = recvInteractiveModel.getData().toJavaObject(ClientConnectModel.class);
Integer listenPort = CommonFormat.getSocketPortByPartKey(clientConnectModel.getSocketPartKey());
ServerListenThread serverListenThread = ListenServerControl.get(listenPort);
if (serverListenThread == null) {
socketChannel.writeAndFlush(
InteractiveModel.of(recvInteractiveModel.getInteractiveSeq(), InteractiveTypeEnum.COMMON_REPLY,
NatcrossResultEnum.NO_HAS_SERVER_LISTEN.toResultModel()));
return false;
}
// 回复设置成功,如果doSetPartClient没有找到对应的搭档,则直接按关闭处理
socketChannel.writeAndFlush(
InteractiveModel.of(recvInteractiveModel.getInteractiveSeq(), InteractiveTypeEnum.COMMON_REPLY,
NatcrossResultModel.ofSuccess()));
// 若设置成功,则上层无需关闭
// 若设置失败,则由上层关闭
return serverListenThread.doSetPartClient(clientConnectModel.getSocketPartKey(), socketChannel.getSocket());
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/process/ClientControlProcess.java
================================================
package person.pluto.natcross2.serverside.client.process;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.model.InteractiveModel;
import person.pluto.natcross2.model.NatcrossResultModel;
import person.pluto.natcross2.model.enumeration.InteractiveTypeEnum;
import person.pluto.natcross2.model.enumeration.NatcrossResultEnum;
import person.pluto.natcross2.model.interactive.ClientControlModel;
import person.pluto.natcross2.serverside.listen.ListenServerControl;
import person.pluto.natcross2.serverside.listen.ServerListenThread;
/**
* <p>
* 请求建立控制器处理方法
* </p>
*
* @author Pluto
* @since 2020-01-08 16:48:43
*/
public class ClientControlProcess implements IProcess {
public static final ClientControlProcess INSTANCE = new ClientControlProcess();
@Override
public boolean wouldProc(InteractiveModel recvInteractiveModel) {
InteractiveTypeEnum interactiveTypeEnum = InteractiveTypeEnum.getEnumByName(
recvInteractiveModel.getInteractiveType());
return InteractiveTypeEnum.CLIENT_CONTROL.equals(interactiveTypeEnum);
}
@Override
public boolean processMethod(SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel,
InteractiveModel recvInteractiveModel) throws Exception {
ClientControlModel clientControlModel = recvInteractiveModel.getData().toJavaObject(ClientControlModel.class);
ServerListenThread serverListenThread = ListenServerControl.get(clientControlModel.getListenPort());
if (serverListenThread == null) {
socketChannel.writeAndFlush(
InteractiveModel.of(recvInteractiveModel.getInteractiveSeq(), InteractiveTypeEnum.COMMON_REPLY,
NatcrossResultEnum.NO_HAS_SERVER_LISTEN.toResultModel()));
return false;
}
socketChannel.writeAndFlush(
InteractiveModel.of(recvInteractiveModel.getInteractiveSeq(), InteractiveTypeEnum.COMMON_REPLY,
NatcrossResultModel.ofSuccess()));
serverListenThread.setControlSocket(socketChannel.getSocket());
return true;
}
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/client/process/IProcess.java
================================================
package person.pluto.natcross2.serverside.client.process;
import person.pluto.natcross2.channel.SocketChannel;
import person.pluto.natcross2.model.InteractiveModel;
/**
* <p>
* 处理方法接口
* </p>
*
* @author Pluto
* @since 2020-01-08 16:49:06
*/
public interface IProcess {
/**
* 判断是否是由这个处理
*
* @param recvInteractiveModel
* @return
*/
boolean wouldProc(InteractiveModel recvInteractiveModel);
/**
* 处理方法,需要回复信息的,自己使用socketChannel回复
*
* @param socketChannel
* @param recvInteractiveModel
* @return 是否保持socket开启状态
*/
boolean processMethod(SocketChannel<? extends InteractiveModel, ? super InteractiveModel> socketChannel,
InteractiveModel recvInteractiveModel) throws Exception;
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/listen/IServerListen.java
================================================
package person.pluto.natcross2.serverside.listen;
import person.pluto.natcross2.serverside.listen.control.IControlSocket;
/**
* <p>
* 端口监听服务接口
* </p>
*
* @author Pluto
* @since 2021-07-01 13:46:20
*/
public interface IServerListen {
/**
* 格式化信息
*
* @return
*/
String formatInfo();
/**
* 控制端口通知关闭
*
* @param controlSocket
*/
void controlCloseNotice(IControlSocket controlSocket);
}
================================================
FILE: src/main/java/person/pluto/natcross2/serverside/listen/ListenServerControl.java
================================================
package person.pluto.natcross2.serverside.listen;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import person.pluto.natcross2.serverside.listen.config.IListenServerConfig;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/**
* <p>
* 转发监听服务控制类
* </p>
*
* @author Pluto
* @since 2019-07-05 11:25:44
*/
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class ListenServerControl {
private static final ConcurrentHashMap<Integer, ServerListenThread> serverListenMap = new ConcurrentHashMap<>();
/**
* 加入新的监听服务进程
*
* @param serverListen
* @return
*/
public static boolean add(ServerListenThread serverListen) {
if (serverListen == null) {
return false;
}
Integer listenPort = serverListen.getListenPort();
ServerListenThread serverListenThread = serverListenMap.get(listenPort);
if (serverListenThread != null) {
// 必须要先remove掉才能add,讲道理如果原先的存在应该直接报错才对,也就是参数为null,所以这里不自动remove
return false;
}
serverListenMap.put(listenPort, serverListen);
return true;
}
/**
* 去除指定端口的监听服务端口
*
* @param listenPort
* @return
*/
public static boolean remove(Integer listenPort) {
ServerListenThread serverListenThread = serverListenMap.remove(listenPort);
if (Objects.nonNull(serverListenThread)) {
serverListenThread.cancel();
}
return true;
}
/**
* 根据端口获取监听服务端口
*
* @param listenPort
* @return
*/
public static ServerListenThread get(Integer listenPort) {
return serverListenMap.get(listenPort);
}
/**
* 获取全部监听服务
*
* @return
*/
public static List<ServerListenThread> getAll() {
List<ServerListenThread> list = n
gitextract_mhw2jg3q/
├── .gitignore
├── LICENSE
├── README.md
├── VERSION.md
├── pom.xml
└── src/
├── main/
│ ├── java/
│ │ └── person/
│ │ └── pluto/
│ │ └── natcross2/
│ │ ├── ClientApp.java
│ │ ├── CommonConstants.java
│ │ ├── ServerApp.java
│ │ ├── api/
│ │ │ ├── IBelongControl.java
│ │ │ ├── IHttpRouting.java
│ │ │ ├── passway/
│ │ │ │ ├── SecretPassway.java
│ │ │ │ └── SimplePassway.java
│ │ │ ├── secret/
│ │ │ │ ├── AESSecret.java
│ │ │ │ └── ISecret.java
│ │ │ └── socketpart/
│ │ │ ├── AbsSocketPart.java
│ │ │ ├── HttpRouteSocketPart.java
│ │ │ ├── SecretSocketPart.java
│ │ │ └── SimpleSocketPart.java
│ │ ├── channel/
│ │ │ ├── Channel.java
│ │ │ ├── InteractiveChannel.java
│ │ │ ├── JsonChannel.java
│ │ │ ├── LengthChannel.java
│ │ │ ├── SecretInteractiveChannel.java
│ │ │ ├── SocketChannel.java
│ │ │ └── StringChannel.java
│ │ ├── clientside/
│ │ │ ├── ClientControlThread.java
│ │ │ ├── adapter/
│ │ │ │ ├── IClientAdapter.java
│ │ │ │ └── InteractiveSimpleClientAdapter.java
│ │ │ ├── config/
│ │ │ │ ├── AllSecretInteractiveClientConfig.java
│ │ │ │ ├── HttpRouteClientConfig.java
│ │ │ │ ├── IClientConfig.java
│ │ │ │ ├── InteractiveClientConfig.java
│ │ │ │ └── SecretInteractiveClientConfig.java
│ │ │ ├── handler/
│ │ │ │ ├── CommonReplyHandler.java
│ │ │ │ ├── IClientHandler.java
│ │ │ │ ├── ServerHeartHandler.java
│ │ │ │ └── ServerWaitClientHandler.java
│ │ │ └── heart/
│ │ │ ├── ClientHeartThread.java
│ │ │ └── IClientHeartThread.java
│ │ ├── common/
│ │ │ ├── CommonFormat.java
│ │ │ └── Optional.java
│ │ ├── executor/
│ │ │ ├── IExecutor.java
│ │ │ ├── NatcrossExecutor.java
│ │ │ └── SimpleExecutor.java
│ │ ├── model/
│ │ │ ├── HttpRoute.java
│ │ │ ├── InteractiveModel.java
│ │ │ ├── NatcrossResultModel.java
│ │ │ ├── SecretInteractiveModel.java
│ │ │ ├── enumeration/
│ │ │ │ ├── InteractiveTypeEnum.java
│ │ │ │ └── NatcrossResultEnum.java
│ │ │ └── interactive/
│ │ │ ├── ClientConnectModel.java
│ │ │ ├── ClientControlModel.java
│ │ │ └── ServerWaitModel.java
│ │ ├── nio/
│ │ │ ├── INioProcessor.java
│ │ │ ├── NioHallows.java
│ │ │ └── ProcesserHolder.java
│ │ ├── serverside/
│ │ │ ├── client/
│ │ │ │ ├── ClientServiceThread.java
│ │ │ │ ├── adapter/
│ │ │ │ │ ├── DefaultReadAheadPassValueAdapter.java
│ │ │ │ │ ├── IClientServiceAdapter.java
│ │ │ │ │ ├── PassValueNextEnum.java
│ │ │ │ │ └── ReadAheadPassValueAdapter.java
│ │ │ │ ├── config/
│ │ │ │ │ ├── IClientServiceConfig.java
│ │ │ │ │ ├── SecretSimpleClientServiceConfig.java
│ │ │ │ │ └── SimpleClientServiceConfig.java
│ │ │ │ ├── handler/
│ │ │ │ │ ├── DefaultInteractiveProcessHandler.java
│ │ │ │ │ ├── IPassValueHandler.java
│ │ │ │ │ └── InteractiveProcessHandler.java
│ │ │ │ └── process/
│ │ │ │ ├── ClientConnectProcess.java
│ │ │ │ ├── ClientControlProcess.java
│ │ │ │ └── IProcess.java
│ │ │ └── listen/
│ │ │ ├── IServerListen.java
│ │ │ ├── ListenServerControl.java
│ │ │ ├── ServerListenThread.java
│ │ │ ├── clear/
│ │ │ │ ├── ClearInvalidSocketPartThread.java
│ │ │ │ └── IClearInvalidSocketPartThread.java
│ │ │ ├── config/
│ │ │ │ ├── AllSecretSimpleListenServerConfig.java
│ │ │ │ ├── IListenServerConfig.java
│ │ │ │ ├── MultControlListenServerConfig.java
│ │ │ │ ├── SecretSimpleListenServerConfig.java
│ │ │ │ └── SimpleListenServerConfig.java
│ │ │ ├── control/
│ │ │ │ ├── ControlSocket.java
│ │ │ │ ├── IControlSocket.java
│ │ │ │ └── MultiControlSocket.java
│ │ │ ├── recv/
│ │ │ │ ├── ClientHeartHandler.java
│ │ │ │ ├── CommonReplyHandler.java
│ │ │ │ └── IRecvHandler.java
│ │ │ └── serversocket/
│ │ │ └── ICreateServerSocket.java
│ │ └── utils/
│ │ ├── AESUtil.java
│ │ ├── Assert.java
│ │ ├── CountWaitLatch.java
│ │ ├── MD5Signature.java
│ │ └── Tools.java
│ └── resources/
│ └── logback.xml
└── test/
└── java/
└── person/
└── pluto/
└── TestMain.java
SYMBOL INDEX (555 symbols across 88 files)
FILE: src/main/java/person/pluto/natcross2/ClientApp.java
class ClientApp (line 19) | public class ClientApp {
method main (line 21) | public static void main(String[] args) throws Exception {
method secretHttpRoute (line 33) | public static void secretHttpRoute() throws Exception {
method secretAll (line 65) | public static void secretAll() throws Exception {
method secret (line 91) | public static void secret() throws Exception {
method simple (line 115) | public static void simple() throws Exception {
FILE: src/main/java/person/pluto/natcross2/CommonConstants.java
class CommonConstants (line 14) | @NoArgsConstructor(access = AccessLevel.PRIVATE)
class ListenDest (line 39) | static class ListenDest {
method of (line 40) | public static ListenDest of(int listenPort, String destIp, int destP...
FILE: src/main/java/person/pluto/natcross2/ServerApp.java
class ServerApp (line 30) | public class ServerApp {
method main (line 39) | public static void main(String[] args) throws Exception {
method multControlSecret (line 67) | public static void multControlSecret() throws Exception {
method secretAll (line 92) | public static void secretAll() throws Exception {
method secret (line 115) | public static void secret() throws Exception {
method simple (line 137) | public static void simple() throws Exception {
FILE: src/main/java/person/pluto/natcross2/api/IBelongControl.java
type IBelongControl (line 11) | public interface IBelongControl {
method noticeStop (line 16) | default void noticeStop() {
method stopSocketPart (line 26) | default boolean stopSocketPart(String socketPartKey) {
FILE: src/main/java/person/pluto/natcross2/api/IHttpRouting.java
type IHttpRouting (line 13) | public interface IHttpRouting {
method pickEffectiveRoute (line 21) | HttpRoute pickEffectiveRoute(String host);
FILE: src/main/java/person/pluto/natcross2/api/passway/SecretPassway.java
class SecretPassway (line 28) | @Data
type Mode (line 35) | public enum Mode {
method run (line 59) | @Override
method secretToNo (line 80) | private void secretToNo() throws Exception {
method noToSecret (line 112) | private void noToSecret() throws Exception {
method isValid (line 135) | public boolean isValid() {
method cancel (line 145) | public void cancel() {
method start (line 173) | public void start() {
FILE: src/main/java/person/pluto/natcross2/api/passway/SimplePassway.java
class SimplePassway (line 30) | @Slf4j
method getOutputStream (line 55) | private OutputStream getOutputStream() throws IOException {
method getOutputChannel (line 64) | private SocketChannel getOutputChannel() {
method write (line 86) | private void write(ByteBuffer byteBuffer) throws IOException {
method run (line 97) | @Override
method obtainByteBuffer (line 124) | private ByteBuffer obtainByteBuffer() {
method process (line 138) | @Override
method isValid (line 179) | public boolean isValid() {
method cancel (line 186) | public void cancel() {
method start (line 214) | public void start() {
FILE: src/main/java/person/pluto/natcross2/api/secret/AESSecret.java
class AESSecret (line 16) | @Data
method encrypt (line 21) | @Override
method decrypt (line 26) | @Override
method setBaseAesKey (line 36) | public void setBaseAesKey(String aesKey) {
FILE: src/main/java/person/pluto/natcross2/api/secret/ISecret.java
type ISecret (line 11) | public interface ISecret {
method encrypt (line 22) | byte[] encrypt(byte[] content, int offset, int len) throws Exception;
method decrypt (line 31) | byte[] decrypt(byte[] result) throws Exception;
FILE: src/main/java/person/pluto/natcross2/api/socketpart/AbsSocketPart.java
class AbsSocketPart (line 21) | @Data
method AbsSocketPart (line 53) | protected AbsSocketPart(IBelongControl belongThread) {
method isValid (line 61) | public boolean isValid() {
method cancel (line 77) | public abstract void cancel();
method createPassWay (line 84) | public abstract boolean createPassWay();
FILE: src/main/java/person/pluto/natcross2/api/socketpart/HttpRouteSocketPart.java
class HttpRouteSocketPart (line 27) | @Slf4j
method HttpRouteSocketPart (line 39) | public HttpRouteSocketPart(IBelongControl belongThread, IHttpRouting h...
method routeHost (line 51) | protected void routeHost() throws Exception {
method createPassWay (line 158) | @Override
FILE: src/main/java/person/pluto/natcross2/api/socketpart/SecretSocketPart.java
class SecretSocketPart (line 24) | @Slf4j
method SecretSocketPart (line 40) | public SecretSocketPart(IBelongControl belongThread) {
method cancel (line 44) | @Override
method createPassWay (line 88) | @Override
method stop (line 126) | public void stop() {
method noticeStop (line 136) | @Override
FILE: src/main/java/person/pluto/natcross2/api/socketpart/SimpleSocketPart.java
class SimpleSocketPart (line 22) | @Slf4j
method SimpleSocketPart (line 34) | public SimpleSocketPart(IBelongControl belongThread) {
method stop (line 41) | public void stop() {
method cancel (line 51) | @Override
method createPassWay (line 95) | @Override
method noticeStop (line 127) | @Override
FILE: src/main/java/person/pluto/natcross2/channel/Channel.java
type Channel (line 16) | public interface Channel<R, W> extends Closeable {
method read (line 24) | R read() throws Exception;
method write (line 32) | void write(W value) throws Exception;
method flush (line 39) | void flush() throws Exception;
method writeAndFlush (line 47) | void writeAndFlush(W value) throws Exception;
method setCharset (line 54) | default void setCharset(Charset charset) {
FILE: src/main/java/person/pluto/natcross2/channel/InteractiveChannel.java
class InteractiveChannel (line 18) | public class InteractiveChannel extends SocketChannel<InteractiveModel, ...
method InteractiveChannel (line 25) | public InteractiveChannel() {
method InteractiveChannel (line 29) | public InteractiveChannel(Socket socket) throws IOException {
method read (line 33) | @Override
method write (line 39) | @Override
method flush (line 44) | @Override
method writeAndFlush (line 49) | @Override
method getCharset (line 59) | public Charset getCharset() {
method setCharset (line 63) | @Override
method getSocket (line 68) | @Override
method setSocket (line 73) | @Override
method closeSocket (line 78) | @Override
FILE: src/main/java/person/pluto/natcross2/channel/JsonChannel.java
class JsonChannel (line 19) | public class JsonChannel extends SocketChannel<JSONObject, Object> {
method JsonChannel (line 26) | public JsonChannel() {
method JsonChannel (line 30) | public JsonChannel(Socket socket) throws IOException {
method read (line 34) | @Override
method valueConvert (line 40) | private String valueConvert(Object value) {
method write (line 50) | @Override
method flush (line 55) | @Override
method writeAndFlush (line 60) | @Override
method getCharset (line 70) | public Charset getCharset() {
method setCharset (line 74) | @Override
method getSocket (line 79) | @Override
method setSocket (line 84) | @Override
method closeSocket (line 89) | @Override
FILE: src/main/java/person/pluto/natcross2/channel/LengthChannel.java
class LengthChannel (line 21) | public class LengthChannel extends SocketChannel<byte[], byte[]> {
method LengthChannel (line 34) | public LengthChannel() {
method LengthChannel (line 37) | public LengthChannel(Socket socket) throws IOException {
method read (line 41) | @Override
method write (line 80) | @Override
method flush (line 100) | @Override
method writeAndFlush (line 112) | @Override
method getSocket (line 125) | @Override
method setSocket (line 130) | @Override
method closeSocket (line 144) | @Override
method getInputSteam (line 157) | private InputStream getInputSteam() throws IOException {
method getOutputStream (line 173) | private OutputStream getOutputStream() throws IOException {
FILE: src/main/java/person/pluto/natcross2/channel/SecretInteractiveChannel.java
class SecretInteractiveChannel (line 23) | @Data
method SecretInteractiveChannel (line 45) | public SecretInteractiveChannel() {
method SecretInteractiveChannel (line 49) | public SecretInteractiveChannel(Socket socket) throws IOException {
method read (line 53) | @Override
method valueConvert (line 76) | private Object valueConvert(InteractiveModel value) throws Exception {
method write (line 83) | @Override
method flush (line 88) | @Override
method writeAndFlush (line 93) | @Override
method getCharset (line 103) | public Charset getCharset() {
method setCharset (line 107) | @Override
method getSocket (line 112) | @Override
method setSocket (line 117) | @Override
method closeSocket (line 122) | @Override
method setBaseAesKey (line 132) | public void setBaseAesKey(String aesKey) {
FILE: src/main/java/person/pluto/natcross2/channel/SocketChannel.java
class SocketChannel (line 16) | public abstract class SocketChannel<R, W> implements Channel<R, W> {
method getSocket (line 23) | public abstract Socket getSocket();
method setSocket (line 31) | public abstract void setSocket(Socket socket) throws IOException;
method closeSocket (line 38) | public abstract void closeSocket() throws IOException;
method close (line 40) | @Override
FILE: src/main/java/person/pluto/natcross2/channel/StringChannel.java
class StringChannel (line 16) | public class StringChannel extends SocketChannel<String, String> {
method StringChannel (line 22) | public StringChannel() {
method StringChannel (line 26) | public StringChannel(Socket socket) throws IOException {
method read (line 30) | @Override
method valueConvert (line 42) | private byte[] valueConvert(String value) {
method write (line 46) | @Override
method flush (line 51) | @Override
method writeAndFlush (line 56) | @Override
method getCharset (line 66) | public Charset getCharset() {
method setCharset (line 70) | @Override
method getSocket (line 75) | @Override
method setSocket (line 80) | @Override
method closeSocket (line 85) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/ClientControlThread.java
class ClientControlThread (line 24) | @Slf4j
method ClientControlThread (line 40) | public ClientControlThread(IClientConfig<?, ?> config) {
method createControl (line 51) | public boolean createControl() throws Exception {
method run (line 68) | @Override
method stopSocketPart (line 82) | @Override
method start (line 97) | private void start() {
method stopClient (line 121) | public void stopClient() {
method cancell (line 143) | public void cancell() {
method getListenServerPort (line 184) | public Integer getListenServerPort() {
method setDestIpPort (line 194) | public void setDestIpPort(String destIp, Integer destPort) {
method isAlive (line 203) | public boolean isAlive() {
method sendHeartTest (line 212) | public void sendHeartTest() throws Exception {
method obtainServerHeartLastRecvTime (line 222) | public LocalDateTime obtainServerHeartLastRecvTime() {
method putSocketPart (line 232) | public void putSocketPart(String socketPartKey, AbsSocketPart socketPa...
method formatInfo (line 241) | public String formatInfo() {
FILE: src/main/java/person/pluto/natcross2/clientside/adapter/IClientAdapter.java
type IClientAdapter (line 18) | public interface IClientAdapter<R, W> {
method createControlChannel (line 26) | boolean createControlChannel() throws Exception;
method clientConnect (line 34) | boolean clientConnect(ServerWaitModel serverWaitModel);
method waitMessage (line 41) | void waitMessage() throws Exception;
method close (line 48) | void close() throws Exception;
method sendHeartTest (line 55) | void sendHeartTest() throws Exception;
method obtainServerHeartLastRecvTime (line 62) | LocalDateTime obtainServerHeartLastRecvTime();
method resetServerHeartLastRecvTime (line 71) | default LocalDateTime resetServerHeartLastRecvTime() {
method resetServerHeartLastRecvTime (line 81) | LocalDateTime resetServerHeartLastRecvTime(LocalDateTime time);
method getSocketChannel (line 88) | SocketChannel<? extends R, ? super W> getSocketChannel();
FILE: src/main/java/person/pluto/natcross2/clientside/adapter/InteractiveSimpleClientAdapter.java
class InteractiveSimpleClientAdapter (line 33) | @Slf4j
method InteractiveSimpleClientAdapter (line 57) | public InteractiveSimpleClientAdapter(ClientControlThread clientContro...
method newClientChannel (line 68) | protected SocketChannel<? extends InteractiveModel, ? super Interactiv...
method newDestSocket (line 78) | protected Socket newDestSocket() throws Exception {
method newSocketPart (line 87) | protected AbsSocketPart newSocketPart() {
method createControlChannel (line 91) | @Override
method clientConnect (line 126) | @Override
method waitMessage (line 193) | @Override
method procMethod (line 208) | protected void procMethod(InteractiveModel recvInteractiveModel) {
method getSocketChannel (line 233) | @Override
method addMessageHandler (line 244) | public InteractiveSimpleClientAdapter addMessageHandler(
method close (line 250) | @Override
method sendHeartTest (line 259) | @Override
method obtainServerHeartLastRecvTime (line 265) | @Override
method resetServerHeartLastRecvTime (line 270) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/config/AllSecretInteractiveClientConfig.java
class AllSecretInteractiveClientConfig (line 21) | public class AllSecretInteractiveClientConfig extends SecretInteractiveC...
method newSocketPart (line 27) | @Override
method setBasePasswayKey (line 41) | public void setBasePasswayKey(String key) {
FILE: src/main/java/person/pluto/natcross2/clientside/config/HttpRouteClientConfig.java
class HttpRouteClientConfig (line 35) | @Slf4j
method HttpRouteClientConfig (line 47) | public HttpRouteClientConfig() {
method HttpRouteClientConfig (line 51) | public HttpRouteClientConfig(InteractiveClientConfig baseConfig) {
method presetRoute (line 61) | public void presetRoute(HttpRoute masterRoute, LinkedHashMap<String, H...
method getRouteMap (line 83) | public Map<String, HttpRoute> getRouteMap() {
method addRoute (line 92) | public void addRoute(HttpRoute... httpRoutes) {
method clearRoute (line 123) | public void clearRoute(String... hosts) {
method setDestIpPort (line 173) | @Override
method newClientHeartThread (line 178) | @Override
method newCreateControlAdapter (line 183) | @Override
method newClientChannel (line 194) | @Override
method newSocketPart (line 199) | @Override
method pickEffectiveRoute (line 208) | @Override
method newDestSocket (line 237) | @Override
method getClientServiceIp (line 244) | @Override
method setClientServiceIp (line 249) | @Override
method getClientServicePort (line 254) | @Override
method setClientServicePort (line 259) | @Override
method getListenServerPort (line 264) | @Override
method setListenServerPort (line 269) | @Override
method getDestIp (line 274) | @Override
method setDestIp (line 279) | @Override
method getDestPort (line 284) | @Override
method setDestPort (line 289) | @Override
method getCharset (line 294) | @Override
method setCharset (line 299) | @Override
method getStreamCacheSize (line 304) | @Override
method setStreamCacheSize (line 309) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/config/IClientConfig.java
type IClientConfig (line 23) | public interface IClientConfig<R, W> {
method getClientServiceIp (line 30) | String getClientServiceIp();
method getClientServicePort (line 37) | Integer getClientServicePort();
method getListenServerPort (line 44) | Integer getListenServerPort();
method getDestIp (line 51) | String getDestIp();
method getDestPort (line 58) | Integer getDestPort();
method setDestIpPort (line 65) | void setDestIpPort(String destIp, Integer destPort);
method newClientHeartThread (line 73) | IClientHeartThread newClientHeartThread(ClientControlThread clientCont...
method newCreateControlAdapter (line 81) | IClientAdapter<R, W> newCreateControlAdapter(ClientControlThread clien...
method newClientChannel (line 88) | SocketChannel<? extends R, ? super W> newClientChannel();
method newSocketPart (line 95) | AbsSocketPart newSocketPart(ClientControlThread clientControlThread);
method newDestSocket (line 103) | default Socket newDestSocket() throws Exception {
FILE: src/main/java/person/pluto/natcross2/clientside/config/InteractiveClientConfig.java
class InteractiveClientConfig (line 34) | @Slf4j
method setDestIpPort (line 57) | @Override
method newClientHeartThread (line 63) | @Override
method newCreateControlAdapter (line 71) | @Override
method newClientChannel (line 82) | @Override
method newSocketPart (line 101) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/config/SecretInteractiveClientConfig.java
class SecretInteractiveClientConfig (line 23) | @Slf4j
method newClientChannel (line 31) | @Override
method setBaseAesKey (line 54) | public void setBaseAesKey(String aesKey) {
FILE: src/main/java/person/pluto/natcross2/clientside/handler/CommonReplyHandler.java
class CommonReplyHandler (line 20) | @Slf4j
method proc (line 29) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/handler/IClientHandler.java
type IClientHandler (line 13) | public interface IClientHandler<R, W> {
method proc (line 23) | boolean proc(R model, IClientAdapter<? extends R, ? super W> clientAda...
FILE: src/main/java/person/pluto/natcross2/clientside/handler/ServerHeartHandler.java
class ServerHeartHandler (line 17) | @Slf4j
method proc (line 22) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/handler/ServerWaitClientHandler.java
class ServerWaitClientHandler (line 16) | public class ServerWaitClientHandler implements IClientHandler<Interacti...
method proc (line 20) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/heart/ClientHeartThread.java
class ClientHeartThread (line 23) | @Slf4j
method ClientHeartThread (line 44) | public ClientHeartThread(ClientControlThread clientControlThread) {
method lastServerHeartEffective (line 55) | private boolean lastServerHeartEffective(ClientControlThread clientCon...
method run (line 60) | @Override
method start (line 113) | @Override
method cancel (line 124) | @Override
method isAlive (line 138) | @Override
FILE: src/main/java/person/pluto/natcross2/clientside/heart/IClientHeartThread.java
type IClientHeartThread (line 11) | public interface IClientHeartThread {
method isAlive (line 18) | boolean isAlive();
method cancel (line 23) | void cancel();
method start (line 28) | void start();
FILE: src/main/java/person/pluto/natcross2/common/CommonFormat.java
class CommonFormat (line 19) | @NoArgsConstructor(access = AccessLevel.PRIVATE)
method generateSocketPartKey (line 28) | public static String generateSocketPartKey(Integer listenPort) {
method getSocketPortByPartKey (line 41) | public static Integer getSocketPortByPartKey(String socketPartKey) {
method generateInteractiveSeq (line 51) | public static String generateInteractiveSeq() {
FILE: src/main/java/person/pluto/natcross2/common/Optional.java
class Optional (line 12) | public class Optional<T> {
method of (line 14) | public static <T> Optional<T> of(T value) {
method Optional (line 18) | public Optional(T value) {
method getValue (line 24) | public T getValue() {
method setValue (line 28) | public void setValue(T value) {
FILE: src/main/java/person/pluto/natcross2/executor/IExecutor.java
type IExecutor (line 13) | public interface IExecutor {
method shutdown (line 20) | void shutdown();
method execute (line 27) | void execute(Runnable runnable);
method executeServerListenAccept (line 36) | default void executeServerListenAccept(Runnable runnable) {
method executeClientServiceAccept (line 47) | default void executeClientServiceAccept(Runnable runnable) {
method executeClientMessageProc (line 59) | default void executeClientMessageProc(Runnable runnable) {
method executePassway (line 70) | default void executePassway(Runnable runnable) {
method executeNioAction (line 81) | default void executeNioAction(Runnable runnable) {
method scheduledClientHeart (line 91) | ScheduledFuture<?> scheduledClientHeart(Runnable runnable, long delayS...
method scheduledClearInvalidSocketPart (line 100) | ScheduledFuture<?> scheduledClearInvalidSocketPart(Runnable runnable, ...
FILE: src/main/java/person/pluto/natcross2/executor/NatcrossExecutor.java
class NatcrossExecutor (line 19) | @NoArgsConstructor(access = AccessLevel.PRIVATE)
method shutdown (line 24) | public static void shutdown() {
method resetExecutor (line 35) | public static void resetExecutor(IExecutor executor) {
method executeServerListenAccept (line 54) | public static void executeServerListenAccept(Runnable runnable) {
method executeClientServiceAccept (line 65) | public static void executeClientServiceAccept(Runnable runnable) {
method executeClientMessageProc (line 77) | public static void executeClientMessageProc(Runnable runnable) {
method executePassway (line 88) | public static void executePassway(Runnable runnable) {
method executeNioAction (line 99) | public static void executeNioAction(Runnable runnable) {
method scheduledClientHeart (line 109) | public static ScheduledFuture<?> scheduledClientHeart(Runnable runnabl...
method scheduledClearInvalidSocketPart (line 120) | public static ScheduledFuture<?> scheduledClearInvalidSocketPart(Runna...
FILE: src/main/java/person/pluto/natcross2/executor/SimpleExecutor.java
class SimpleExecutor (line 17) | public class SimpleExecutor implements IExecutor {
method shutdown (line 23) | @Override
method execute (line 29) | @Override
method scheduledClientHeart (line 34) | @Override
method scheduledClearInvalidSocketPart (line 39) | @Override
FILE: src/main/java/person/pluto/natcross2/model/HttpRoute.java
class HttpRoute (line 15) | @Getter
method of (line 19) | public static HttpRoute of(String host, String destIp, Integer destPor...
method of (line 23) | public static HttpRoute of(boolean master, String host, String destIp,...
FILE: src/main/java/person/pluto/natcross2/model/InteractiveModel.java
class InteractiveModel (line 22) | @Data
method of (line 28) | public static InteractiveModel of(InteractiveTypeEnum interactiveTypeE...
method of (line 35) | public static InteractiveModel of(String interactiveSeq, InteractiveTy...
method of (line 40) | public static InteractiveModel of(InteractiveTypeEnum interactiveTypeE...
method of (line 45) | public static InteractiveModel of(String interactiveType, Object data) {
method InteractiveModel (line 50) | public InteractiveModel(InteractiveModel model) {
method fullValue (line 54) | public void fullValue(InteractiveModel model) {
method toJSONString (line 73) | @Override
FILE: src/main/java/person/pluto/natcross2/model/NatcrossResultModel.java
class NatcrossResultModel (line 20) | @Data
method of (line 24) | public static NatcrossResultModel of(String retCode, String retMsg, Ob...
method of (line 28) | public static NatcrossResultModel of(NatcrossResultEnum resultEnum, Ob...
method of (line 32) | public static NatcrossResultModel of(NatcrossResultEnum resultEnum) {
method ofFail (line 36) | public static NatcrossResultModel ofFail(Object data) {
method ofFail (line 40) | public static NatcrossResultModel ofFail() {
method ofSuccess (line 44) | public static NatcrossResultModel ofSuccess(Object data) {
method ofSuccess (line 49) | public static NatcrossResultModel ofSuccess() {
method NatcrossResultModel (line 58) | public NatcrossResultModel(String retCod, String retMsg, Object data) {
method set (line 73) | public NatcrossResultModel set(String fieldStr, Object object) {
method toString (line 93) | @Override
method toJSONString (line 98) | @Override
FILE: src/main/java/person/pluto/natcross2/model/SecretInteractiveModel.java
class SecretInteractiveModel (line 25) | @Data
method SecretInteractiveModel (line 31) | public SecretInteractiveModel(InteractiveModel model) {
method encryptMsg (line 63) | public void encryptMsg(Key key) throws Exception {
method decryptMsg (line 75) | public void decryptMsg(Key key) throws Exception {
method autographMsg (line 89) | public void autographMsg(String tokenKey) {
method checkAutograph (line 102) | public boolean checkAutograph(String tokenKey) {
method fullMessage (line 117) | public void fullMessage(Key key, String tokenKey) throws Exception {
method toJSONString (line 123) | @Override
FILE: src/main/java/person/pluto/natcross2/model/enumeration/InteractiveTypeEnum.java
type InteractiveTypeEnum (line 14) | @Getter
method InteractiveTypeEnum (line 35) | InteractiveTypeEnum(String describe) {
method getEnumByName (line 39) | public static InteractiveTypeEnum getEnumByName(String name) {
FILE: src/main/java/person/pluto/natcross2/model/enumeration/NatcrossResultEnum.java
type NatcrossResultEnum (line 15) | public enum NatcrossResultEnum {
method NatcrossResultEnum (line 28) | NatcrossResultEnum(String code, String name) {
method getEnumByCode (line 33) | public static NatcrossResultEnum getEnumByCode(String code) {
method toResultModel (line 45) | public NatcrossResultModel toResultModel() {
method getCode (line 49) | public String getCode() {
method getName (line 53) | public String getName() {
FILE: src/main/java/person/pluto/natcross2/model/interactive/ClientConnectModel.java
class ClientConnectModel (line 15) | @Data
FILE: src/main/java/person/pluto/natcross2/model/interactive/ClientControlModel.java
class ClientControlModel (line 15) | @Data
FILE: src/main/java/person/pluto/natcross2/model/interactive/ServerWaitModel.java
class ServerWaitModel (line 15) | @Data
FILE: src/main/java/person/pluto/natcross2/nio/INioProcessor.java
type INioProcessor (line 13) | @FunctionalInterface
method process (line 21) | void process(SelectionKey key);
FILE: src/main/java/person/pluto/natcross2/nio/NioHallows.java
class NioHallows (line 30) | @Slf4j
method register (line 56) | public static void register(SelectableChannel channel, int ops, INioPr...
method reRegisterByKey (line 70) | public static boolean reRegisterByKey(SelectionKey key, int ops) {
method release (line 81) | public static void release(SelectableChannel channel) {
method getSelector (line 114) | public Selector getSelector() throws IOException {
method getWakeupSelector (line 146) | public Selector getWakeupSelector() throws IOException {
method register0 (line 170) | public void register0(SelectableChannel channel, int ops, INioProcesso...
method reRegisterByKey0 (line 197) | public boolean reRegisterByKey0(SelectionKey key, int ops) {
method release0 (line 226) | public void release0(SelectableChannel channel) {
method run (line 239) | @Override
method start (line 293) | public synchronized void start() {
method cancel (line 314) | public void cancel() {
FILE: src/main/java/person/pluto/natcross2/nio/ProcesserHolder.java
class ProcesserHolder (line 17) | @Data
method process (line 34) | public void process(SelectionKey key) {
FILE: src/main/java/person/pluto/natcross2/serverside/client/ClientServiceThread.java
class ClientServiceThread (line 26) | @Slf4j
method ClientServiceThread (line 37) | public ClientServiceThread(IClientServiceConfig<?, ?> config) throws E...
method run (line 46) | @Override
method process (line 59) | @Override
method procMethod (line 84) | public void procMethod(Socket listenSocket) {
method start (line 105) | public synchronized void start() {
method cancel (line 137) | public synchronized void cancel() {
method isAlive (line 172) | public boolean isAlive() {
method isCanceled (line 183) | public boolean isCanceled() {
method getListenPort (line 194) | public Integer getListenPort() {
method formatInfo (line 205) | public String formatInfo() {
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/DefaultReadAheadPassValueAdapter.java
class DefaultReadAheadPassValueAdapter (line 15) | public class DefaultReadAheadPassValueAdapter extends ReadAheadPassValue...
method DefaultReadAheadPassValueAdapter (line 17) | public DefaultReadAheadPassValueAdapter(IClientServiceConfig<Interacti...
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/IClientServiceAdapter.java
type IClientServiceAdapter (line 13) | public interface IClientServiceAdapter {
method procMethod (line 21) | void procMethod(Socket listenSocket) throws Exception;
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/PassValueNextEnum.java
type PassValueNextEnum (line 13) | @Getter
method PassValueNextEnum (line 30) | PassValueNextEnum(boolean nextFlag, boolean closeFlag) {
FILE: src/main/java/person/pluto/natcross2/serverside/client/adapter/ReadAheadPassValueAdapter.java
class ReadAheadPassValueAdapter (line 24) | @Slf4j
method ReadAheadPassValueAdapter (line 36) | public ReadAheadPassValueAdapter(IClientServiceConfig<R, W> config) {
method procMethod (line 40) | @Override
method addLast (line 93) | public ReadAheadPassValueAdapter<R, W> addLast(IPassValueHandler<? sup...
method setHandlerList (line 106) | public ReadAheadPassValueAdapter<R, W> setHandlerList(List<IPassValueH...
method getHandlerList (line 118) | public List<IPassValueHandler<? super R, ? extends W>> getHandlerList() {
FILE: src/main/java/person/pluto/natcross2/serverside/client/config/IClientServiceConfig.java
type IClientServiceConfig (line 20) | public interface IClientServiceConfig<R, W> {
method getListenPort (line 27) | Integer getListenPort();
method createServerSocket (line 35) | ServerSocket createServerSocket() throws Exception;
method getClientServiceAdapter (line 42) | IClientServiceAdapter getClientServiceAdapter();
method newSocketChannel (line 51) | SocketChannel<? extends R, ? super W> newSocketChannel(Socket listenSo...
method getCharset (line 58) | Charset getCharset();
FILE: src/main/java/person/pluto/natcross2/serverside/client/config/SecretSimpleClientServiceConfig.java
class SecretSimpleClientServiceConfig (line 22) | @Data
method SecretSimpleClientServiceConfig (line 36) | public SecretSimpleClientServiceConfig(Integer listenPort) {
method newSocketChannel (line 40) | @Override
method setBaseAesKey (line 56) | public void setBaseAesKey(String aesKey) {
FILE: src/main/java/person/pluto/natcross2/serverside/client/config/SimpleClientServiceConfig.java
class SimpleClientServiceConfig (line 26) | @NoArgsConstructor
method SimpleClientServiceConfig (line 33) | public SimpleClientServiceConfig(Integer listenPort) {
method setListenPort (line 37) | public void setListenPort(int listenPort) {
method getListenPort (line 41) | @Override
method createServerSocket (line 46) | @Override
method setClientServiceAdapter (line 59) | public void setClientServiceAdapter(IClientServiceAdapter clientServic...
method getClientServiceAdapter (line 63) | @Override
method newSocketChannel (line 68) | @Override
method getCharset (line 77) | @Override
method setCharset (line 87) | public void setCharset(Charset charset) {
FILE: src/main/java/person/pluto/natcross2/serverside/client/handler/DefaultInteractiveProcessHandler.java
class DefaultInteractiveProcessHandler (line 14) | public class DefaultInteractiveProcessHandler extends InteractiveProcess...
method DefaultInteractiveProcessHandler (line 18) | public DefaultInteractiveProcessHandler() {
FILE: src/main/java/person/pluto/natcross2/serverside/client/handler/IPassValueHandler.java
type IPassValueHandler (line 17) | public interface IPassValueHandler<R, W> {
method proc (line 26) | PassValueNextEnum proc(SocketChannel<? extends R, ? super W> socketCha...
FILE: src/main/java/person/pluto/natcross2/serverside/client/handler/InteractiveProcessHandler.java
class InteractiveProcessHandler (line 23) | @Slf4j
method proc (line 31) | @Override
method addLast (line 71) | public InteractiveProcessHandler addLast(IProcess process) {
method getProcessList (line 81) | public List<IProcess> getProcessList() {
method setProcessList (line 91) | public InteractiveProcessHandler setProcessList(List<IProcess> process...
FILE: src/main/java/person/pluto/natcross2/serverside/client/process/ClientConnectProcess.java
class ClientConnectProcess (line 21) | public class ClientConnectProcess implements IProcess {
method wouldProc (line 25) | @Override
method processMethod (line 32) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/client/process/ClientControlProcess.java
class ClientControlProcess (line 20) | public class ClientControlProcess implements IProcess {
method wouldProc (line 24) | @Override
method processMethod (line 31) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/client/process/IProcess.java
type IProcess (line 14) | public interface IProcess {
method wouldProc (line 22) | boolean wouldProc(InteractiveModel recvInteractiveModel);
method processMethod (line 31) | boolean processMethod(SocketChannel<? extends InteractiveModel, ? supe...
FILE: src/main/java/person/pluto/natcross2/serverside/listen/IServerListen.java
type IServerListen (line 13) | public interface IServerListen {
method formatInfo (line 20) | String formatInfo();
method controlCloseNotice (line 27) | void controlCloseNotice(IControlSocket controlSocket);
FILE: src/main/java/person/pluto/natcross2/serverside/listen/ListenServerControl.java
class ListenServerControl (line 21) | @Slf4j
method add (line 33) | public static boolean add(ServerListenThread serverListen) {
method remove (line 55) | public static boolean remove(Integer listenPort) {
method get (line 69) | public static ServerListenThread get(Integer listenPort) {
method getAll (line 78) | public static List<ServerListenThread> getAll() {
method closeAll (line 87) | public static void closeAll() {
method createNewListenServer (line 102) | public static ServerListenThread createNewListenServer(IListenServerCo...
FILE: src/main/java/person/pluto/natcross2/serverside/listen/ServerListenThread.java
class ServerListenThread (line 32) | @Slf4j
method ServerListenThread (line 48) | public ServerListenThread(IListenServerConfig config) throws Exception {
method run (line 57) | @Override
method process (line 70) | @Override
method procMethod (line 93) | private void procMethod(Socket listenSocket) {
method sendClientWait (line 127) | private boolean sendClientWait(String socketPartKey) {
method start (line 158) | private void start() {
method stopListen (line 201) | private synchronized void stopListen() {
method cancel (line 230) | public synchronized void cancel() {
method stopSocketPart (line 274) | @Override
method clearInvalidSocketPart (line 291) | public void clearInvalidSocketPart() {
method doSetPartClient (line 316) | public boolean doSetPartClient(String socketPartKey, Socket sendSocket) {
method setControlSocket (line 339) | public synchronized void setControlSocket(Socket socket) {
method controlCloseNotice (line 360) | @Override
method getListenPort (line 374) | public Integer getListenPort() {
method getSocketPartList (line 385) | public List<String> getSocketPartList() {
method getSocketPartMap (line 396) | public Map<String, AbsSocketPart> getSocketPartMap() {
method isAlive (line 407) | public boolean isAlive() {
method isCanceled (line 418) | public boolean isCanceled() {
method getConfig (line 429) | public IListenServerConfig getConfig() {
method formatInfo (line 433) | public String formatInfo() {
FILE: src/main/java/person/pluto/natcross2/serverside/listen/clear/ClearInvalidSocketPartThread.java
class ClearInvalidSocketPartThread (line 20) | @Slf4j
method ClearInvalidSocketPartThread (line 34) | public ClearInvalidSocketPartThread(ServerListenThread serverListenThr...
method run (line 38) | @Override
method start (line 43) | @Override
method cancel (line 53) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/listen/clear/IClearInvalidSocketPartThread.java
type IClearInvalidSocketPartThread (line 11) | public interface IClearInvalidSocketPartThread extends Runnable {
method start (line 16) | void start();
method cancel (line 21) | void cancel();
FILE: src/main/java/person/pluto/natcross2/serverside/listen/config/AllSecretSimpleListenServerConfig.java
class AllSecretSimpleListenServerConfig (line 21) | public class AllSecretSimpleListenServerConfig extends SecretSimpleListe...
method AllSecretSimpleListenServerConfig (line 27) | public AllSecretSimpleListenServerConfig(Integer listenPort) {
method newSocketPart (line 31) | @Override
method setBasePasswayKey (line 45) | public void setBasePasswayKey(String key) {
FILE: src/main/java/person/pluto/natcross2/serverside/listen/config/IListenServerConfig.java
type IListenServerConfig (line 21) | public interface IListenServerConfig {
method getListenPort (line 28) | Integer getListenPort();
method newClearInvalidSocketPartThread (line 36) | IClearInvalidSocketPartThread newClearInvalidSocketPartThread(ServerLi...
method newSocketPart (line 44) | AbsSocketPart newSocketPart(ServerListenThread serverListenThread);
method getCharset (line 51) | Charset getCharset();
method newControlSocket (line 60) | IControlSocket newControlSocket(Socket socket, JSONObject config);
method createServerSocket (line 68) | ServerSocket createServerSocket() throws Exception;
FILE: src/main/java/person/pluto/natcross2/serverside/listen/config/MultControlListenServerConfig.java
class MultControlListenServerConfig (line 22) | public class MultControlListenServerConfig implements IListenServerConfig {
method MultControlListenServerConfig (line 28) | public MultControlListenServerConfig(IListenServerConfig baseConfig) {
method createServerSocket (line 32) | @Override
method newControlSocket (line 37) | @Override
method newClearInvalidSocketPartThread (line 44) | @Override
method newSocketPart (line 49) | @Override
method getListenPort (line 54) | @Override
method getCharset (line 59) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/listen/config/SecretSimpleListenServerConfig.java
class SecretSimpleListenServerConfig (line 24) | @Slf4j
method SecretSimpleListenServerConfig (line 33) | public SecretSimpleListenServerConfig(Integer listenPort) {
method newControlSocketChannel (line 37) | @Override
method setBaseAesKey (line 58) | public void setBaseAesKey(String aesKey) {
FILE: src/main/java/person/pluto/natcross2/serverside/listen/config/SimpleListenServerConfig.java
class SimpleListenServerConfig (line 38) | @Slf4j
method SimpleListenServerConfig (line 54) | public SimpleListenServerConfig(Integer listenPort) {
method createServerSocket (line 58) | @Override
method newControlSocketChannel (line 78) | protected SocketChannel<? extends InteractiveModel, ? super Interactiv...
method newControlSocket (line 90) | @Override
method newClearInvalidSocketPartThread (line 100) | @Override
method newSocketPart (line 108) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/listen/control/ControlSocket.java
class ControlSocket (line 26) | @Slf4j
method ControlSocket (line 39) | public ControlSocket(SocketChannel<? extends InteractiveModel, ? super...
method isValid (line 43) | @Override
method close (line 65) | @Override
method sendClientWait (line 94) | @Override
method startRecv (line 108) | @Override
method run (line 123) | @Override
method formatServerListenInfo (line 159) | private String formatServerListenInfo() {
method setServerListen (line 166) | @Override
method addRecvHandler (line 179) | public ControlSocket addRecvHandler(IRecvHandler<? super InteractiveMo...
FILE: src/main/java/person/pluto/natcross2/serverside/listen/control/IControlSocket.java
type IControlSocket (line 13) | public interface IControlSocket {
method isValid (line 20) | boolean isValid();
method sendClientWait (line 28) | boolean sendClientWait(String socketPartKey);
method close (line 33) | void close();
method replaceClose (line 43) | default void replaceClose() {
method startRecv (line 52) | void startRecv();
method setServerListen (line 59) | void setServerListen(IServerListen serverListen);
FILE: src/main/java/person/pluto/natcross2/serverside/listen/control/MultiControlSocket.java
class MultiControlSocket (line 17) | public class MultiControlSocket implements IControlSocket {
method addControlSocket (line 29) | public synchronized boolean addControlSocket(IControlSocket controlSoc...
method isValid (line 33) | @Override
method pollControlSocket (line 46) | protected synchronized IControlSocket pollControlSocket(IControlSocket...
method sendClientWait (line 65) | @Override
method close (line 81) | @Override
method replaceClose (line 97) | @Override
method startRecv (line 102) | @Override
method removeControlSocket (line 115) | private synchronized boolean removeControlSocket(IControlSocket contro...
method setServerListen (line 119) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/listen/recv/ClientHeartHandler.java
class ClientHeartHandler (line 16) | public class ClientHeartHandler implements IRecvHandler<InteractiveModel...
method proc (line 20) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/listen/recv/CommonReplyHandler.java
class CommonReplyHandler (line 20) | @Slf4j
method proc (line 29) | @Override
FILE: src/main/java/person/pluto/natcross2/serverside/listen/recv/IRecvHandler.java
type IRecvHandler (line 13) | public interface IRecvHandler<R, W> {
method proc (line 23) | boolean proc(R model, SocketChannel<? extends R, ? super W> channel) t...
FILE: src/main/java/person/pluto/natcross2/serverside/listen/serversocket/ICreateServerSocket.java
type ICreateServerSocket (line 13) | public interface ICreateServerSocket {
method createServerSocket (line 21) | ServerSocket createServerSocket(int listenPort) throws Exception;
FILE: src/main/java/person/pluto/natcross2/utils/AESUtil.java
class AESUtil (line 23) | @Slf4j
method createKeyByBase64 (line 33) | public static Key createKeyByBase64(String baseKey) {
method createKey (line 44) | public static Key createKey(int length) {
method encryptBase64 (line 70) | public static String encryptBase64(Key key, byte[] content) throws Exc...
method encrypt (line 83) | public static byte[] encrypt(Key key, byte[] content) throws Exception {
method encrypt (line 97) | public static byte[] encrypt(Key key, byte[] content, int inputOffset,...
method encrypt (line 110) | public static byte[] encrypt(Key key, byte[] content, String encryptMe...
method encrypt (line 126) | public static byte[] encrypt(Key key, byte[] content, String encryptMe...
method decryptBase64 (line 141) | public static byte[] decryptBase64(Key key, String result) throws Exce...
method decrypt (line 154) | public static byte[] decrypt(Key key, byte[] result) throws Exception {
method decrypt (line 168) | public static byte[] decrypt(Key key, byte[] result, String decryptMet...
FILE: src/main/java/person/pluto/natcross2/utils/Assert.java
class Assert (line 11) | public final class Assert {
method Assert (line 13) | private Assert() {
method state (line 22) | public static void state(boolean expression, String message) {
method isTrue (line 34) | public static void isTrue(boolean expression, String message) {
method isNull (line 46) | public static void isNull(Object object, String message) {
method notNull (line 58) | public static void notNull(Object object, String message) {
FILE: src/main/java/person/pluto/natcross2/utils/CountWaitLatch.java
class CountWaitLatch (line 14) | public class CountWaitLatch {
class Sync (line 19) | private static final class Sync extends AbstractQueuedSynchronizer {
method Sync (line 22) | Sync(int count) {
method getCount (line 26) | int getCount() {
method tryAcquireShared (line 30) | @Override
method tryReleaseShared (line 45) | @Override
method CountWaitLatch (line 66) | public CountWaitLatch() {
method CountWaitLatch (line 73) | public CountWaitLatch(int count) {
method await (line 85) | public void await() throws InterruptedException {
method await (line 97) | public boolean await(long timeout, TimeUnit unit) throws InterruptedEx...
method countUp (line 104) | public void countUp() {
method countDown (line 111) | public void countDown() {
method getCount (line 118) | public long getCount() {
method toString (line 122) | @Override
FILE: src/main/java/person/pluto/natcross2/utils/MD5Signature.java
class MD5Signature (line 22) | @Slf4j
method toHexString (line 41) | public static String toHexString(byte[] bytes) {
method getRandomStr (line 59) | public static String getRandomStr(int count) {
method intToBytes (line 75) | public static byte[] intToBytes(int source) {
method bytes2int (line 88) | public static int bytes2int(byte[] byteArr) {
method getSignature (line 99) | public static String getSignature(String... params) {
method getSignature (line 112) | public static String getSignature(Charset charset, String... params) {
FILE: src/main/java/person/pluto/natcross2/utils/Tools.java
class Tools (line 20) | @NoArgsConstructor(access = AccessLevel.PRIVATE)
method intToBytes (line 29) | public static byte[] intToBytes(int source) {
method bytes2int (line 40) | public static int bytes2int(byte[] byteArr) {
method streamCopy (line 59) | public static int streamCopy(InputStream inputStream, OutputStream out...
method channelWrite (line 74) | public static int channelWrite(WritableByteChannel channel, ByteBuffer...
FILE: src/test/java/person/pluto/TestMain.java
class TestMain (line 8) | public class TestMain {
method main (line 10) | public static void main(String[] args) throws Exception {
Condensed preview — 94 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (285K chars).
[
{
"path": ".gitignore",
"chars": 32,
"preview": ".*\r\n!/.gitignore\r\ntarget/\r\n*.iml"
},
{
"path": "LICENSE",
"chars": 11545,
"preview": " Apache License\r\n Version 2.0, January 2004\r\n "
},
{
"path": "README.md",
"chars": 2577,
"preview": "# natcross2\n内网穿透工具\n\n*********************\n\n**Q群:438793541**\n**群密码:natcross2**\n\n## natcross是做什么的?\n- 需要自己提供硬件支持、部署的内网穿透工具\n"
},
{
"path": "VERSION.md",
"chars": 643,
"preview": "# VERSION 2.3\r\n\r\n1. 支持多客户端连接同一监听端口,从服务端进行负载分发(场景特殊,主要是面对客户端处在非稳定内网,使用多客户端进行多活保证)\r\n\r\n# VERSION 2.2\r\n\r\n1. socket使用channel方"
},
{
"path": "pom.xml",
"chars": 7225,
"preview": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\r\n xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\r\n "
},
{
"path": "src/main/java/person/pluto/natcross2/ClientApp.java",
"chars": 4589,
"preview": "package person.pluto.natcross2;\r\n\r\nimport person.pluto.natcross2.CommonConstants.ListenDest;\r\nimport person.pluto.natcro"
},
{
"path": "src/main/java/person/pluto/natcross2/CommonConstants.java",
"chars": 1367,
"preview": "package person.pluto.natcross2;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\n\r\n/**\r\n * <p>\r\n * 公共参数\r"
},
{
"path": "src/main/java/person/pluto/natcross2/ServerApp.java",
"chars": 6338,
"preview": "package person.pluto.natcross2;\r\n\r\nimport org.apache.commons.lang3.StringUtils;\r\nimport person.pluto.natcross2.CommonCon"
},
{
"path": "src/main/java/person/pluto/natcross2/api/IBelongControl.java",
"chars": 435,
"preview": "package person.pluto.natcross2.api;\n\n/**\n * <p>\n * 通知上次停止的统一类,为适应不同的类型进行不同的函数封装\n * </p>\n *\n * @author Pluto\n * @since 20"
},
{
"path": "src/main/java/person/pluto/natcross2/api/IHttpRouting.java",
"chars": 347,
"preview": "package person.pluto.natcross2.api;\r\n\r\nimport person.pluto.natcross2.model.HttpRoute;\r\n\r\n/**\r\n * <p>\r\n * http 路由器\r\n * </"
},
{
"path": "src/main/java/person/pluto/natcross2/api/passway/SecretPassway.java",
"chars": 4285,
"preview": "package person.pluto.natcross2.api.passway;\r\n\r\nimport lombok.Data;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.plu"
},
{
"path": "src/main/java/person/pluto/natcross2/api/passway/SimplePassway.java",
"chars": 6333,
"preview": "package person.pluto.natcross2.api.passway;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r"
},
{
"path": "src/main/java/person/pluto/natcross2/api/secret/AESSecret.java",
"chars": 810,
"preview": "package person.pluto.natcross2.api.secret;\r\n\r\nimport lombok.Data;\r\nimport person.pluto.natcross2.utils.AESUtil;\r\n\r\nimpor"
},
{
"path": "src/main/java/person/pluto/natcross2/api/secret/ISecret.java",
"chars": 548,
"preview": "package person.pluto.natcross2.api.secret;\r\n\r\n/**\r\n * <p>\r\n * 加密方法\r\n * </p>\r\n *\r\n * @author Pluto\r\n * @since 2020-01-08 "
},
{
"path": "src/main/java/person/pluto/natcross2/api/socketpart/AbsSocketPart.java",
"chars": 1775,
"preview": "package person.pluto.natcross2.api.socketpart;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.Data;\r\nimport lombok.Getter;"
},
{
"path": "src/main/java/person/pluto/natcross2/api/socketpart/HttpRouteSocketPart.java",
"chars": 6576,
"preview": "package person.pluto.natcross2.api.socketpart;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natcross2.api.I"
},
{
"path": "src/main/java/person/pluto/natcross2/api/socketpart/SecretSocketPart.java",
"chars": 4330,
"preview": "package person.pluto.natcross2.api.socketpart;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport lombok.extern.slf"
},
{
"path": "src/main/java/person/pluto/natcross2/api/socketpart/SimpleSocketPart.java",
"chars": 3934,
"preview": "package person.pluto.natcross2.api.socketpart;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport lombok.extern.slf"
},
{
"path": "src/main/java/person/pluto/natcross2/channel/Channel.java",
"chars": 969,
"preview": "package person.pluto.natcross2.channel;\r\n\r\nimport java.io.Closeable;\r\nimport java.nio.charset.Charset;\r\n\r\n/**\r\n * <p>\r\n "
},
{
"path": "src/main/java/person/pluto/natcross2/channel/InteractiveChannel.java",
"chars": 1874,
"preview": "package person.pluto.natcross2.channel;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport person.pluto.natcross2.model."
},
{
"path": "src/main/java/person/pluto/natcross2/channel/JsonChannel.java",
"chars": 2123,
"preview": "package person.pluto.natcross2.channel;\r\n\r\nimport com.alibaba.fastjson.JSON;\r\nimport com.alibaba.fastjson.JSONAware;\r\nim"
},
{
"path": "src/main/java/person/pluto/natcross2/channel/LengthChannel.java",
"chars": 5039,
"preview": "package person.pluto.natcross2.channel;\r\n\r\nimport person.pluto.natcross2.utils.Tools;\r\n\r\nimport java.io.IOException;\r\nim"
},
{
"path": "src/main/java/person/pluto/natcross2/channel/SecretInteractiveChannel.java",
"chars": 3670,
"preview": "package person.pluto.natcross2.channel;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport lombok.*;\r\nimport lombok.Equa"
},
{
"path": "src/main/java/person/pluto/natcross2/channel/SocketChannel.java",
"chars": 766,
"preview": "package person.pluto.natcross2.channel;\n\nimport java.io.IOException;\nimport java.net.Socket;\n\n/**\n * <p>\n * socket通道\n * "
},
{
"path": "src/main/java/person/pluto/natcross2/channel/StringChannel.java",
"chars": 1954,
"preview": "package person.pluto.natcross2.channel;\r\n\r\nimport java.io.IOException;\r\nimport java.net.Socket;\r\nimport java.nio.charset"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/ClientControlThread.java",
"chars": 6162,
"preview": "package person.pluto.natcross2.clientside;\r\n\r\nimport lombok.Getter;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pl"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/adapter/IClientAdapter.java",
"chars": 1687,
"preview": "package person.pluto.natcross2.clientside.adapter;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\nimport perso"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/adapter/InteractiveSimpleClientAdapter.java",
"chars": 9306,
"preview": "package person.pluto.natcross2.clientside.adapter;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport org.apache.commons.lang3"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/config/AllSecretInteractiveClientConfig.java",
"chars": 1205,
"preview": "package person.pluto.natcross2.clientside.config;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport person.pluto.n"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/config/HttpRouteClientConfig.java",
"chars": 9707,
"preview": "package person.pluto.natcross2.clientside.config;\r\n\r\nimport lombok.Getter;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport or"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/config/IClientConfig.java",
"chars": 2335,
"preview": "package person.pluto.natcross2.clientside.config;\r\n\r\nimport person.pluto.natcross2.api.socketpart.AbsSocketPart;\r\nimport"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/config/InteractiveClientConfig.java",
"chars": 4191,
"preview": "package person.pluto.natcross2.clientside.config;\r\n\r\nimport lombok.Data;\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport pers"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/config/SecretInteractiveClientConfig.java",
"chars": 1590,
"preview": "package person.pluto.natcross2.clientside.config;\r\n\r\nimport lombok.Data;\r\nimport lombok.EqualsAndHashCode;\r\nimport lombo"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/handler/CommonReplyHandler.java",
"chars": 1430,
"preview": "package person.pluto.natcross2.clientside.handler;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport lombok.extern"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/handler/IClientHandler.java",
"chars": 490,
"preview": "package person.pluto.natcross2.clientside.handler;\r\n\r\nimport person.pluto.natcross2.clientside.adapter.IClientAdapter;\r\n"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/handler/ServerHeartHandler.java",
"chars": 1517,
"preview": "package person.pluto.natcross2.clientside.handler;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natcross2.c"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/handler/ServerWaitClientHandler.java",
"chars": 1204,
"preview": "package person.pluto.natcross2.clientside.handler;\r\n\r\nimport person.pluto.natcross2.clientside.adapter.IClientAdapter;\r\n"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/heart/ClientHeartThread.java",
"chars": 4328,
"preview": "package person.pluto.natcross2.clientside.heart;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport lombok.extern.s"
},
{
"path": "src/main/java/person/pluto/natcross2/clientside/heart/IClientHeartThread.java",
"chars": 340,
"preview": "package person.pluto.natcross2.clientside.heart;\n\n/**\n * <p>\n * 心跳测试线程\n * </p>\n *\n * @author Pluto\n * @since 2020-01-08 "
},
{
"path": "src/main/java/person/pluto/natcross2/common/CommonFormat.java",
"chars": 1524,
"preview": "package person.pluto.natcross2.common;\n\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport org.apache.co"
},
{
"path": "src/main/java/person/pluto/natcross2/common/Optional.java",
"chars": 526,
"preview": "package person.pluto.natcross2.common;\r\n\r\n/**\r\n * <p>\r\n * 操作对象,主要是让值能够通过引用进行传递\r\n * </p>\r\n *\r\n * @param <T> 存放的类型\r\n * @au"
},
{
"path": "src/main/java/person/pluto/natcross2/executor/IExecutor.java",
"chars": 2101,
"preview": "package person.pluto.natcross2.executor;\r\n\r\nimport java.util.concurrent.ScheduledFuture;\r\n\r\n/**\r\n * <p>\r\n * 执行器实现\r\n * </"
},
{
"path": "src/main/java/person/pluto/natcross2/executor/NatcrossExecutor.java",
"chars": 3054,
"preview": "package person.pluto.natcross2.executor;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\n\r\nimport java."
},
{
"path": "src/main/java/person/pluto/natcross2/executor/SimpleExecutor.java",
"chars": 1344,
"preview": "package person.pluto.natcross2.executor;\r\n\r\nimport java.util.concurrent.ExecutorService;\r\nimport java.util.concurrent.Ex"
},
{
"path": "src/main/java/person/pluto/natcross2/model/HttpRoute.java",
"chars": 934,
"preview": "package person.pluto.natcross2.model;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\n\r\n/**"
},
{
"path": "src/main/java/person/pluto/natcross2/model/InteractiveModel.java",
"chars": 2638,
"preview": "package person.pluto.natcross2.model;\r\n\r\nimport com.alibaba.fastjson.JSON;\r\nimport com.alibaba.fastjson.JSONAware;\r\nimpo"
},
{
"path": "src/main/java/person/pluto/natcross2/model/NatcrossResultModel.java",
"chars": 3093,
"preview": "package person.pluto.natcross2.model;\n\nimport com.alibaba.fastjson.JSONAware;\nimport com.alibaba.fastjson.JSONObject;\n\ni"
},
{
"path": "src/main/java/person/pluto/natcross2/model/SecretInteractiveModel.java",
"chars": 3459,
"preview": "package person.pluto.natcross2.model;\r\n\r\nimport com.alibaba.fastjson.JSON;\r\nimport com.alibaba.fastjson.JSONObject;\r\nimp"
},
{
"path": "src/main/java/person/pluto/natcross2/model/enumeration/InteractiveTypeEnum.java",
"chars": 970,
"preview": "package person.pluto.natcross2.model.enumeration;\n\nimport lombok.Getter;\nimport org.apache.commons.lang3.StringUtils;\n\n/"
},
{
"path": "src/main/java/person/pluto/natcross2/model/enumeration/NatcrossResultEnum.java",
"chars": 1178,
"preview": "package person.pluto.natcross2.model.enumeration;\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport person.pluto.nat"
},
{
"path": "src/main/java/person/pluto/natcross2/model/interactive/ClientConnectModel.java",
"chars": 341,
"preview": "package person.pluto.natcross2.model.interactive;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.N"
},
{
"path": "src/main/java/person/pluto/natcross2/model/interactive/ClientControlModel.java",
"chars": 340,
"preview": "package person.pluto.natcross2.model.interactive;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.N"
},
{
"path": "src/main/java/person/pluto/natcross2/model/interactive/ServerWaitModel.java",
"chars": 341,
"preview": "package person.pluto.natcross2.model.interactive;\n\nimport lombok.AllArgsConstructor;\nimport lombok.Data;\nimport lombok.N"
},
{
"path": "src/main/java/person/pluto/natcross2/nio/INioProcessor.java",
"chars": 311,
"preview": "package person.pluto.natcross2.nio;\n\nimport java.nio.channels.SelectionKey;\n\n/**\n * <p>\n * nio 执行器\n * </p>\n *\n * @author"
},
{
"path": "src/main/java/person/pluto/natcross2/nio/NioHallows.java",
"chars": 10563,
"preview": "package person.pluto.natcross2.nio;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.Getter;\r\nimport lombok.NoArgsConstructo"
},
{
"path": "src/main/java/person/pluto/natcross2/nio/ProcesserHolder.java",
"chars": 807,
"preview": "package person.pluto.natcross2.nio;\r\n\r\nimport lombok.AllArgsConstructor;\r\nimport lombok.Data;\r\n\r\nimport java.nio.channel"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/ClientServiceThread.java",
"chars": 5800,
"preview": "package person.pluto.natcross2.serverside.client;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natcross2.ex"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/adapter/DefaultReadAheadPassValueAdapter.java",
"chars": 718,
"preview": "package person.pluto.natcross2.serverside.client.adapter;\r\n\r\nimport person.pluto.natcross2.model.InteractiveModel;\r\nimpo"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/adapter/IClientServiceAdapter.java",
"chars": 360,
"preview": "package person.pluto.natcross2.serverside.client.adapter;\n\nimport java.net.Socket;\n\n/**\n * <p>\n * 客户端服务适配器\n * </p>\n *\n *"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/adapter/PassValueNextEnum.java",
"chars": 615,
"preview": "package person.pluto.natcross2.serverside.client.adapter;\n\nimport lombok.Getter;\n\n/**\n * <p>\n * 传值适配器的handler回复信息\n * </p"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/adapter/ReadAheadPassValueAdapter.java",
"chars": 3192,
"preview": "package person.pluto.natcross2.serverside.client.adapter;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natc"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/config/IClientServiceConfig.java",
"chars": 1115,
"preview": "package person.pluto.natcross2.serverside.client.config;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\nimport"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/config/SecretSimpleClientServiceConfig.java",
"chars": 1546,
"preview": "package person.pluto.natcross2.serverside.client.config;\r\n\r\nimport lombok.Data;\r\nimport lombok.EqualsAndHashCode;\r\nimpor"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/config/SimpleClientServiceConfig.java",
"chars": 2760,
"preview": "package person.pluto.natcross2.serverside.client.config;\r\n\r\nimport lombok.NoArgsConstructor;\r\nimport person.pluto.natcro"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/handler/DefaultInteractiveProcessHandler.java",
"chars": 685,
"preview": "package person.pluto.natcross2.serverside.client.handler;\r\n\r\nimport person.pluto.natcross2.serverside.client.process.Cli"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/handler/IPassValueHandler.java",
"chars": 660,
"preview": "package person.pluto.natcross2.serverside.client.handler;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\nimpor"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/handler/InteractiveProcessHandler.java",
"chars": 2852,
"preview": "package person.pluto.natcross2.serverside.client.handler;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natc"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/process/ClientConnectProcess.java",
"chars": 2489,
"preview": "package person.pluto.natcross2.serverside.client.process;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\nimpor"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/process/ClientControlProcess.java",
"chars": 2226,
"preview": "package person.pluto.natcross2.serverside.client.process;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\nimpor"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/client/process/IProcess.java",
"chars": 770,
"preview": "package person.pluto.natcross2.serverside.client.process;\n\nimport person.pluto.natcross2.channel.SocketChannel;\nimport p"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/IServerListen.java",
"chars": 478,
"preview": "package person.pluto.natcross2.serverside.listen;\r\n\r\nimport person.pluto.natcross2.serverside.listen.control.IControlSoc"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/ListenServerControl.java",
"chars": 3028,
"preview": "package person.pluto.natcross2.serverside.listen;\n\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport lo"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/ServerListenThread.java",
"chars": 12794,
"preview": "package person.pluto.natcross2.serverside.listen;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natcross2.ap"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/clear/ClearInvalidSocketPartThread.java",
"chars": 1873,
"preview": "package person.pluto.natcross2.serverside.listen.clear;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport lombok.e"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/clear/IClearInvalidSocketPartThread.java",
"chars": 326,
"preview": "package person.pluto.natcross2.serverside.listen.clear;\r\n\r\n/**\r\n * <p>\r\n * 清理无效端口 线程\r\n * </p>\r\n *\r\n * @author Pluto\r\n * "
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/config/AllSecretSimpleListenServerConfig.java",
"chars": 1326,
"preview": "package person.pluto.natcross2.serverside.listen.config;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport person."
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/config/IListenServerConfig.java",
"chars": 1481,
"preview": "package person.pluto.natcross2.serverside.listen.config;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport person.pluto"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/config/MultControlListenServerConfig.java",
"chars": 2050,
"preview": "package person.pluto.natcross2.serverside.listen.config;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport person.pluto"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/config/SecretSimpleListenServerConfig.java",
"chars": 1674,
"preview": "package person.pluto.natcross2.serverside.listen.config;\r\n\r\nimport lombok.Data;\r\nimport lombok.EqualsAndHashCode;\r\nimpor"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/config/SimpleListenServerConfig.java",
"chars": 4417,
"preview": "package person.pluto.natcross2.serverside.listen.config;\r\n\r\nimport com.alibaba.fastjson.JSONObject;\r\nimport lombok.Data;"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/control/ControlSocket.java",
"chars": 5900,
"preview": "package person.pluto.natcross2.serverside.listen.control;\r\n\r\nimport lombok.extern.slf4j.Slf4j;\r\nimport person.pluto.natc"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/control/IControlSocket.java",
"chars": 968,
"preview": "package person.pluto.natcross2.serverside.listen.control;\r\n\r\nimport person.pluto.natcross2.serverside.listen.IServerList"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/control/MultiControlSocket.java",
"chars": 4133,
"preview": "package person.pluto.natcross2.serverside.listen.control;\n\nimport person.pluto.natcross2.serverside.listen.IServerListen"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/recv/ClientHeartHandler.java",
"chars": 1256,
"preview": "package person.pluto.natcross2.serverside.listen.recv;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\nimport p"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/recv/CommonReplyHandler.java",
"chars": 1403,
"preview": "package person.pluto.natcross2.serverside.listen.recv;\r\n\r\nimport lombok.Getter;\r\nimport lombok.Setter;\r\nimport lombok.ex"
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/recv/IRecvHandler.java",
"chars": 467,
"preview": "package person.pluto.natcross2.serverside.listen.recv;\r\n\r\nimport person.pluto.natcross2.channel.SocketChannel;\r\n\r\n/**\r\n "
},
{
"path": "src/main/java/person/pluto/natcross2/serverside/listen/serversocket/ICreateServerSocket.java",
"chars": 393,
"preview": "package person.pluto.natcross2.serverside.listen.serversocket;\r\n\r\nimport java.net.ServerSocket;\r\n\r\n/**\r\n * <p>\r\n * 创建服务端"
},
{
"path": "src/main/java/person/pluto/natcross2/utils/AESUtil.java",
"chars": 4707,
"preview": "package person.pluto.natcross2.utils;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\nimport lombok.ext"
},
{
"path": "src/main/java/person/pluto/natcross2/utils/Assert.java",
"chars": 1233,
"preview": "package person.pluto.natcross2.utils;\r\n\r\n/**\r\n * <p>\r\n * 断言\r\n * </p>\r\n *\r\n * @author Pluto\r\n * @since 2021-04-13 13:46:5"
},
{
"path": "src/main/java/person/pluto/natcross2/utils/CountWaitLatch.java",
"chars": 2878,
"preview": "package person.pluto.natcross2.utils;\r\n\r\nimport java.util.concurrent.TimeUnit;\r\nimport java.util.concurrent.locks.Abstra"
},
{
"path": "src/main/java/person/pluto/natcross2/utils/MD5Signature.java",
"chars": 3302,
"preview": "package person.pluto.natcross2.utils;\n\nimport lombok.AccessLevel;\nimport lombok.NoArgsConstructor;\nimport lombok.extern."
},
{
"path": "src/main/java/person/pluto/natcross2/utils/Tools.java",
"chars": 1912,
"preview": "package person.pluto.natcross2.utils;\r\n\r\nimport lombok.AccessLevel;\r\nimport lombok.NoArgsConstructor;\r\n\r\nimport java.io."
},
{
"path": "src/main/resources/logback.xml",
"chars": 1053,
"preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<configuration scan=\"true\" scanPeriod=\"60 seconds\"\r\n\tdebug=\"false\">\r\n\r\n\t<!-- 日志记"
},
{
"path": "src/test/java/person/pluto/TestMain.java",
"chars": 368,
"preview": "package person.pluto;\r\n\r\nimport java.security.Key;\r\nimport java.util.Base64;\r\n\r\nimport person.pluto.natcross2.utils.AESU"
}
]
About this extraction
This page contains the full source code of the Pluto-Whong/natcross2 GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 94 files (238.6 KB), approximately 60.8k tokens, and a symbol index with 555 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.