Merge pull request #360 from ehang-io/dev

Dev
This commit is contained in:
ffdfgdfg 2020-01-08 23:33:31 +08:00 committed by GitHub
commit a8c0f1653d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
70 changed files with 1622 additions and 450 deletions

View File

@ -1,10 +1,10 @@
FROM golang as builder
WORKDIR /go/src/github.com/cnlh/nps
WORKDIR /go/src/ehang.io/nps
COPY . .
RUN go get -d -v ./...
RUN CGO_ENABLED=0 go build -ldflags="-w -s -extldflags -static" ./cmd/npc/npc.go
FROM scratch
COPY --from=builder /go/src/github.com/cnlh/nps/npc /
COPY --from=builder /go/src/ehang.io/nps/npc /
VOLUME /conf
ENTRYPOINT ["/npc"]

View File

@ -1,11 +1,11 @@
FROM golang as builder
WORKDIR /go/src/github.com/cnlh/nps
WORKDIR /go/src/ehang.io/nps
COPY . .
RUN go get -d -v ./...
RUN CGO_ENABLED=0 go build -ldflags="-w -s -extldflags -static" ./cmd/nps/nps.go
FROM scratch
COPY --from=builder /go/src/github.com/cnlh/nps/nps /
COPY --from=builder /go/src/github.com/cnlh/nps/web /web
COPY --from=builder /go/src/ehang.io/nps/nps /
COPY --from=builder /go/src/ehang.io/nps/web /web
VOLUME /conf
CMD ["/nps"]

102
README.md
View File

@ -1,78 +1,76 @@
# nps
![](https://img.shields.io/github/stars/cnlh/nps.svg) ![](https://img.shields.io/github/forks/cnlh/nps.svg)
# NPS
![](https://img.shields.io/github/stars/ehang-io/nps.svg) ![](https://img.shields.io/github/forks/ehang-io/nps.svg)
[![Gitter](https://badges.gitter.im/cnlh-nps/community.svg)](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/cnlh/nps.svg?branch=master)](https://travis-ci.org/cnlh/nps)
![GitHub All Releases](https://img.shields.io/github/downloads/cnlh/nps/total)
[![Build Status](https://travis-ci.org/ehang-io/nps.svg?branch=master)](https://travis-ci.org/ehang-io/nps)
![GitHub All Releases](https://img.shields.io/github/downloads/ehang-io/nps/total)
nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议访问内网网站、本地支付接口调试、ssh访问、远程桌面内网dns解析等等……此外还**支持内网http代理、内网socks5代理**、**p2p等**并带有功能强大的web管理端。
[README](https://github.com/ehang-io/nps/blob/master/README.md)|[中文文档](https://github.com/ehang-io/nps/blob/master/README_zh.md)
NPS is a lightweight, high-performance, powerful **intranet penetration** proxy server, with a powerful web management terminal.
## 背景
![image](https://github.com/cnlh/nps/blob/master/image/web.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
1. 做微信公众号开发、小程序开发等----> 域名代理模式
## Feature
2. 想在外网通过ssh连接内网的机器做云服务器到内网服务器端口的映射----> tcp代理模式
- Comprehensive protocol support, compatible with almost all commonly used protocols, such as tcp, udp, http(s), socks5, p2p, http proxy ...
- Full platform compatibility (linux, windows, macos, Qunhui, etc.), support installation as a system service simply.
- Comprehensive control, both client and server control are allowed.
- Https integration, support to convert backend proxy and web services to https, and support multiple certificates.
- Just simple configuration on web ui can complete most requirements.
- Complete information display, such as traffic, system information, real-time bandwidth, client version, etc.
- Powerful extension functions, everything is available (cache, compression, encryption, traffic limit, bandwidth limit, port reuse, etc.)
- Domain name resolution has functions such as custom headers, 404 page configuration, host modification, site protection, URL routing, and pan-resolution.
- Multi-user and user registration support on server.
3. 在非内网环境下使用内网dns或者需要通过udp访问内网机器等----> udp代理模式
**Didn't find the feature you want? It doesn't matter, click [Enter the document](https://ehang-io.github.io/nps/) to find it!**
4. 在外网使用HTTP代理访问内网站点----> http代理模式
## Quick start
5. 搭建一个内网穿透ss在外网如同使用内网vpn一样访问内网资源或者设备----> socks5代理模式
## 特点
- 协议支持全面兼容几乎所有常用协议例如tcp、udp、http(s)、socks5、p2p、http代理...
- 全平台兼容(linux、windows、macos、群辉等),支持一键安装为系统服务
- 控制全面,同时支持服务端和客户端控制
- https集成支持将后端代理和web服务转成https同时支持多证书
- 操作简单只需简单的配置即可在web ui上完成其余操作
- 展示信息全面,流量、系统信息、即时带宽、客户端版本等
- 扩展功能强大,该有的都有了(缓存、压缩、加密、流量限制、带宽限制、端口复用等等)
- 域名解析具备自定义header、404页面配置、host修改、站点保护、URL路由、泛解析等功能
- 服务端支持多用户和用户注册功能
### Installation
**没找到你想要的功能?不要紧,点击[进入文档](https://ehang-io.github.io/nps)查找吧**
## 快速开始
> [releases](https://github.com/ehang-io/nps/releases)
### 安装
> [releases](https://github.com/cnlh/nps/releases)
Download the corresponding system version, the server and client are separate.
下载对应的系统版本即可,服务端和客户端是单独的
### Server start
### 服务端启动
下载完服务器压缩包后,解压,然后进入解压后的文件夹
After downloading the server compressed package, unzip it, and then enter the unzipped folder.
- 执行安装命令
- execute installation command
对于linux|darwin ```sudo ./nps install```
For linux、darwin ```sudo ./nps install```
对于windows管理员身份运行cmd进入安装目录 ```nps.exe install```
For windows, run cmd as administrator and enter the installation directory ```nps.exe install```
- 启动
- start up
对于linux|darwin ```sudo nps start```
For linux、darwin ```sudo nps start```
对于windows管理员身份运行cmd进入程序目录 ```nps.exe start```
For windows, run cmd as administrator and enter the program directory ```nps.exe start```
```安装后windows配置文件位于 C:\Program Files\npslinux和darwin位于/etc/nps```
```After installation, the windows configuration file is located at C:\Program Files\nps, linux or darwin is located at /etc/nps```
**如果发现没有启动成功,可以查看日志(Windows日志文件位于当前运行目录下linux和darwin位于/var/log/nps.log)**
- 访问服务端ip:web服务端口默认为8080
- 使用用户名和密码登陆默认admin/123正式使用一定要更改
- 创建客户端
**If you don't find it started successfully, you can check the log (Windows log files are located in the current running directory, linux and darwin are located in /var/log/nps.log).**
### 客户端连接
- 点击web管理中客户端前的+号,复制启动命令
- 执行启动命令linux直接执行即可windows将./npc换成npc.exe用cmd执行
- Access server IP:web service port (default is 8080).
- Login with username and password (default is admin/123, must be modified when officially used).
- Create a client.
如果需要注册到系统服务可查看[注册到系统服务](https://ehang-io.github.io/nps/#/use?id=注册到系统服务)
### Client connection
- Click the + sign in front of the client in web management and copy the startup command.
- Execute the startup command, Linux can be executed directly, Windows will replace ./npc with npc.exe and execute it with cmd.
### 配置
- 客户端连接后在web中配置对应穿透服务即可
- 更多高级用法见[完整文档](https://ehang-io.github.io/nps)
## 贡献
- 如果遇到bug可以直接提交至dev分支
- 使用遇到问题可以通过issues反馈
- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支
- 如果有新的功能特性反馈可以通过issues或者qq群反馈
If you need to register to the system service, you can check [Register to the system service](https://ehang-io.github.io/nps/#/use?id=注册到系统服务)
### Configuration
- After the client connects, configure the corresponding penetration service in the web.
- For more advanced usage, see [Complete Documentation](https://ehang-io.github.io/nps/)
## Contribution
- If you encounter a bug, you can submit it to the dev branch directly.
- If you encounter a problem, you can feedback through the issue.
- The project is under development, and there is still a lot of room for improvement. If you can contribute code, please submit PR to the dev branch.
- If there is feedback on new features, you can feedback via issues or qq group.

80
README_zh.md Normal file
View File

@ -0,0 +1,80 @@
# nps
![](https://img.shields.io/github/stars/ehang-io/nps.svg) ![](https://img.shields.io/github/forks/ehang-io/nps.svg)
[![Gitter](https://badges.gitter.im/cnlh-nps/community.svg)](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/ehang-io/nps.svg?branch=master)](https://travis-ci.org/ehang-io/nps)
![GitHub All Releases](https://img.shields.io/github/downloads/ehang-io/nps/total)
[README](https://github.com/ehang-io/nps/blob/master/README.md)|[中文文档](https://github.com/ehang-io/nps/blob/master/README_zh.md)
nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议访问内网网站、本地支付接口调试、ssh访问、远程桌面内网dns解析等等……此外还**支持内网http代理、内网socks5代理**、**p2p等**并带有功能强大的web管理端。
## 背景
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
1. 做微信公众号开发、小程序开发等----> 域名代理模式
2. 想在外网通过ssh连接内网的机器做云服务器到内网服务器端口的映射----> tcp代理模式
3. 在非内网环境下使用内网dns或者需要通过udp访问内网机器等----> udp代理模式
4. 在外网使用HTTP代理访问内网站点----> http代理模式
5. 搭建一个内网穿透ss在外网如同使用内网vpn一样访问内网资源或者设备----> socks5代理模式
## 特点
- 协议支持全面兼容几乎所有常用协议例如tcp、udp、http(s)、socks5、p2p、http代理...
- 全平台兼容(linux、windows、macos、群辉等),支持一键安装为系统服务
- 控制全面,同时支持服务端和客户端控制
- https集成支持将后端代理和web服务转成https同时支持多证书
- 操作简单只需简单的配置即可在web ui上完成其余操作
- 展示信息全面,流量、系统信息、即时带宽、客户端版本等
- 扩展功能强大,该有的都有了(缓存、压缩、加密、流量限制、带宽限制、端口复用等等)
- 域名解析具备自定义header、404页面配置、host修改、站点保护、URL路由、泛解析等功能
- 服务端支持多用户和用户注册功能
**没找到你想要的功能?不要紧,点击[进入文档](https://ehang-io.github.io/nps)查找吧**
## 快速开始
### 安装
> [releases](https://github.com/ehang-io/nps/releases)
下载对应的系统版本即可,服务端和客户端是单独的
### 服务端启动
下载完服务器压缩包后,解压,然后进入解压后的文件夹
- 执行安装命令
对于linux|darwin ```sudo ./nps install```
对于windows管理员身份运行cmd进入安装目录 ```nps.exe install```
- 启动
对于linux|darwin ```sudo nps start```
对于windows管理员身份运行cmd进入程序目录 ```nps.exe start```
```安装后windows配置文件位于 C:\Program Files\npslinux和darwin位于/etc/nps```
**如果发现没有启动成功,可以查看日志(Windows日志文件位于当前运行目录下linux和darwin位于/var/log/nps.log)**
- 访问服务端ip:web服务端口默认为8080
- 使用用户名和密码登陆默认admin/123正式使用一定要更改
- 创建客户端
### 客户端连接
- 点击web管理中客户端前的+号,复制启动命令
- 执行启动命令linux直接执行即可windows将./npc换成npc.exe用cmd执行
如果需要注册到系统服务可查看[注册到系统服务](https://ehang-io.github.io/nps/#/use?id=注册到系统服务)
### 配置
- 客户端连接后在web中配置对应穿透服务即可
- 更多高级用法见[完整文档](https://ehang-io.github.io/nps/)
## 贡献
- 如果遇到bug可以直接提交至dev分支
- 使用遇到问题可以通过issues反馈
- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支
- 如果有新的功能特性反馈可以通过issues或者qq群反馈

View File

@ -11,16 +11,16 @@ import (
"sync"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/mux"
"ehang.io/nps/lib/version"
"ehang.io/nps/server/connection"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/lib/version"
"github.com/cnlh/nps/server/connection"
"github.com/cnlh/nps/server/tool"
)
type Client struct {

View File

@ -14,16 +14,16 @@ git checkout v1.2.0
go install -v ./cmd/fyne
#fyne package -os android fyne.io/fyne/cmd/hello
echo "fyne install success"
mkdir -p /go/src/github.com/cnlh/nps
cp -R /app/* /go/src/github.com/cnlh/nps
cd /go/src/github.com/cnlh/nps
mkdir -p /go/src/ehang.io/nps
cp -R /app/* /go/src/ehang.io/nps
cd /go/src/ehang.io/nps
#go get -u fyne.io/fyne fyne.io/fyne/cmd/fyne
rm cmd/npc/sdk.go
#go get -u ./...
#go mod tidy
#rm -rf /go/src/golang.org/x/mobile
echo "tidy success"
cd /go/src/github.com/cnlh/nps
cd /go/src/ehang.io/nps
go mod vendor
cd vendor
cp -R * /go/src

View File

@ -1,5 +1,5 @@
#/bash/sh
export VERSION=0.25.4
export VERSION=0.26.0
sudo apt-get install gcc-mingw-w64-i686
env GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -ldflags "-s -w -extldflags -static -extldflags -static" -buildmode=c-shared -o npc_sdk.dll cmd/npc/sdk.go

View File

@ -11,11 +11,11 @@ import (
"github.com/astaxie/beego/logs"
"github.com/xtaci/kcp-go"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/mux"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/mux"
)
type TRPClient struct {
@ -44,6 +44,7 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl st
var NowStatus int
var CloseClient bool
//start
func (s *TRPClient) Start() {
CloseClient = false

View File

@ -19,12 +19,12 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/version"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/version"
"github.com/xtaci/kcp-go"
"golang.org/x/net/proxy"
)

View File

@ -7,10 +7,10 @@ import (
"strings"
"time"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/sheap"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/sheap"
"github.com/pkg/errors"
)

View File

@ -8,14 +8,14 @@ import (
"sync"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/mux"
"ehang.io/nps/server/proxy"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/server/proxy"
"github.com/xtaci/kcp-go"
)

View File

@ -5,7 +5,7 @@ import (
"log"
"os"
"github.com/cnlh/nps/lib/common"
"ehang.io/nps/lib/common"
)
func RegisterLocalIp(server string, vKey string, tp string, proxyUrl string, hour int) {

View File

@ -1,16 +1,16 @@
package main
import (
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/install"
"ehang.io/nps/lib/version"
"flag"
"fmt"
"github.com/astaxie/beego/logs"
"github.com/ccding/go-stun/stun"
"github.com/cnlh/nps/client"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/install"
"github.com/cnlh/nps/lib/version"
"github.com/kardianos/service"
"os"
"runtime"
@ -114,7 +114,7 @@ func main() {
}
err := service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
return
}

View File

@ -2,10 +2,10 @@ package main
import (
"C"
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/version"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/client"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/version"
)
var cl *client.TRPClient

View File

@ -1,26 +1,26 @@
package main
import (
"ehang.io/nps/lib/install"
"flag"
"github.com/cnlh/nps/lib/install"
"log"
"os"
"path/filepath"
"runtime"
"strings"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/daemon"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/version"
"ehang.io/nps/server"
"ehang.io/nps/server/connection"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/version"
"github.com/cnlh/nps/server"
"github.com/cnlh/nps/server/connection"
"github.com/cnlh/nps/server/tool"
"github.com/cnlh/nps/web/routers"
"ehang.io/nps/web/routers"
"github.com/kardianos/service"
)
@ -94,13 +94,13 @@ func main() {
}
err = service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
return
case "start", "restart", "stop", "uninstall":
err := service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
return
case "update":

View File

@ -7,7 +7,7 @@ nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务
## 背景
![image](https://github.com/cnlh/nps/blob/master/image/web.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
1. 做微信公众号开发、小程序开发等----> 域名代理模式

View File

@ -1,6 +1,6 @@
![logo](logo.svg)
# NPS <small>0.25.4</small>
# NPS <small>0.26.0</small>
> 一款轻量级、高性能、功能强大的内网穿透代理服务器
@ -12,5 +12,5 @@
- 全平台兼容,一键注册为服务
[GitHub](https://github.com/cnlh/nps/)
[GitHub](https://github.com/ehang-io/nps/)
[开始使用](#nps)

View File

@ -43,4 +43,4 @@ POST /auth/getauthkey
- **此文档近期可能更新较慢,建议自行抓包**
为方便第三方扩展在web模式下可利用webAPI进行相关操作详情见
[webAPI文档](https://github.com/cnlh/nps/wiki/webAPI%E6%96%87%E6%A1%A3)
[webAPI文档](https://github.com/ehang-io/nps/wiki/webAPI%E6%96%87%E6%A1%A3)

View File

@ -2,6 +2,6 @@
如果您觉得nps对你有帮助欢迎给予我们一定捐助也是帮助nps更好的发展。
## 支付宝
![image](https://github.com/cnlh/nps/blob/master/image/donation_zfb.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/donation_zfb.png?raw=true)
## 微信
![image](https://github.com/cnlh/nps/blob/master/image/donation_wx.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/donation_wx.png?raw=true)

View File

@ -29,7 +29,7 @@
hideOtherSidebarContent: true, // whether or not to hide other sidebar content
},
plugins: [
EditOnGithubPlugin.create("https://github.com/cnlh/nps/tree/master/docs/", "", "在github上编辑"),
EditOnGithubPlugin.create("https://github.com/ehang-io/nps/tree/master/docs/", "", "在github上编辑"),
]
}

View File

@ -1,13 +1,12 @@
# 安装
## 安装包安装
[releases](https://github.com/cnlh/nps/releases)
[releases](https://github.com/ehang-io/nps/releases)
下载对应的系统版本即可,服务端和客户端是单独的
## 源码安装
- 安装源码
```go get -u github.com/cnlh/nps...
```
```go get -u ehang.io/nps...```
- 编译
服务端```go build cmd/nps/nps.go```

View File

@ -1,4 +1,4 @@
![image](https://github.com/cnlh/nps/blob/master/image/web2.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/web2.png?raw=true)
# 介绍
可在网页上配置和管理各个tcp、udp隧道、内网站点代理http、https解析等功能强大操作方便。

View File

@ -44,7 +44,7 @@ npc-update.exe update
./npc -config=npc配置文件路径
```
## 配置文件说明
[示例配置文件](https://github.com/cnlh/nps/tree/master/conf/npc.conf)
[示例配置文件](https://github.com/ehang-io/nps/tree/master/conf/npc.conf)
#### 全局配置
```ini
[common]

233
docs/webapi.md Normal file
View File

@ -0,0 +1,233 @@
获取客户端列表
```
POST /client/list/
```
| 参数 | 含义 |
| --- | --- |
| search | 搜索 |
| order | 排序asc 正序 desc倒序 |
| offset | 分页(第几页) |
| limit | 条数(分页显示的条数) |
***
获取单个客户端
```
POST /client/getclient/
```
| 参数 | 含义 |
| --- | --- |
| id | 客户端id |
***
添加客户端
```
POST /client/add/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| u | basic权限认证用户名 |
| p | basic权限认证密码 |
| limit | 条数(分页显示的条数) |
| vkey | 客户端验证密钥 |
| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 |
| compress | 压缩1允许 0不允许 |
| crypt | 是否加密1或者01允许 0不允许 |
| rate\_limit | 带宽限制 单位KB/S 空则为不限制 |
| flow\_limit | 流量限制 单位M 空则为不限制 |
| max\_conn | 客户端最大连接数量 空则为不限制 |
| max\_tunnel | 客户端最大隧道数量 空则为不限制 |
***
修改客户端(25.4版本有问题暂时不能用)
```
POST /client/edit/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| u | basic权限认证用户名 |
| p | basic权限认证密码 |
| limit | 条数(分页显示的条数) |
| vkey | 客户端验证密钥 |
| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 |
| compress | 压缩1允许 0不允许 |
| crypt | 是否加密1或者01允许 0不允许 |
| rate\_limit | 带宽限制 单位KB/S 空则为不限制 |
| flow\_limit | 流量限制 单位M 空则为不限制 |
| max\_conn | 客户端最大连接数量 空则为不限制 |
| max\_tunnel | 客户端最大隧道数量 空则为不限制 |
| id | 要修改的客户端id |
***
删除客户端
```
POST /client/del/
```
| 参数 | 含义 |
| --- | --- |
| id | 要删除的客户端id |
***
获取域名解析列表
```
POST /index/hostlist/
```
| 参数 | 含义 |
| --- | --- |
| search | 搜索(可以搜域名/备注什么的) |
| offset | 分页(第几页) |
| limit | 条数(分页显示的条数) |
***
添加域名解析
```
POST /index/addhost/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| host | 域名 |
| scheme | 协议类型(三种 all http https) |
| location | url路由 空则为不限制 |
| client\_id | 客户端id |
| target | 内网目标(ip:端口) |
| header | request header 请求头 |
| hostchange | request host 请求主机 |
***
修改域名解析
```
POST /index/edithost/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| host | 域名 |
| scheme | 协议类型(三种 all http https) |
| location | url路由 空则为不限制 |
| client\_id | 客户端id |
| target | 内网目标(ip:端口) |
| header | request header 请求头 |
| hostchange | request host 请求主机 |
| id | 需要修改的域名解析id |
***
删除域名解析
```
POST /index/delhost/
```
| 参数 | 含义 |
| --- | --- |
| id | 需要删除的域名解析id |
***
获取单条隧道信息
```
POST /index/getonetunnel/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道的id |
***
获取隧道列表
```
POST /index/gettunnel/
```
| 参数 | 含义 |
| --- | --- |
| client\_id | 穿透隧道的客户端id |
| type | 类型tcp udp httpProx socks5 secret p2p |
| search | 搜索 |
| offset | 分页(第几页) |
| limit | 条数(分页显示的条数) |
***
添加隧道
```
POST /index/add/
```
| 参数 | 含义 |
| --- | --- |
| type | 类型tcp udp httpProx socks5 secret p2p |
| remark | 备注 |
| port | 服务端端口 |
| target | 目标(ip:端口) |
| client\_id | 客户端id |
***
修改隧道
```
POST /index/edit/
```
| 参数 | 含义 |
| --- | --- |
| type | 类型tcp udp httpProx socks5 secret p2p |
| remark | 备注 |
| port | 服务端端口 |
| target | 目标(ip:端口) |
| client\_id | 客户端id |
| id | 隧道id |
***
删除隧道
```
POST /index/del/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道id |
***
隧道停止工作
```
POST /index/stop/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道id |
***
隧道开始工作
```
POST /index/start/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道id |

2
go.mod
View File

@ -1,4 +1,4 @@
module github.com/cnlh/nps
module ehang.io/nps
go 1.13

View File

@ -2,7 +2,7 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="0.25.4"
android:versionName="0.26.0"
package="org.nps.client"
platformBuildVersionCode="15"
platformBuildVersionName="4.0.4-1406430">

View File

@ -1,16 +1,16 @@
package main
import (
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/daemon"
"ehang.io/nps/lib/version"
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/client"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/version"
"io/ioutil"
"os"
"path"

821
image/work_flow.svg Normal file
View File

@ -0,0 +1,821 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- 由 Microsoft Visio, SVG Export 生成 工作图.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="11.6929in" height="8.26772in"
viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB" class="st17">
<v:documentProperties v:langID="2052" v:metric="true" v:viewMarkup="false">
<v:userDefs>
<v:ud v:nameU="msvNoAutoConnect" v:prompt="" v:val="VT0(0):26"/>
</v:userDefs>
</v:documentProperties>
<style type="text/css">
<![CDATA[
.st1 {fill:#5b9bd5;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st2 {stroke:#ffffff;stroke-linecap:butt;stroke-width:0.5}
.st3 {fill:#1e4a73;font-family:Calibri;font-size:1.5em}
.st4 {fill:#aad288;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st5 {fill:#ffffff}
.st6 {font-size:1em}
.st7 {fill:#5b9bd5}
.st8 {stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st9 {fill:#a5a5a5}
.st10 {marker-end:url(#mrkr4-135);marker-start:url(#mrkr4-133);stroke:#5592c9;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
.st11 {fill:#5592c9;fill-opacity:1;stroke:#5592c9;stroke-opacity:1;stroke-width:0.37313432835821}
.st12 {fill:#ffffff;stroke:none;stroke-linecap:butt;stroke-width:7.2}
.st13 {fill:#41729d;font-family:Calibri;font-size:1.5em}
.st14 {fill:none;stroke:#42829e;stroke-dasharray:45,27;stroke-linecap:round;stroke-linejoin:round;stroke-width:3}
.st15 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st16 {fill:#5b9bd5;font-family:Calibri;font-size:1.99999em}
.st17 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]>
</style>
<defs id="Markers">
<g id="lend4">
<path d="M 2 1 L 0 0 L 2 -1 L 2 1 " style="stroke:none"/>
</g>
<marker id="mrkr4-133" class="st11" v:arrowType="4" v:arrowSize="2" v:setback="5.12" refX="5.12" orient="auto"
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(2.68) "/>
</marker>
<marker id="mrkr4-135" class="st11" v:arrowType="4" v:arrowSize="2" v:setback="5.36" refX="-5.36" orient="auto"
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(-2.68,-2.68) "/>
</marker>
</defs>
<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
<v:userDefs>
<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
</v:userDefs>
<title>页-1</title>
<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394"
v:shadowOffsetY="-8.50394"/>
<v:layer v:name="连接线" v:index="0"/>
<g id="group1-1" transform="translate(418.11,-311.811)" v:mID="1" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(防火墙)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>防火墙</title>
<desc>NAT</desc>
<g id="shape2-2" v:mID="2" v:groupContext="shape" transform="translate(0.545123,-7.63784)">
<title>工作表.2</title>
<rect x="0" y="539.685" width="69.7759" height="55.5905" class="st1"/>
</g>
<g id="shape3-4" v:mID="3" v:groupContext="shape" transform="translate(0.795123,-7.88784)">
<title>工作表.3</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M0 587.41 L69.28 587.41" class="st2"/>
<path d="M0 579.54 L69.28 579.54" class="st2"/>
<path d="M0 571.67 L69.28 571.67" class="st2"/>
<path d="M0 563.8 L69.28 563.8" class="st2"/>
<path d="M0 555.93 L69.28 555.93" class="st2"/>
<path d="M0 548.06 L69.28 548.06" class="st2"/>
<path d="M17.32 595.28 L17.32 587.41" class="st2"/>
<path d="M34.64 595.28 L34.64 587.41" class="st2"/>
<path d="M51.96 595.28 L51.96 587.41" class="st2"/>
<path d="M8.66 587.41 L8.66 579.54" class="st2"/>
<path d="M25.98 587.41 L25.98 579.54" class="st2"/>
<path d="M43.3 587.41 L43.3 579.54" class="st2"/>
<path d="M60.62 587.41 L60.62 579.54" class="st2"/>
<path d="M17.32 579.54 L17.32 571.67" class="st2"/>
<path d="M34.64 579.54 L34.64 571.67" class="st2"/>
<path d="M51.96 579.54 L51.96 571.67" class="st2"/>
<path d="M8.66 571.67 L8.66 563.8" class="st2"/>
<path d="M25.98 571.67 L25.98 563.8" class="st2"/>
<path d="M43.3 571.67 L43.3 563.8" class="st2"/>
<path d="M60.62 571.67 L60.62 563.8" class="st2"/>
<path d="M17.32 563.8 L17.32 555.93" class="st2"/>
<path d="M34.64 563.8 L34.64 555.93" class="st2"/>
<path d="M51.96 563.8 L51.96 555.93" class="st2"/>
<path d="M8.66 555.93 L8.66 548.06" class="st2"/>
<path d="M25.98 555.93 L25.98 548.06" class="st2"/>
<path d="M43.3 555.93 L43.3 548.06" class="st2"/>
<path d="M60.62 555.93 L60.62 548.06" class="st2"/>
<path d="M17.32 548.06 L17.32 540.19" class="st2"/>
<path d="M34.64 548.06 L34.64 540.19" class="st2"/>
<path d="M51.96 548.06 L51.96 540.19" class="st2"/>
</g>
<g id="shape1-36" v:mID="1" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="610.077" width="42.88" height="29.6036"/>
<text x="20.03" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NAT</text> </g>
</g>
<g id="group4-38" transform="translate(701.575,-311.811)" v:mID="4" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Memory" v:lbl="内存" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(服务器)"/>
<v:cp v:nameU="BelongsTo" v:lbl="属于" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true" v:ask="false"
v:langID="2052" v:cal="0"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>服务器</title>
<desc>Application2 10.0.0.4:PORT</desc>
<g id="shape5-39" v:mID="5" v:groupContext="shape" transform="translate(12.8133,0)">
<title>工作表.5</title>
<rect x="0" y="524.409" width="45.2395" height="70.8661" class="st1"/>
</g>
<g id="shape6-41" v:mID="6" v:groupContext="shape" transform="translate(46.625,-30.2513)">
<title>工作表.6</title>
<ellipse cx="2.73472" cy="592.541" rx="2.73472" ry="2.73472" class="st4"/>
</g>
<g id="shape7-43" v:mID="7" v:groupContext="shape" transform="translate(30.0295,-11.6164)">
<title>工作表.7</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M-0 595.28 L22.06 595.28 L22.06 593.5 L-0 593.5 L-0 595.28 ZM-0 589.9 L22.06 589.9 L22.06 588.13 L-0 588.13
L-0 589.9 ZM-0 584.53 L22.06 584.53 L22.06 582.76 L-0 582.76 L-0 584.53 Z" class="st5"/>
</g>
<g id="shape4-46" v:mID="4" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="115.9" height="51.2036"/>
<text x="-10.6" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Application2<v:newlineChar/><tspan
x="-16.48" dy="1.2em" class="st6">10.0.0.4:PORT</tspan></text> </g>
</g>
<g id="group8-49" transform="translate(701.575,-496.063)" v:mID="8" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Memory" v:lbl="内存" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(服务器)"/>
<v:cp v:nameU="BelongsTo" v:lbl="属于" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true" v:ask="false"
v:langID="2052" v:cal="0"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>服务器.8</title>
<desc>Application1 10.0.0.3:PORT</desc>
<g id="shape9-50" v:mID="9" v:groupContext="shape" transform="translate(12.8133,0)">
<title>工作表.9</title>
<rect x="0" y="524.409" width="45.2395" height="70.8661" class="st1"/>
</g>
<g id="shape10-52" v:mID="10" v:groupContext="shape" transform="translate(46.625,-30.2513)">
<title>工作表.10</title>
<ellipse cx="2.73472" cy="592.541" rx="2.73472" ry="2.73472" class="st4"/>
</g>
<g id="shape11-54" v:mID="11" v:groupContext="shape" transform="translate(30.0295,-11.6164)">
<title>工作表.11</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M-0 595.28 L22.06 595.28 L22.06 593.5 L-0 593.5 L-0 595.28 ZM-0 589.9 L22.06 589.9 L22.06 588.13 L-0 588.13
L-0 589.9 ZM-0 584.53 L22.06 584.53 L22.06 582.76 L-0 582.76 L-0 584.53 Z" class="st5"/>
</g>
<g id="shape8-57" v:mID="8" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="115.9" height="51.2036"/>
<text x="-10.6" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Application1<v:newlineChar/><tspan
x="-16.48" dy="1.2em" class="st6">10.0.0.3:PORT</tspan></text> </g>
</g>
<g id="group12-60" transform="translate(701.575,-127.559)" v:mID="12" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Memory" v:lbl="内存" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(服务器)"/>
<v:cp v:nameU="BelongsTo" v:lbl="属于" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true" v:ask="false"
v:langID="2052" v:cal="0"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>服务器.12</title>
<desc>Application3 10.0.0.5:PORT</desc>
<g id="shape13-61" v:mID="13" v:groupContext="shape" transform="translate(12.8133,0)">
<title>工作表.13</title>
<rect x="0" y="524.409" width="45.2395" height="70.8661" class="st1"/>
</g>
<g id="shape14-63" v:mID="14" v:groupContext="shape" transform="translate(46.625,-30.2513)">
<title>工作表.14</title>
<ellipse cx="2.73472" cy="592.541" rx="2.73472" ry="2.73472" class="st4"/>
</g>
<g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(30.0295,-11.6164)">
<title>工作表.15</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M-0 595.28 L22.06 595.28 L22.06 593.5 L-0 593.5 L-0 595.28 ZM-0 589.9 L22.06 589.9 L22.06 588.13 L-0 588.13
L-0 589.9 ZM-0 584.53 L22.06 584.53 L22.06 582.76 L-0 582.76 L-0 584.53 Z" class="st5"/>
</g>
<g id="shape12-68" v:mID="12" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="115.9" height="51.2036"/>
<text x="-10.6" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Application3<v:newlineChar/><tspan
x="-16.48" dy="1.2em" class="st6">10.0.0.5:PORT</tspan></text> </g>
</g>
<g id="group16-71" transform="translate(538.583,-311.811)" v:mID="16" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="建筑物" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(计算机)"/>
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(大型机)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>主机</title>
<desc>NPC Client 10.0.0.2 Dial To: -&#62;10.0.0.3:PORT -&#62;10.0.0.4:PORT ...</desc>
<g id="shape17-72" v:mID="17" v:groupContext="shape" transform="translate(5.68158,0)">
<title>工作表.17</title>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28 Z" class="st7"/>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28" class="st8"/>
<path d="M29.75 595.28 L29.75 524.41" class="st8"/>
</g>
<g id="shape18-76" v:mID="18" v:groupContext="shape" transform="translate(11.5726,-5.14536)">
<title>工作表.18</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M36.09 551.42 L49.23 551.42 L49.23 536.38 L36.09 536.38 L36.09 551.42 ZM14.89 542.47 L16.68 542.47 L16.68
536.06 L14.89 536.06 L14.89 542.47 ZM14.89 551.71 L16.68 551.71 L16.68 545.3 L14.89 545.3 L14.89 551.71
ZM10.63 542.47 L12.43 542.47 L12.43 536.06 L10.63 536.06 L10.63 542.47 ZM10.63 551.71 L12.43 551.71
L12.43 545.3 L10.63 545.3 L10.63 551.71 ZM4.25 542.47 L6.05 542.47 L6.05 536.06 L4.25 536.06 L4.25 542.47
ZM4.25 551.71 L6.05 551.71 L6.05 545.3 L4.25 545.3 L4.25 551.71 ZM0 542.47 L1.79 542.47 L1.79 536.06
L0 536.06 L0 542.47 ZM0 551.71 L1.79 551.71 L1.79 545.3 L0 545.3 L0 551.71 ZM33.82 586.45 L33.82 587.7
L49.39 587.69 L49.39 586.44 L33.82 586.45 ZM33.82 590.24 L33.82 591.49 L49.39 591.48 L49.39 590.24 L33.82
590.24 ZM33.82 594.03 L33.82 595.28 L49.39 595.27 L49.39 594.03 L33.82 594.03 ZM2.96 587.7 L18.52 587.7
L18.52 586.45 L2.95 586.45 L2.96 587.7 ZM2.96 591.49 L18.52 591.49 L18.52 590.24 L2.95 590.24 L2.96
591.49 ZM2.96 595.28 L18.52 595.28 L18.52 594.03 L2.95 594.03 L2.96 595.28 Z" class="st5"/>
</g>
<g id="shape19-79" v:mID="19" v:groupContext="shape" transform="translate(49.0815,-50.4059)">
<title>工作表.19</title>
<path d="M8.15 583.79 A1.11073 1.11073 -180 1 0 10.24 584.56 A1.11073 1.11073 -180 1 0 8.15 583.79 ZM8.17 587.08
A1.11073 1.11073 -180 1 0 10.22 587.93 A1.11073 1.11073 -180 1 0 8.17 587.08 ZM8.18 590.39 A1.11073
1.11073 -180 1 0 10.21 591.28 A1.11073 1.11073 -180 1 0 8.18 590.39 ZM8.18 593.71 A1.11073 1.11073 -180
1 0 10.21 594.6 A1.11073 1.11073 -180 1 0 8.18 593.71 ZM4.1 583.84 A1.11073 1.11073 -180 1 0 6.21 584.51
A1.11073 1.11073 -180 1 0 4.1 583.84 ZM4.11 587.14 A1.11073 1.11073 -180 1 0 6.2 587.87 A1.11073 1.11073
-180 1 0 4.11 587.14 ZM4.11 590.44 A1.11073 1.11073 -180 1 0 6.19 591.22 A1.11073 1.11073 -180 1 0 4.11
590.44 ZM4.11 593.77 A1.11073 1.11073 -180 1 0 6.19 594.55 A1.11073 1.11073 -180 1 0 4.11 593.77 ZM0.04
583.9 A1.11144 1.11144 -180 1 0 2.19 584.46 A1.11144 1.11144 -180 1 0 0.04 583.9 ZM0.05 587.19 A1.11144
1.11144 -180 1 0 2.18 587.82 A1.11144 1.11144 -180 1 0 0.05 587.19 ZM0.05 590.5 A1.11144 1.11144 -180
1 0 2.17 591.16 A1.11144 1.11144 -180 1 0 0.05 590.5 ZM0.05 593.83 A1.11144 1.11144 -180 1 0 2.17 594.49
A1.11144 1.11144 -180 1 0 0.05 593.83 Z" class="st9"/>
</g>
<g id="shape16-82" v:mID="16" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="674.877" width="130.38" height="159.204"/>
<text x="20.18" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPC<v:newlineChar/><tspan
x="14.28" dy="1.2em" class="st6">Client<v:newlineChar/></tspan><tspan x="5.81" dy="1.2em" class="st6">10.0.0.2<v:newlineChar/></tspan><tspan
x="7.88" dy="1.2em" class="st6">Dial To:<v:newlineChar/></tspan><tspan x="-23.72" dy="1.2em"
class="st6">-</tspan>&#62;10.0.0.3:PORT<v:newlineChar/><tspan x="-23.72" dy="1.2em" class="st6">-</tspan>&#62;10.0.0.4:PORT<v:newlineChar/><tspan
x="-23.72" dy="1.2em" class="st6">-</tspan>&#62;10.0.0.5:PORT</text> </g>
</g>
<g id="group20-90" transform="translate(212.598,-311.811)" v:mID="20" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="建筑物" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(计算机)"/>
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(大型机)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>主机.20</title>
<desc>NPS Server 1.1.1.1 Listen On: 8003-&#62;10.0.0.3:PORT 8004-&#62;10.0....</desc>
<g id="shape21-91" v:mID="21" v:groupContext="shape" transform="translate(5.68158,0)">
<title>工作表.21</title>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28 Z" class="st7"/>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28" class="st8"/>
<path d="M29.75 595.28 L29.75 524.41" class="st8"/>
</g>
<g id="shape22-95" v:mID="22" v:groupContext="shape" transform="translate(11.5726,-5.14536)">
<title>工作表.22</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M36.09 551.42 L49.23 551.42 L49.23 536.38 L36.09 536.38 L36.09 551.42 ZM14.89 542.47 L16.68 542.47 L16.68
536.06 L14.89 536.06 L14.89 542.47 ZM14.89 551.71 L16.68 551.71 L16.68 545.3 L14.89 545.3 L14.89 551.71
ZM10.63 542.47 L12.43 542.47 L12.43 536.06 L10.63 536.06 L10.63 542.47 ZM10.63 551.71 L12.43 551.71
L12.43 545.3 L10.63 545.3 L10.63 551.71 ZM4.25 542.47 L6.05 542.47 L6.05 536.06 L4.25 536.06 L4.25 542.47
ZM4.25 551.71 L6.05 551.71 L6.05 545.3 L4.25 545.3 L4.25 551.71 ZM0 542.47 L1.79 542.47 L1.79 536.06
L0 536.06 L0 542.47 ZM0 551.71 L1.79 551.71 L1.79 545.3 L0 545.3 L0 551.71 ZM33.82 586.45 L33.82 587.7
L49.39 587.69 L49.39 586.44 L33.82 586.45 ZM33.82 590.24 L33.82 591.49 L49.39 591.48 L49.39 590.24 L33.82
590.24 ZM33.82 594.03 L33.82 595.28 L49.39 595.27 L49.39 594.03 L33.82 594.03 ZM2.96 587.7 L18.52 587.7
L18.52 586.45 L2.95 586.45 L2.96 587.7 ZM2.96 591.49 L18.52 591.49 L18.52 590.24 L2.95 590.24 L2.96
591.49 ZM2.96 595.28 L18.52 595.28 L18.52 594.03 L2.95 594.03 L2.96 595.28 Z" class="st5"/>
</g>
<g id="shape23-98" v:mID="23" v:groupContext="shape" transform="translate(49.0815,-50.4059)">
<title>工作表.23</title>
<path d="M8.15 583.79 A1.11073 1.11073 -180 1 0 10.24 584.56 A1.11073 1.11073 -180 1 0 8.15 583.79 ZM8.17 587.08
A1.11073 1.11073 -180 1 0 10.22 587.93 A1.11073 1.11073 -180 1 0 8.17 587.08 ZM8.18 590.39 A1.11073
1.11073 -180 1 0 10.21 591.28 A1.11073 1.11073 -180 1 0 8.18 590.39 ZM8.18 593.71 A1.11073 1.11073 -180
1 0 10.21 594.6 A1.11073 1.11073 -180 1 0 8.18 593.71 ZM4.1 583.84 A1.11073 1.11073 -180 1 0 6.21 584.51
A1.11073 1.11073 -180 1 0 4.1 583.84 ZM4.11 587.14 A1.11073 1.11073 -180 1 0 6.2 587.87 A1.11073 1.11073
-180 1 0 4.11 587.14 ZM4.11 590.44 A1.11073 1.11073 -180 1 0 6.19 591.22 A1.11073 1.11073 -180 1 0 4.11
590.44 ZM4.11 593.77 A1.11073 1.11073 -180 1 0 6.19 594.55 A1.11073 1.11073 -180 1 0 4.11 593.77 ZM0.04
583.9 A1.11144 1.11144 -180 1 0 2.19 584.46 A1.11144 1.11144 -180 1 0 0.04 583.9 ZM0.05 587.19 A1.11144
1.11144 -180 1 0 2.18 587.82 A1.11144 1.11144 -180 1 0 0.05 587.19 ZM0.05 590.5 A1.11144 1.11144 -180
1 0 2.17 591.16 A1.11144 1.11144 -180 1 0 0.05 590.5 ZM0.05 593.83 A1.11144 1.11144 -180 1 0 2.17 594.49
A1.11144 1.11144 -180 1 0 0.05 593.83 Z" class="st9"/>
</g>
<g id="shape20-101" v:mID="20" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="674.877" width="166.87" height="159.204"/>
<text x="20.84" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPS<v:newlineChar/><tspan
x="12" dy="1.2em" class="st6">Server<v:newlineChar/></tspan><tspan x="10.37" dy="1.2em" class="st6">1.1.1.1<v:newlineChar/></tspan><tspan
x="-1.29" dy="1.2em" class="st6">Listen On:<v:newlineChar/></tspan><tspan x="-41.96" dy="1.2em"
class="st6">8003</tspan>-&#62;10.0.0.3:PORT<v:newlineChar/><tspan x="-41.96" dy="1.2em" class="st6">8004</tspan>-&#62;10.0.0.4:PORT<v:newlineChar/><tspan
x="-41.96" dy="1.2em" class="st6">8005</tspan>-&#62;10.0.0.5:PORT</text> </g>
</g>
<g id="group24-109" transform="translate(49.6063,-496.063)" v:mID="24" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="Name" v:lbl="名称" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(连接性)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(用户)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>用户</title>
<desc>User1 Wants:APP1</desc>
<g id="shape25-110" v:mID="25" v:groupContext="shape" transform="translate(18.0575,0)">
<title>工作表.25</title>
<path d="M26.29 533.22 A8.81 8.81 -180 1 0 8.67 533.22 A8.81 8.81 -180 1 0 26.29 533.22 ZM27.58 544.41 L7.17 544.41
C3.22 544.41 0 547.62 0 551.58 L0 576.58 L5.59 576.58 L5.59 562.03 L7.45 562.03 L7.45 595.28 L16.55
595.28 L16.55 580.42 C16.55 579.9 16.97 579.48 17.48 579.48 C18 579.48 18.42 579.9 18.42 580.42 L18.42
595.28 L28.04 595.28 L28.04 561.98 L29.91 561.98 L29.91 576.58 L34.75 576.58 L34.75 551.58 C34.75 547.62
31.54 544.41 27.58 544.41 Z" class="st1"/>
</g>
<g id="shape24-112" v:mID="24" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="102.19" height="51.2036"/>
<text x="13.96" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>User1<v:newlineChar/><tspan
x="-9.62" dy="1.2em" class="st6">Wants:APP1</tspan></text> </g>
</g>
<g id="group26-115" transform="translate(49.6063,-311.811)" v:mID="26" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="Name" v:lbl="名称" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(连接性)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(用户)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>用户.26</title>
<desc>User2 Wants:APP2</desc>
<g id="shape27-116" v:mID="27" v:groupContext="shape" transform="translate(18.0575,0)">
<title>工作表.27</title>
<path d="M26.29 533.22 A8.81 8.81 -180 1 0 8.67 533.22 A8.81 8.81 -180 1 0 26.29 533.22 ZM27.58 544.41 L7.17 544.41
C3.22 544.41 0 547.62 0 551.58 L0 576.58 L5.59 576.58 L5.59 562.03 L7.45 562.03 L7.45 595.28 L16.55
595.28 L16.55 580.42 C16.55 579.9 16.97 579.48 17.48 579.48 C18 579.48 18.42 579.9 18.42 580.42 L18.42
595.28 L28.04 595.28 L28.04 561.98 L29.91 561.98 L29.91 576.58 L34.75 576.58 L34.75 551.58 C34.75 547.62
31.54 544.41 27.58 544.41 Z" class="st1"/>
</g>
<g id="shape26-118" v:mID="26" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="102.19" height="51.2036"/>
<text x="13.96" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>User2<v:newlineChar/><tspan
x="-9.62" dy="1.2em" class="st6">Wants:APP2</tspan></text> </g>
</g>
<g id="group28-121" transform="translate(49.6063,-127.559)" v:mID="28" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="Name" v:lbl="名称" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(连接性)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(用户)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>用户.28</title>
<desc>User3 Wants:APP3</desc>
<g id="shape29-122" v:mID="29" v:groupContext="shape" transform="translate(18.0575,0)">
<title>工作表.29</title>
<path d="M26.29 533.22 A8.81 8.81 -180 1 0 8.67 533.22 A8.81 8.81 -180 1 0 26.29 533.22 ZM27.58 544.41 L7.17 544.41
C3.22 544.41 0 547.62 0 551.58 L0 576.58 L5.59 576.58 L5.59 562.03 L7.45 562.03 L7.45 595.28 L16.55
595.28 L16.55 580.42 C16.55 579.9 16.97 579.48 17.48 579.48 C18 579.48 18.42 579.9 18.42 580.42 L18.42
595.28 L28.04 595.28 L28.04 561.98 L29.91 561.98 L29.91 576.58 L34.75 576.58 L34.75 551.58 C34.75 547.62
31.54 544.41 27.58 544.41 Z" class="st1"/>
</g>
<g id="shape28-124" v:mID="28" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="102.19" height="51.2036"/>
<text x="13.96" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>User3<v:newlineChar/><tspan
x="-9.62" dy="1.2em" class="st6">Wants:APP3</tspan></text> </g>
</g>
<g id="shape1003-127" v:mID="1003" v:groupContext="shape" v:layerMember="0" transform="translate(99.8466,-514.757)">
<title>动态连接线.1003</title>
<desc>-&#62;8003 Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.059" cy="684.836" width="93.29" height="51.2036"/>
<path d="M5.09 601.03 L5.33 601.3 L113.11 723.13" class="st10"/>
<rect v:rectContext="textBkgnd" x="15.4535" y="663.236" width="81.2109" height="43.1999" class="st12"/>
<text x="30.58" y="679.44" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;8003<v:newlineChar/><tspan
x="15.45" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1004-139" v:mID="1004" v:groupContext="shape" v:layerMember="0" transform="translate(102.415,-340.157)">
<title>动态连接线.1004</title>
<desc>-&#62;8004 Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="57.9325" cy="588.189" width="93.29" height="51.2036"/>
<path d="M7.68 588.19 L8.04 588.19 L107.83 588.19" class="st10"/>
<rect v:rectContext="textBkgnd" x="17.327" y="566.589" width="81.2109" height="43.1999" class="st12"/>
<text x="32.45" y="582.79" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;8004<v:newlineChar/><tspan
x="17.33" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1005-149" v:mID="1005" v:groupContext="shape" v:layerMember="0" transform="translate(98.1493,-177.812)">
<title>动态连接线.1005</title>
<desc>-&#62;8005 Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="60.0654" cy="527.376" width="93.29" height="51.2036"/>
<path d="M5.09 589.52 L5.33 589.25 L114.8 465.5" class="st10"/>
<rect v:rectContext="textBkgnd" x="19.4599" y="505.776" width="81.2109" height="43.1999" class="st12"/>
<text x="34.58" y="521.98" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;8005<v:newlineChar/><tspan
x="19.46" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1006-159" v:mID="1006" v:groupContext="shape" v:layerMember="0" transform="translate(277.783,-354.331)">
<title>动态连接线.1006</title>
<desc>NPS &#38; NPC Multiplexing Connection TCP or KCP Only One Conn Pe...</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="70.4362" cy="602.362" width="103.62" height="159.204"/>
<path d="M7.68 602.36 L8.04 602.36 L132.83 602.36" class="st10"/>
<rect v:rectContext="textBkgnd" x="24.6671" y="526.762" width="91.5381" height="151.2" class="st12"/>
<text x="30.38" y="542.96" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPS &#38; NPC<v:newlineChar/><tspan
x="24.67" dy="1.2em" class="st6">Multiplexing<v:newlineChar/></tspan><tspan x="28.6" dy="1.2em" class="st6">Connection<v:newlineChar/></tspan><tspan
x="30.53" dy="1.2em" class="st6">TCP or KCP<v:newlineChar/></tspan><tspan x="36.41" dy="1.2em" class="st6">Only One<v:newlineChar/></tspan><tspan
x="32.66" dy="1.2em" class="st6">Conn Peer<v:newlineChar/></tspan><tspan x="55.18" dy="1.2em" class="st6">NPC</tspan></text> </g>
<g id="shape1007-174" v:mID="1007" v:groupContext="shape" v:layerMember="0" transform="translate(603.767,-380.876)">
<title>动态连接线.1007</title>
<desc>-&#62;PORT Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="55.0044" cy="550.955" width="93.29" height="51.2036"/>
<path d="M5.09 589.52 L5.33 589.25 L105.29 476.25" class="st10"/>
<rect v:rectContext="textBkgnd" x="14.3989" y="529.355" width="81.2109" height="43.1999" class="st12"/>
<text x="27.89" y="545.56" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;PORT<v:newlineChar/><tspan
x="14.4" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1008-184" v:mID="1008" v:groupContext="shape" v:layerMember="0" transform="translate(603.767,-340.157)">
<title>动态连接线.1008</title>
<desc>-&#62;PORT Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="55.3104" cy="588.189" width="93.29" height="51.2036"/>
<path d="M7.68 588.19 L8.04 588.19 L102.58 588.19" class="st10"/>
<rect v:rectContext="textBkgnd" x="14.7049" y="566.589" width="81.2109" height="43.1999" class="st12"/>
<text x="28.19" y="582.79" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;PORT<v:newlineChar/><tspan
x="14.7" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1009-194" v:mID="1009" v:groupContext="shape" v:layerMember="0" transform="translate(603.767,-313.612)">
<title>动态连接线.1009</title>
<desc>-&#62;PORT Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="68.0438" cy="667.943" width="93.29" height="51.2036"/>
<path d="M5.09 601.03 L5.33 601.3 L105.29 714.3" class="st10"/>
<rect v:rectContext="textBkgnd" x="27.4383" y="646.343" width="81.2109" height="43.1999" class="st12"/>
<text x="40.93" y="662.54" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;PORT<v:newlineChar/><tspan
x="27.44" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1010-204" v:mID="1010" v:groupContext="shape" v:layerMember="0" transform="translate(488.431,-340.157)">
<title>动态连接线.1010</title>
<desc>NPS &#38; NPC</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="27.9165" cy="588.189" width="90" height="72.8036"/>
<path d="M7.68 588.19 L8.04 588.19 L47.79 588.19" class="st10"/>
<rect v:rectContext="textBkgnd" x="12.6587" y="555.789" width="30.5156" height="64.7998" class="st12"/>
<text x="13.32" y="571.99" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPS<v:newlineChar/><tspan
x="21.78" dy="1.2em" class="st6">&#38;<v:newlineChar/></tspan><tspan x="12.66" dy="1.2em" class="st6">NPC</tspan></text> </g>
<g id="shape1011-215" v:mID="1011" v:groupContext="shape" transform="translate(34.0157,-62.8844)">
<title>工作表.1011</title>
<path d="M0 595.28 L398.27 595.28 L398.27 85.04 L0 85.04 L0 595.28 Z" class="st14"/>
</g>
<g id="shape1012-217" v:mID="1012" v:groupContext="shape" transform="translate(473.386,-62.8844)">
<title>工作表.1012</title>
<path d="M0 595.28 L320.31 595.28 L320.31 85.04 L0 85.04 L0 595.28 Z" class="st14"/>
</g>
<g id="shape1013-219" v:mID="1013" v:groupContext="shape" transform="translate(255.118,-496.063)">
<title>工作表.1013</title>
<desc>Internet</desc>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="63.7795" cy="559.843" width="127.56" height="70.8661"/>
<rect x="0" y="524.409" width="127.559" height="70.8661" class="st15"/>
<text x="23.98" y="567.04" class="st16" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Internet</text> </g>
<g id="shape1014-222" v:mID="1014" v:groupContext="shape" transform="translate(517.323,-515.866)">
<title>工作表.1014</title>
<desc>Intranet</desc>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="76" cy="579.646" width="152.01" height="31.2598"/>
<rect x="0" y="564.016" width="152" height="31.2598" class="st15"/>
<text x="36.43" y="586.85" class="st16" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Intranet</text> </g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -49,6 +49,6 @@ const (
MUX_PING_RETURN
MUX_PING int32 = -1
MAXIMUM_SEGMENT_SIZE = PoolSizeWindow
MAXIMUM_WINDOW_SIZE = 1 << 25 // 1<<31-1 TCP slide window size is very large,
// we use 32M, reduce memory usage
MAXIMUM_WINDOW_SIZE = 1 << 27 // 1<<31-1 TCP slide window size is very large,
// we use 128M, reduce memory usage
)

View File

@ -162,10 +162,9 @@ func (Self *ConnPackager) UnPack(reader io.Reader) (n uint16, err error) {
}
type MuxPackager struct {
Flag uint8
Id int32
Window uint32
ReadLength uint32
Flag uint8
Id int32
Window uint64
BasePackager
}
@ -178,19 +177,8 @@ func (Self *MuxPackager) NewPac(flag uint8, id int32, content ...interface{}) (e
err = Self.BasePackager.NewPac(content...)
//logs.Warn(Self.Length, string(Self.Content))
case MUX_MSG_SEND_OK:
// MUX_MSG_SEND_OK contains two data
switch content[0].(type) {
case int:
Self.Window = uint32(content[0].(int))
case uint32:
Self.Window = content[0].(uint32)
}
switch content[1].(type) {
case int:
Self.ReadLength = uint32(content[1].(int))
case uint32:
Self.ReadLength = content[1].(uint32)
}
// MUX_MSG_SEND_OK contains one data
Self.Window = content[0].(uint64)
}
return
}
@ -210,10 +198,6 @@ func (Self *MuxPackager) Pack(writer io.Writer) (err error) {
WindowBuff.Put(Self.Content)
case MUX_MSG_SEND_OK:
err = binary.Write(writer, binary.LittleEndian, Self.Window)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, Self.ReadLength)
}
return
}
@ -235,12 +219,7 @@ func (Self *MuxPackager) UnPack(reader io.Reader) (n uint16, err error) {
//logs.Warn("unpack", Self.Length, string(Self.Content))
case MUX_MSG_SEND_OK:
err = binary.Read(reader, binary.LittleEndian, &Self.Window)
if err != nil {
return
}
n += 4 // uint32
err = binary.Read(reader, binary.LittleEndian, &Self.ReadLength)
n += 4 // uint32
n += 8 // uint64
}
n += 5 //uint8 int32
return
@ -251,7 +230,6 @@ func (Self *MuxPackager) reset() {
Self.Flag = 0
Self.Length = 0
Self.Content = nil
Self.ReadLength = 0
Self.Window = 0
}

View File

@ -16,7 +16,7 @@ import (
"strings"
"sync"
"github.com/cnlh/nps/lib/crypt"
"ehang.io/nps/lib/crypt"
)
//Get the corresponding IP address through domain name

View File

@ -6,8 +6,8 @@ import (
"regexp"
"strings"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
)
type CommonConfig struct {
@ -241,7 +241,7 @@ func dealTunnel(s string) *file.Tunnel {
t.StripPre = item[1]
case "multi_account":
t.MultiAccount = &file.MultiAccount{}
if common.FileExists(item[1]){
if common.FileExists(item[1]) {
if b, err := common.ReadAllFromFile(item[1]); err != nil {
panic(err)
} else {

View File

@ -3,11 +3,11 @@ package conn
import (
"bufio"
"bytes"
"ehang.io/nps/lib/goroutine"
"encoding/binary"
"encoding/json"
"errors"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/goroutine"
"io"
"net"
"net/http"
@ -16,11 +16,11 @@ import (
"strings"
"time"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/mux"
"ehang.io/nps/lib/rate"
"github.com/xtaci/kcp-go"
)

View File

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
"github.com/cnlh/nps/lib/common"
"ehang.io/nps/lib/common"
)
func InitDaemon(f string, runPath string, pidPath string) {

View File

@ -8,8 +8,8 @@ import (
"path/filepath"
"syscall"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
)
func init() {

View File

@ -9,9 +9,9 @@ import (
"strings"
"sync"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/rate"
)
type DbUtils struct {

View File

@ -9,8 +9,8 @@ import (
"sync"
"sync/atomic"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/rate"
)
func NewJsonDb(runPath string) *JsonDb {

View File

@ -6,7 +6,7 @@ import (
"sync/atomic"
"time"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/rate"
"github.com/pkg/errors"
)

View File

@ -1,8 +1,8 @@
package goroutine
import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"github.com/panjf2000/ants/v2"
"io"
"net"

View File

@ -1,11 +1,11 @@
package install
import (
"ehang.io/nps/lib/common"
"encoding/json"
"errors"
"fmt"
"github.com/c4milo/unpackit"
"github.com/cnlh/nps/lib/common"
"io"
"io/ioutil"
"log"
@ -50,7 +50,7 @@ func downloadLatest(bin string) string {
fmt.Println("the latest version is", version)
filename := runtime.GOOS + "_" + runtime.GOARCH + "_" + bin + ".tar.gz"
// download latest package
downloadUrl := fmt.Sprintf("https://github.com/cnlh/nps/releases/download/%s/%s", version, filename)
downloadUrl := fmt.Sprintf("https://ehang.io/nps/releases/download/%s/%s", version, filename)
fmt.Println("download package from ", downloadUrl)
resp, err := http.Get(downloadUrl)
if err != nil {

View File

@ -1,6 +1,7 @@
package mux
import (
"ehang.io/nps/lib/common"
"errors"
"github.com/astaxie/beego/logs"
"io"
@ -10,8 +11,6 @@ import (
"sync"
"sync/atomic"
"time"
"github.com/cnlh/nps/lib/common"
)
type conn struct {
@ -146,25 +145,44 @@ func (s *conn) SetWriteDeadline(t time.Time) error {
}
type window struct {
remainingWait uint64 // 64bit alignment
off uint32
maxSize uint32
closeOp bool
closeOpCh chan struct{}
mux *Mux
maxSizeDone uint64
// 64bit alignment
// maxSizeDone contains 4 parts
// 1 31 1 31
// wait maxSize useless done
// wait zero means false, one means true
off uint32
closeOp bool
closeOpCh chan struct{}
mux *Mux
}
func (Self *window) unpack(ptrs uint64) (remaining, wait uint32) {
const mask = 1<<dequeueBits - 1
remaining = uint32((ptrs >> dequeueBits) & mask)
wait = uint32(ptrs & mask)
const windowBits = 31
const waitBits = dequeueBits + windowBits
const mask1 = 1
const mask31 = 1<<windowBits - 1
func (Self *window) unpack(ptrs uint64) (maxSize, done uint32, wait bool) {
maxSize = uint32((ptrs >> dequeueBits) & mask31)
done = uint32(ptrs & mask31)
//logs.Warn("unpack", maxSize, done)
if ((ptrs >> waitBits) & mask1) == 1 {
wait = true
return
}
return
}
func (Self *window) pack(remaining, wait uint32) uint64 {
const mask = 1<<dequeueBits - 1
return (uint64(remaining) << dequeueBits) |
uint64(wait&mask)
func (Self *window) pack(maxSize, done uint32, wait bool) uint64 {
//logs.Warn("pack", maxSize, done, wait)
if wait {
return (uint64(1)<<waitBits |
uint64(maxSize&mask31)<<dequeueBits) |
uint64(done&mask31)
}
return (uint64(0)<<waitBits |
uint64(maxSize&mask31)<<dequeueBits) |
uint64(done&mask31)
}
func (Self *window) New() {
@ -184,21 +202,25 @@ type ReceiveWindow struct {
bufQueue ReceiveWindowQueue
element *common.ListElement
count int8
bw *writeBandwidth
once sync.Once
// receive window send the current max size and read size to send window
// means done size actually store the size receive window has read
}
func (Self *ReceiveWindow) New(mux *Mux) {
// initial a window for receive
Self.bufQueue.New()
Self.element = common.ListElementPool.Get()
Self.maxSize = common.MAXIMUM_SEGMENT_SIZE * 10
Self.maxSizeDone = Self.pack(common.MAXIMUM_SEGMENT_SIZE*30, 0, false)
Self.mux = mux
Self.window.New()
Self.bw = NewWriteBandwidth()
}
func (Self *ReceiveWindow) remainingSize(delta uint16) (n uint32) {
func (Self *ReceiveWindow) remainingSize(maxSize uint32, delta uint16) (n uint32) {
// receive window remaining
l := int64(atomic.LoadUint32(&Self.maxSize)) - int64(Self.bufQueue.Len())
l := int64(maxSize) - int64(Self.bufQueue.Len())
l -= int64(delta)
if l > 0 {
n = uint32(l)
@ -210,30 +232,46 @@ func (Self *ReceiveWindow) calcSize() {
// calculating maximum receive window size
if Self.count == 0 {
//logs.Warn("ping, bw", Self.mux.latency, Self.bw.Get())
conns := Self.mux.connMap.Size()
n := uint32(math.Float64frombits(atomic.LoadUint64(&Self.mux.latency)) *
Self.mux.bw.Get() / float64(conns))
if n < common.MAXIMUM_SEGMENT_SIZE*10 {
n = common.MAXIMUM_SEGMENT_SIZE * 10
//conns := Self.mux.connMap.Size()
muxBw := Self.mux.bw.Get()
connBw := Self.bw.Get()
//logs.Warn("muxbw connbw", muxBw, connBw)
var n uint32
if connBw > 0 && muxBw > 0 {
n = uint32(math.Float64frombits(atomic.LoadUint64(&Self.mux.latency)) *
(muxBw + connBw))
}
//bufLen := Self.bufQueue.Len()
//if n < bufLen {
// n = bufLen
//}
if n < Self.maxSize/2 {
n = Self.maxSize / 2
//logs.Warn(n)
if n < common.MAXIMUM_SEGMENT_SIZE*30 {
//logs.Warn("window small", n, Self.mux.bw.Get(), Self.bw.Get())
n = common.MAXIMUM_SEGMENT_SIZE * 30
}
// set the minimal size
if n > 2*Self.maxSize {
n = 2 * Self.maxSize
for {
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
size, read, wait := Self.unpack(ptrs)
if n < size/2 {
n = size / 2
// half reduce
}
// set the minimal size
if n > 2*size {
n = 2 * size
// twice grow
}
if connBw > 0 && muxBw > 0 {
limit := uint32(common.MAXIMUM_WINDOW_SIZE * (connBw / (muxBw + connBw)))
if n > limit {
logs.Warn("window too large, calculated:", n, "limit:", limit, connBw, muxBw)
n = limit
}
}
// set the maximum size
//logs.Warn("n", n)
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(n, read, wait)) {
// only change the maxSize
break
}
}
if n > (common.MAXIMUM_WINDOW_SIZE / uint32(conns)) {
logs.Warn("window too large", n)
n = common.MAXIMUM_WINDOW_SIZE / uint32(conns)
}
// set the maximum size
//logs.Warn("n", n)
atomic.StoreUint32(&Self.maxSize, n)
Self.count = -10
}
Self.count += 1
@ -245,30 +283,40 @@ func (Self *ReceiveWindow) Write(buf []byte, l uint16, part bool, id int32) (err
return errors.New("conn.receiveWindow: write on closed window")
}
element, err := NewListElement(buf, l, part)
//logs.Warn("push the buf", len(buf), l, (&element).l)
//logs.Warn("push the buf", len(buf), l, element.L)
if err != nil {
return
}
Self.calcSize() // calculate the max window size
var wait uint32
var wait bool
var maxSize, read uint32
start:
ptrs := atomic.LoadUint64(&Self.remainingWait)
_, wait = Self.unpack(ptrs)
newRemaining := Self.remainingSize(l)
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
maxSize, read, wait = Self.unpack(ptrs)
remain := Self.remainingSize(maxSize, l)
// calculate the remaining window size now, plus the element we will push
if newRemaining == 0 {
if remain == 0 && !wait {
//logs.Warn("window full true", remaining)
wait = 1
}
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, wait)) {
goto start
// another goroutine change the status, make sure shall we need wait
}
wait = true
if !atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, read, wait)) {
// only change the wait status, not send the read size
goto start
// another goroutine change the status, make sure shall we need wait
}
//logs.Warn("receive window full")
} else if !wait {
if !atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, 0, wait)) {
// reset read size here, and send the read size directly
goto start
// another goroutine change the status, make sure shall we need wait
}
} // maybe there are still some data received even if window is full, just keep the wait status
// and push into queue. when receive window read enough, send window will be acknowledged.
Self.bufQueue.Push(element)
// status check finish, now we can push the element into the queue
if wait == 0 {
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.maxSize, newRemaining)
// send the remaining window size, not including zero size
if !wait {
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.pack(maxSize, read, false))
// send the current status to send window
}
return nil
}
@ -277,9 +325,16 @@ func (Self *ReceiveWindow) Read(p []byte, id int32) (n int, err error) {
if Self.closeOp {
return 0, io.EOF // receive close signal, returns eof
}
Self.bw.StartRead()
n, err = Self.readFromQueue(p, id)
Self.bw.SetCopySize(uint16(n))
return
}
func (Self *ReceiveWindow) readFromQueue(p []byte, id int32) (n int, err error) {
pOff := 0
l := 0
//logs.Warn("receive window read off, element.l", Self.off, Self.element.l)
//logs.Warn("receive window read off, element.l", Self.off, Self.element.L)
copyData:
if Self.off == uint32(Self.element.L) {
// on the first Read method invoked, Self.off and Self.element.l
@ -291,14 +346,13 @@ copyData:
Self.element, err = Self.bufQueue.Pop()
// if the queue is empty, Pop method will wait until one element push
// into the queue successful, or timeout.
// timer start on timeout parameter is set up ,
// reset to 60s if timeout and data still available
// timer start on timeout parameter is set up
Self.off = 0
if err != nil {
Self.CloseWindow() // also close the window, to avoid read twice
return // queue receive stop or time out, break the loop and return
}
//logs.Warn("pop element", Self.element.l, Self.element.part)
//logs.Warn("pop element", Self.element.L, Self.element.Part)
}
l = copy(p[pOff:], Self.element.Buf[Self.off:Self.element.L])
pOff += l
@ -320,22 +374,41 @@ copyData:
}
func (Self *ReceiveWindow) sendStatus(id int32, l uint16) {
var remaining, wait uint32
var maxSize, read uint32
var wait bool
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
remaining += uint32(l)
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(remaining, 0)) {
break
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
maxSize, read, wait = Self.unpack(ptrs)
if read <= (read+uint32(l))&mask31 {
read += uint32(l)
remain := Self.remainingSize(maxSize, 0)
if wait && remain > 0 || read >= maxSize/2 || remain == maxSize {
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, 0, false)) {
// now we get the current window status success
// receive window free up some space we need acknowledge send window, also reset the read size
// still having a condition that receive window is empty and not send the status to send window
// so send the status here
//logs.Warn("receive window free up some space", remain)
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.pack(maxSize, read, false))
break
}
} else {
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, read, wait)) {
// receive window not into the wait status, or still not having any space now,
// just change the read size
break
}
}
} else {
//overflow
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, uint32(l), wait)) {
// reset to l
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.pack(maxSize, read, false))
break
}
}
runtime.Gosched()
// another goroutine change remaining or wait status, make sure
// we need acknowledge other side
}
// now we get the current window status success
if wait == 1 {
//logs.Warn("send the wait status", remaining)
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, atomic.LoadUint32(&Self.maxSize), remaining)
}
return
}
@ -380,12 +453,14 @@ type SendWindow struct {
buf []byte
setSizeCh chan struct{}
timeout time.Time
// send window receive the receive window max size and read size
// done size store the size send window has send, send and read will be totally equal
// so send minus read, send window can get the current window size remaining
}
func (Self *SendWindow) New(mux *Mux) {
Self.setSizeCh = make(chan struct{})
Self.maxSize = common.MAXIMUM_SEGMENT_SIZE * 10
atomic.AddUint64(&Self.remainingWait, uint64(common.MAXIMUM_SEGMENT_SIZE*10)<<dequeueBits)
Self.maxSizeDone = Self.pack(common.MAXIMUM_SEGMENT_SIZE*30, 0, false)
Self.mux = mux
Self.window.New()
}
@ -396,7 +471,15 @@ func (Self *SendWindow) SetSendBuf(buf []byte) {
Self.off = 0
}
func (Self *SendWindow) SetSize(windowSize, newRemaining uint32) (closed bool) {
func (Self *SendWindow) remainingSize(maxSize, send uint32) uint32 {
l := int64(maxSize&mask31) - int64(send&mask31)
if l > 0 {
return uint32(l)
}
return 0
}
func (Self *SendWindow) SetSize(currentMaxSizeDone uint64) (closed bool) {
// set the window size from receive window
defer func() {
if recover() != nil {
@ -408,26 +491,34 @@ func (Self *SendWindow) SetSize(windowSize, newRemaining uint32) (closed bool) {
return true
}
//logs.Warn("set send window size to ", windowSize, newRemaining)
var remaining, wait, newWait uint32
var maxsize, send uint32
var wait, newWait bool
currentMaxSize, read, _ := Self.unpack(currentMaxSizeDone)
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
if remaining == newRemaining {
//logs.Warn("waiting for another window size")
return false // waiting for receive another usable window size
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
maxsize, send, wait = Self.unpack(ptrs)
if read > send {
logs.Error("window read > send: max size:", currentMaxSize, "read:", read, "send", send)
return
}
if newRemaining == 0 && wait == 1 {
newWait = 1 // keep the wait status,
// also if newRemaining is not zero, change wait to 0
if read == 0 && currentMaxSize == maxsize {
return
}
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(newRemaining, newWait)) {
send -= read
remain := Self.remainingSize(currentMaxSize, send)
if remain == 0 && wait {
// just keep the wait status
newWait = true
}
// remain > 0, change wait to false. or remain == 0, wait is false, just keep it
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(currentMaxSize, send, newWait)) {
break
}
// anther goroutine change wait status or window size
}
if wait == 1 {
if wait && !newWait {
// send window into the wait status, need notice the channel
//logs.Warn("send window remaining size is 0")
//logs.Warn("send window allow")
Self.allow()
}
// send window not into the wait status, so just do slide
@ -446,18 +537,20 @@ func (Self *SendWindow) allow() {
}
func (Self *SendWindow) sent(sentSize uint32) {
var remaining, wait uint32
var maxSie, send uint32
var wait bool
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
if remaining >= sentSize {
atomic.AddUint64(&Self.remainingWait, ^(uint64(sentSize)<<dequeueBits - 1))
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
maxSie, send, wait = Self.unpack(ptrs)
if (send+sentSize)&mask31 < send {
// overflow
runtime.Gosched()
continue
}
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSie, send+sentSize, wait)) {
// set the send size
//logs.Warn("sent", maxSie, send+sentSize, wait)
break
} else {
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, wait)) {
// just keep the wait status, it will be wait in the next loop
break
}
}
}
}
@ -472,12 +565,14 @@ func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err err
return nil, 0, false, io.EOF
// send window buff is drain, return eof and get another one
}
var remaining uint32
var maxSize, send uint32
start:
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, _ = Self.unpack(ptrs)
if remaining == 0 {
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, 1)) {
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
maxSize, send, _ = Self.unpack(ptrs)
remain := Self.remainingSize(maxSize, send)
if remain == 0 {
if !atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, send, true)) {
// just change the status wait status
goto start // another goroutine change the window, try again
}
// into the wait status
@ -490,17 +585,17 @@ start:
goto start
}
// there are still remaining window
//logs.Warn("rem", remaining)
//logs.Warn("rem", remain, maxSize, send)
if len(Self.buf[Self.off:]) > common.MAXIMUM_SEGMENT_SIZE {
sendSize = common.MAXIMUM_SEGMENT_SIZE
//logs.Warn("cut buf by mss")
} else {
sendSize = uint32(len(Self.buf[Self.off:]))
}
if remaining < sendSize {
if remain < sendSize {
// usable window size is small than
// window MAXIMUM_SEGMENT_SIZE or send buf left
sendSize = remaining
sendSize = remain
//logs.Warn("cut buf by remainingsize", sendSize, len(Self.buf[Self.off:]))
}
//logs.Warn("send size", sendSize)
@ -579,81 +674,49 @@ func (Self *SendWindow) SetTimeOut(t time.Time) {
Self.timeout = t
}
//type bandwidth struct {
// readStart time.Time
// lastReadStart time.Time
// readEnd time.Time
// lastReadEnd time.Time
// bufLength int
// lastBufLength int
// count int8
// readBW float64
// writeBW float64
// readBandwidth float64
//}
//
//func (Self *bandwidth) StartRead() {
// Self.lastReadStart, Self.readStart = Self.readStart, time.Now()
// if !Self.lastReadStart.IsZero() {
// if Self.count == -5 {
// Self.calcBandWidth()
// }
// }
//}
//
//func (Self *bandwidth) EndRead() {
// Self.lastReadEnd, Self.readEnd = Self.readEnd, time.Now()
// if Self.count == -5 {
// Self.calcWriteBandwidth()
// }
// if Self.count == 0 {
// Self.calcReadBandwidth()
// Self.count = -6
// }
// Self.count += 1
//}
//
//func (Self *bandwidth) SetCopySize(n int) {
// // must be invoke between StartRead and EndRead
// Self.lastBufLength, Self.bufLength = Self.bufLength, n
//}
//// calculating
//// start end start end
//// read read
//// write
//
//func (Self *bandwidth) calcBandWidth() {
// t := Self.readStart.Sub(Self.lastReadStart)
// if Self.lastBufLength >= 32768 {
// Self.readBandwidth = float64(Self.lastBufLength) / t.Seconds()
// }
//}
//
//func (Self *bandwidth) calcReadBandwidth() {
// // Bandwidth between nps and npc
// readTime := Self.readEnd.Sub(Self.readStart)
// Self.readBW = float64(Self.bufLength) / readTime.Seconds()
// //logs.Warn("calc read bw", Self.readBW, Self.bufLength, readTime.Seconds())
//}
//
//func (Self *bandwidth) calcWriteBandwidth() {
// // Bandwidth between nps and user, npc and application
// writeTime := Self.readStart.Sub(Self.lastReadEnd)
// Self.writeBW = float64(Self.lastBufLength) / writeTime.Seconds()
// //logs.Warn("calc write bw", Self.writeBW, Self.bufLength, writeTime.Seconds())
//}
//
//func (Self *bandwidth) Get() (bw float64) {
// // The zero value, 0 for numeric types
// if Self.writeBW == 0 && Self.readBW == 0 {
// //logs.Warn("bw both 0")
// return 100
// }
// if Self.writeBW == 0 && Self.readBW != 0 {
// return Self.readBW
// }
// if Self.readBW == 0 && Self.writeBW != 0 {
// return Self.writeBW
// }
// return Self.readBandwidth
//}
type writeBandwidth struct {
writeBW uint64 // store in bits, but it's float64
readEnd time.Time
duration float64
bufLength uint32
}
const writeCalcThreshold uint32 = 5 * 1024 * 1024
func NewWriteBandwidth() *writeBandwidth {
return &writeBandwidth{}
}
func (Self *writeBandwidth) StartRead() {
if Self.readEnd.IsZero() {
Self.readEnd = time.Now()
}
Self.duration += time.Now().Sub(Self.readEnd).Seconds()
if Self.bufLength >= writeCalcThreshold {
Self.calcBandWidth()
}
}
func (Self *writeBandwidth) SetCopySize(n uint16) {
Self.bufLength += uint32(n)
Self.endRead()
}
func (Self *writeBandwidth) endRead() {
Self.readEnd = time.Now()
}
func (Self *writeBandwidth) calcBandWidth() {
atomic.StoreUint64(&Self.writeBW, math.Float64bits(float64(Self.bufLength)/Self.duration))
Self.bufLength = 0
Self.duration = 0
}
func (Self *writeBandwidth) Get() (bw float64) {
// The zero value, 0 for numeric types
bw = math.Float64frombits(atomic.LoadUint64(&Self.writeBW))
if bw <= 0 {
bw = 0
}
return
}

View File

@ -9,8 +9,8 @@ import (
"sync/atomic"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
)
type Mux struct {
@ -220,7 +220,7 @@ func (s *Mux) pingReturn() {
case data = <-s.pingCh:
atomic.StoreUint32(&s.pingCheckTime, 0)
case <-s.closeChan:
break
return
}
_ = now.UnmarshalText(data)
latency := time.Now().UTC().Sub(now).Seconds() / 2
@ -303,7 +303,7 @@ func (s *Mux) readSession() {
if connection.isClose {
continue
}
connection.sendWindow.SetSize(pack.Window, pack.ReadLength)
connection.sendWindow.SetSize(pack.Window)
continue
case common.MUX_CONN_CLOSE: //close the connection
connection.closeFlag = true
@ -443,7 +443,7 @@ func (Self *bandwidth) Get() (bw float64) {
// The zero value, 0 for numeric types
bw = math.Float64frombits(atomic.LoadUint64(&Self.readBandwidth))
if bw <= 0 {
bw = 100
bw = 0
}
//logs.Warn(bw)
return

View File

@ -2,10 +2,9 @@ package mux
import (
"bufio"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/goroutine"
"fmt"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/goroutine"
"github.com/xtaci/kcp-go"
"io"
"log"
"net"
@ -34,8 +33,8 @@ func TestNewMux(t *testing.T) {
//poolConnCopy, _ := ants.NewPoolWithFunc(200000, common.copyConn, ants.WithNonblocking(false))
time.Sleep(time.Second * 3)
go func() {
//m2 := NewMux(conn2, "tcp")
m2 := NewMux(conn2, "kcp")
m2 := NewMux(conn2, "tcp")
//m2 := NewMux(conn2, "kcp")
for {
//logs.Warn("npc starting accept")
c, err := m2.Accept()
@ -84,8 +83,8 @@ func TestNewMux(t *testing.T) {
}()
go func() {
//m1 := NewMux(conn1, "tcp")
m1 := NewMux(conn1, "kcp")
m1 := NewMux(conn1, "tcp")
//m1 := NewMux(conn1, "kcp")
l, err := net.Listen("tcp", "127.0.0.1:7777")
if err != nil {
logs.Warn(err)
@ -147,14 +146,14 @@ func TestNewMux(t *testing.T) {
func server() {
var err error
//l, err := net.Listen("tcp", "127.0.0.1:9999")
l, err := kcp.Listen("127.0.0.1:9999")
l, err := net.Listen("tcp", "127.0.0.1:9999")
//l, err := kcp.Listen("127.0.0.1:9999")
if err != nil {
logs.Warn(err)
}
go func() {
conn1, err = l.Accept()
logs.Info("accept", conn1)
//logs.Info("accept", conn1)
if err != nil {
logs.Warn(err)
}
@ -164,9 +163,9 @@ func server() {
func client() {
var err error
//conn2, err = net.Dial("tcp", "127.0.0.1:9999")
logs.Warn("dial")
conn2, err = kcp.Dial("127.0.0.1:9999")
conn2, err = net.Dial("tcp", "127.0.0.1:9999")
//logs.Warn("dial")
//conn2, err = kcp.Dial("127.0.0.1:9999")
if err != nil {
logs.Warn(err)
}

View File

@ -12,8 +12,8 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/pkg/errors"
)

View File

@ -1,8 +1,8 @@
package mux
import (
"ehang.io/nps/lib/common"
"errors"
"github.com/cnlh/nps/lib/common"
"io"
"math"
"runtime"

View File

@ -14,7 +14,7 @@ func sysGetSock(fd *os.File) (bufferSize int, err error) {
if fd != nil {
return syscall.GetsockoptInt(int(fd.Fd()), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
} else {
return 1400 * 320, nil
return 5 * 1024 * 1024, nil
}
}

View File

@ -14,7 +14,7 @@ func sysGetSock(fd *os.File) (bufferSize int, err error) {
// not support, WTF???
// Todo
// return syscall.GetsockoptInt((syscall.Handle)(unsafe.Pointer(fd.Fd())), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
bufferSize = 10 * 1024 * 1024
bufferSize = 5 * 1024 * 1024
return
}

View File

@ -1,8 +1,8 @@
package version
const VERSION = "0.25.4"
const VERSION = "0.26.0"
// Compulsory minimum version, Minimum downward compatibility to this version
func GetVersion() string {
return "0.25.0"
return "0.26.0"
}

View File

@ -5,9 +5,9 @@ import (
"os"
"strconv"
"ehang.io/nps/lib/mux"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/mux"
)
var pMux *mux.PortMux

View File

@ -6,11 +6,11 @@ import (
"net/http"
"sync"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
)
type Service interface {

View File

@ -13,13 +13,13 @@ import (
"strings"
"sync"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/cache"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/connection"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/cache"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server/connection"
)
type httpServer struct {

View File

@ -6,13 +6,13 @@ import (
"net/url"
"sync"
"ehang.io/nps/lib/cache"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/cache"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/pkg/errors"
)

View File

@ -5,8 +5,8 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
)
type P2PServer struct {

View File

@ -7,10 +7,10 @@ import (
"net"
"strconv"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
)
const (

View File

@ -7,13 +7,13 @@ import (
"path/filepath"
"strconv"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/connection"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server/connection"
)
type TunnelModeServer struct {

View File

@ -7,8 +7,8 @@ import (
"strconv"
"syscall"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
)
func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {

View File

@ -3,7 +3,7 @@
package proxy
import (
"github.com/cnlh/nps/lib/conn"
"ehang.io/nps/lib/conn"
)
func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {

View File

@ -4,11 +4,11 @@ import (
"net"
"strings"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
)
type UdpModeServer struct {

View File

@ -1,21 +1,21 @@
package server
import (
"ehang.io/nps/lib/version"
"errors"
"github.com/cnlh/nps/lib/version"
"math"
"os"
"strconv"
"strings"
"time"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/proxy"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server/proxy"
"github.com/cnlh/nps/server/tool"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"

View File

@ -5,9 +5,9 @@ import (
"path/filepath"
"strconv"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
)
func TestServerConfig() {

View File

@ -5,8 +5,8 @@ import (
"strconv"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"

View File

@ -4,8 +4,8 @@ import (
"encoding/hex"
"time"
"ehang.io/nps/lib/crypt"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/crypt"
)
type AuthController struct {

View File

@ -7,11 +7,11 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/server"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server"
)
type BaseController struct {

View File

@ -1,11 +1,11 @@
package controllers
import (
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/rate"
"ehang.io/nps/server"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/rate"
"github.com/cnlh/nps/server"
)
type ClientController struct {

View File

@ -1,10 +1,10 @@
package controllers
import (
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server"
"github.com/cnlh/nps/server/tool"
"ehang.io/nps/lib/file"
"ehang.io/nps/server"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
)

View File

@ -6,10 +6,10 @@ import (
"sync"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"ehang.io/nps/server"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server"
)
type LoginController struct {

View File

@ -1,8 +1,8 @@
package routers
import (
"ehang.io/nps/web/controllers"
"github.com/astaxie/beego"
"github.com/cnlh/nps/web/controllers"
)
func Init() {

View File

@ -5,6 +5,6 @@
<title>nps error</title>
</head>
<body>
404 not found,power by <a href="//github.com/cnlh/nps">nps</a>
404 not found,power by <a href="//ehang.io/nps">nps</a>
</body>
</html>

View File

@ -37,7 +37,7 @@
</p>
<p>
goto <a href="//github.com/cnlh/nps">nps</a>
goto <a href="//ehang.io/nps">nps</a>
</p>
<div class="ibox-content">
<form class="m-t" onsubmit="return false">

View File

@ -105,7 +105,7 @@
<ul class="nav navbar-top-links navbar-right">
<li>
<span class="m-r-sm text-muted welcome-message">Welcome to use <a
href="https://github.com/cnlh/nps">NPS</a></span>
href="https://ehang.io/nps">NPS</a></span>
</li>
<li>
<a id="lang-en">English</a>
@ -128,7 +128,7 @@
{{.LayoutContent}}
<div class="footer">
<div class="pull-right">
read more <strong><a href="https://github.com/cnlh/nps">go</a></strong>
read more <strong><a href="https://ehang.io/nps">go</a></strong>
</div>
<div>
<strong>Copyright</strong> nps &copy; 2018-2019