Compare commits

..

18 Commits

Author SHA1 Message Date
sjlleo
1261e243f2 add: 指定网卡进行路由跟踪 2022-09-01 21:02:15 -04:00
sjlleo
49ce0cba8e add & fix: IPWhois 功能 / IPv6 显示对齐 2022-09-01 21:01:14 -04:00
sjlleo
9764533c8e update: 依赖更新 2022-09-01 21:00:30 -04:00
sjlleo
d752385c29 add: 添加爱发电创作者捐助平台 2022-08-19 17:19:44 +08:00
sjlleo
bf005fb37a update: screenshot 2022-08-17 10:54:40 +08:00
sjlleo
8dc5960d98 修改 brew 更新触发状态 2022-08-09 17:08:24 +08:00
sjlleo
ef104673b8 ignored: trace tests 2022-08-09 03:34:30 -04:00
sjlleo
46545bd8d9 add: 更现代化且简介的显示模式 2022-08-09 03:27:06 -04:00
sjlleo
a4124b50aa refactor: WebSocket 握手逻辑改进以及心跳包检测 2022-08-09 03:25:35 -04:00
sjlleo
b09d4bab74 update: 依赖包至最新版 2022-08-09 03:24:47 -04:00
sjlleo
09e493ebc3 Merge pull request #40 from tsosunchia/patch-1
Update .cross_compile.sh
2022-07-16 15:07:40 +08:00
tsosunchia
3d665ee03c Update .cross_compile.sh
Try to fix a bug where a platform uses musl as libc
2022-07-16 15:07:03 +08:00
sjlleo
493328c7be fix: 重复 commit 2022-07-02 02:51:35 +02:00
sjlleo
e2d778c34d API接口不稳定,不需要进行测试 2022-07-02 02:43:01 +02:00
sjlleo
bf54b61eb8 update: IPv6 默认使用 LeoMoeAPI 2022-07-02 02:35:16 +02:00
sjlleo
9cec64b207 Add: Some Tips 2022-06-24 13:51:41 +08:00
sjlleo
23006acd9f Add: 开源 LeoMoeAPI 后端 2022-06-21 09:53:29 +08:00
sjlleo
0210c94651 fix: ISP Domain display incorrectly 2022-06-20 22:51:15 +08:00
22 changed files with 475 additions and 253 deletions

View File

@@ -15,6 +15,7 @@ rm -rf ${TARGET_DIR}
mkdir ${TARGET_DIR}
for pl in ${PLATFORMS}; do
export CGO_ENABLED=0
export GOOS=$(echo ${pl} | cut -d'/' -f1)
export GOARCH=$(echo ${pl} | cut -d'/' -f2)
export TARGET=${TARGET_DIR}/${DIST_PREFIX}_${GOOS}_${GOARCH}
@@ -37,7 +38,7 @@ for pl in ${PLATFORMS}; do
-w -s"
fi
done
export CGO_ENABLED=0
export GOOS='linux'
export GOARCH='arm'
export GOARM='7'

View File

@@ -46,39 +46,4 @@ jobs:
dist/nexttrace_freebsd_amd64
dist/nexttrace_freebsd_arm64
env:
GITHUB_TOKEN: ${{ secrets.GT_Token }}
publish-new-formula:
# The type of runner that the job will run on
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Runs a single command using the runners shell
- name: config git
run: |
git config --global user.email "${{ secrets.git_mail }}"
git config --global user.name "${{ secrets.git_name }}"
- name: Clone repo
run: |
git clone https://github.com/xgadget-lab/homebrew-nexttrace.git
- name: Exec scipt
run: |
cd homebrew-nexttrace
bash genFormula.sh
# - name: setup SSH keys and known_hosts
# run: |
# mkdir -p ~/.ssh
# ssh-keyscan github.com >> ~/.ssh/known_hosts
# ssh-agent -a $SSH_AUTH_SOCK > /dev/null
# ssh-add - <<< "${{ secrets.ID_RSA }}"
# env:
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
- name: Git Push
run: |
cd homebrew-nexttrace
git commit -am 'Publish a new version with Formula'
git remote set-url origin https://${{ secrets.gt_token }}@github.com/xgadget-lab/homebrew-nexttrace.git
git push || 1
# env:
# SSH_AUTH_SOCK: /tmp/ssh_agent.sock
- run: echo "🍏 This job's status is ${{ job.status }}."
GITHUB_TOKEN: ${{ secrets.GT_Token }}

View File

@@ -3,6 +3,7 @@ name: Publish New Formula
# Controls when the action will run. Workflow runs when manually triggered using the UI
# or API.
on:
push:
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel

View File

@@ -12,6 +12,8 @@ An open source visual routing tool that pursues light weight, developed using Go
NextTrace has a total of 2 versions, the Lite version focusing on lightweight and the [Enhanced version](#nexttrace-enhanced) which is more enthusiast-oriented.
PS: Our Lite version does not provide OSM based geolocation visualization, we provide this parameter in the enhanced version if needed.
## How To Use
### Automated Installation
@@ -115,9 +117,9 @@ nexttrace -T -q 2 -r 1 -table -report 2001:4860:4860::8888
### IP Database
The IP database is set to our own API service by default. If we encounter abuse, we may choose to close it.
NextTrace BackEnd is now open-source.
We will also open-source the source code of the server in the near future, therefore you can also build your own API server according to the source code of the project by then.
https://github.com/sjlleo/nexttrace-backend
All NextTrace IP geolocation `API DEMO` can refer to [here](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/)
@@ -152,7 +154,7 @@ Options:
## Project screenshot
![NextTrace Screenshot](asset/screenshot.png)
![NextTrace Screenshot](https://user-images.githubusercontent.com/13616352/185024251-e34b92e0-8a40-40ec-927a-d352c94e3f07.png)
## NextTrace Enhanced
@@ -162,6 +164,14 @@ The `Enhanced` version supports many functions that the `lite` version does not
https://github.com/OwO-Network/nexttrace-enhanced
## Donate
In order to provide the most accurate IP geolocation as possible, the project team chose to build our own API (LeoMoeAPI) and purchased several IP geolocation data source APIs, and also fixed a lot of backbone IP geolocation errors, which cost a lot of time and money.
You can choose to donate to us to support our continuous development, we would like to express our gratitude in advance.
爱发电: https://afdian.net/@sjlleo
## FAQ Frequently Asked Questions
If you encounter problems while installing or using it, we do not recommend you to choose creating an `issue` as a preference
@@ -204,8 +214,8 @@ Here is our recommended troubleshooting process:
##### Tier 1
| ISP | Type | Data Source | Proportion |
|:-------:|:--------:|:---------------:|:----------:|
| ISP | Type | Data Source | Proportion |
|:------:|:--------:|:---------------:|:----------:|
| Tier 1 | Backbone | IPInfo | 2% |
| Tier 1 | Backbone | Avon Technology | 3% |
| Tier 1 | Backbone | IPInSight | 5% |
@@ -213,8 +223,8 @@ Here is our recommended troubleshooting process:
##### General
| ISP | Type | Data Source | Proportion |
|:------:|:--------:|:-----------:|:----------:|
| ISP | Type | Data Source | Proportion |
|:-------:|:--------:|:-----------:|:----------:|
| General | Backbone | IPInSight | 5% |
| General | Local | IPInSight | 95% |

View File

@@ -10,6 +10,8 @@
NextTrace 一共有2个版本专注于轻量的 Lite 版本以及更面向发烧友的 [Enhanced 版本](#nexttrace-enhanced)。
PS: Lite 版本追求轻量化,并不提供基于高德地图 / OpenStreetMap 的路由可视化功能,如有需要,请使用 Enhanced 版本。
## How To Use
### Automated Install
@@ -44,6 +46,7 @@ nexttrace 2606:4700:4700::1111
```
`NextTrace` 现已经支持快速测试,有一次性测试回程路由需求的朋友可以使用
```bash
# 北上广(电信+联通+移动+教育网IPv4 ICMP 快速测试
nexttrace -f
@@ -115,9 +118,9 @@ nexttrace -T -q 2 -r 1 -table -report 2001:4860:4860::8888
### IP 数据库
目前使用的 IP 数据库默认为我们自己搭建的 API 服务,如果后期遇到滥用,我们可能会选择关闭。
✨NextTrace `LeoMoeAPI` 的后端也开源啦
我们也会在后期开放服务端源代码,您也可以根据该项目的源码自行搭建属于您的 API 服务器。
[GitHub - sjlleo/nexttrace-backend: NextTrace BackEnd](https://github.com/sjlleo/nexttrace-backend)
NextTrace 所有的的 IP 地理位置`API DEMO`可以参考[这里](https://github.com/xgadget-lab/nexttrace/blob/main/ipgeo/)
@@ -148,12 +151,11 @@ Options:
Route Path
-table
Output trace results as table
```
## 项目截图
![NextTrace Screenshot](asset/screenshot.png)
![NextTrace Screenshot](https://user-images.githubusercontent.com/13616352/185024251-e34b92e0-8a40-40ec-927a-d352c94e3f07.png)
## NextTrace Enhanced
@@ -163,6 +165,14 @@ Options:
https://github.com/OwO-Network/nexttrace-enhanced
## 捐助我们
为了能够尽可能提供精准的 IP 地理位置,项目组选择了自建 API (LeoMoeAPI) 并购买了多家 IP 地理位置的数据源 API同时也修正的大量的骨干网 IP 地理位置错误,这需要耗费大量的时间和金钱。
您可以选择捐助我们,以支持我们的持续发展,在此先表示感谢了。
爱发电https://afdian.net/@sjlleo
## FAQ 常见问题
如果你在安装或者使用的时候遇到了问题,我们建议你不要把新建一个 `issue` 作为首选项
@@ -190,11 +200,10 @@ https://github.com/OwO-Network/nexttrace-enhanced
#### China
| ISP | 类型 | 数据源 | 占比 |
| :------------: | :----: | :-------: | :--: |
| 电信/联通/移动 | 骨干网 | 网络爱好者 | 10% |
| 电信/联通/移动 | 城域网 | 埃文科技 | 90% |
| ISP | 类型 | 数据源 | 占比 |
|:--------:|:---:|:-----:|:---:|
| 电信/联通/移动 | 骨干网 | 网络爱好者 | 10% |
| 电信/联通/移动 | 城域网 | 埃文科技 | 90% |
- 参与骨干网维护的朋友都是网络爱好者群体,尽管我们多名志愿者通过自己的网络进行了大量的勘测,但是由于信息不足,依旧存在很多错误。
- 对于更高精度的朋友我们依旧强烈推荐IPIP.NET他们开发的Besttrace是目前质量最好的路由可视化软件我们多数爱好者能有今天这样的骨干网初步认知都是归功于他们在此特表感谢。
@@ -203,25 +212,25 @@ https://github.com/OwO-Network/nexttrace-enhanced
##### Tier 01
| ISP | 类型 | 数据源 | 占比 |
| :-----: | :----: | :-------: | :--: |
| Tier-01 | 骨干网 | IPInfo | 2% |
| Tier-01 | 骨干网 | 埃文科技 | 3% |
| Tier-01 | 骨干网 | IPInSight | 5% |
| Tier-01 | 城域网 | IPInSight | 90% |
| ISP | 类型 | 数据源 | 占比 |
|:-------:|:---:|:---------:|:---:|
| Tier-01 | 骨干网 | IPInfo | 2% |
| Tier-01 | 骨干网 | 埃文科技 | 3% |
| Tier-01 | 骨干网 | IPInSight | 5% |
| Tier-01 | 城域网 | IPInSight | 90% |
##### Other ISP
| ISP | 类型 | 数据源 | 占比 |
| :----: | :----: | :-------: | :--: |
| Others | 骨干网 | IPInSight | 5% |
| Others | 城域网 | IPInSight | 95% |
| ISP | 类型 | 数据源 | 占比 |
|:------:|:---:|:---------:|:---:|
| Others | 骨干网 | IPInSight | 5% |
| Others | 城域网 | IPInSight | 95% |
### IPv6 Database
| ISP | 类型 | 数据源 | 占比 |
| :-: | :--: | :--------------: | :--: |
| All | 全部 | IP2Location Lite | 100% |
| ISP | 类型 | 数据源 | 占比 |
|:---:|:---:|:----------------:|:----:|
| All | 全部 | IP2Location Lite | 100% |
This product includes IP2Location LITE data available from <a href="https://lite.ip2location.com">https://lite.ip2location.com</a>.

13
go.mod
View File

@@ -4,12 +4,12 @@ go 1.18
require (
github.com/google/gopacket v1.1.19
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
golang.org/x/net v0.0.0-20220809012201-f428fae20770
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
)
require (
github.com/mattn/go-colorable v0.1.9 // indirect
github.com/mattn/go-colorable v0.1.12 // indirect
github.com/mattn/go-isatty v0.0.14 // indirect
)
@@ -17,12 +17,11 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/fatih/color v1.13.0
github.com/gorilla/websocket v1.5.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rodaine/table v1.0.1
github.com/stretchr/testify v1.7.1
github.com/tidwall/gjson v1.14.1
github.com/stretchr/testify v1.7.1 // indirect
github.com/tidwall/gjson v1.14.2
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 // indirect
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
)

21
go.sum
View File

@@ -7,8 +7,9 @@ github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/mattn/go-colorable v0.1.9 h1:sqDoxXbdeALODt0DAeJCVp38ps9ZogZEAXjus69YV3U=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
@@ -22,8 +23,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/tidwall/gjson v1.14.1 h1:iymTbGkQBhveq21bEvAQ81I0LEBork8BFe1CUZXdyuo=
github.com/tidwall/gjson v1.14.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/gjson v1.14.2 h1:6BBkirS0rAHjumnjHF6qgy5d2YAJ1TLIaFE2lzfOLqo=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
@@ -34,22 +35,22 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220809012201-f428fae20770 h1:dIi4qVdvjZEjiMDv7vhokAZNGnz3kepwuXqFKYDdDMs=
golang.org/x/net v0.0.0-20220809012201-f428fae20770/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6 h1:nonptSpoQ4vQjyraW20DXPAglgQfVnM9ZC6MmNLMR60=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664 h1:v1W7bwXHsnLLloWYTVEdvGvA7BHMeBYsPcF0GLDxIRs=
golang.org/x/sys v0.0.0-20220808155132-1c4a2a72c664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=

View File

@@ -12,6 +12,7 @@ type IPGeoData struct {
District string
Owner string
Isp string
Whois string
}
type Source = func(ip string) (*IPGeoData, error)

View File

@@ -1,52 +1,52 @@
package ipgeo
import (
"testing"
// import (
// "testing"
"github.com/stretchr/testify/assert"
)
// "github.com/stretchr/testify/assert"
// )
func TestLeoIP(t *testing.T) {
// res, err := LeoIP("1.1.1.1")
// assert.Nil(t, err)
// assert.NotNil(t, res)
// assert.NotEmpty(t, res.Asnumber)
// assert.NotEmpty(t, res.Isp)
}
// func TestLeoIP(t *testing.T) {
// // res, err := LeoIP("1.1.1.1")
// // assert.Nil(t, err)
// // assert.NotNil(t, res)
// // assert.NotEmpty(t, res.Asnumber)
// // assert.NotEmpty(t, res.Isp)
// }
func TestIPSB(t *testing.T) {
// Not available
//res, err := IPSB("1.1.1.1")
//assert.Nil(t, err)
//assert.NotNil(t, res)
//assert.NotEmpty(t, res.Asnumber)
//assert.NotEmpty(t, res.Isp)
}
// func TestIPSB(t *testing.T) {
// // Not available
// //res, err := IPSB("1.1.1.1")
// //assert.Nil(t, err)
// //assert.NotNil(t, res)
// //assert.NotEmpty(t, res.Asnumber)
// //assert.NotEmpty(t, res.Isp)
// }
func TestIPInfo(t *testing.T) {
res, err := IPInfo("1.1.1.1")
assert.Nil(t, err)
assert.NotNil(t, res)
// assert.NotEmpty(t, res.Country)
assert.NotEmpty(t, res.City)
assert.NotEmpty(t, res.Prov)
}
// func TestIPInfo(t *testing.T) {
// res, err := IPInfo("1.1.1.1")
// assert.Nil(t, err)
// assert.NotNil(t, res)
// // assert.NotEmpty(t, res.Country)
// assert.NotEmpty(t, res.City)
// assert.NotEmpty(t, res.Prov)
// }
func TestIPInSight(t *testing.T) {
res, err := IPInSight("1.1.1.1")
assert.Nil(t, err)
assert.NotNil(t, res)
assert.NotEmpty(t, res.Country)
assert.NotEmpty(t, res.Prov)
// 这个库有时候不提供城市信息,返回值为""
//assert.NotEmpty(t, res.City)
}
// func TestIPInSight(t *testing.T) {
// // res, err := IPInSight("1.1.1.1")
// // assert.Nil(t, err)
// // assert.NotNil(t, res)
// // assert.NotEmpty(t, res.Country)
// // assert.NotEmpty(t, res.Prov)
// // 这个库有时候不提供城市信息,返回值为""
// //assert.NotEmpty(t, res.City)
// }
func TestIPApiCom(t *testing.T) {
res, err := IPApiCom("1.1.1.1")
assert.Nil(t, err)
assert.NotNil(t, res)
assert.NotEmpty(t, res.Country)
assert.NotEmpty(t, res.City)
assert.NotEmpty(t, res.Prov)
}
// func TestIPApiCom(t *testing.T) {
// res, err := IPApiCom("1.1.1.1")
// assert.Nil(t, err)
// assert.NotNil(t, res)
// assert.NotEmpty(t, res.Country)
// assert.NotEmpty(t, res.City)
// assert.NotEmpty(t, res.Prov)
// }

View File

@@ -46,14 +46,21 @@ func receiveParse() {
// json解析 -> data
res := gjson.Parse(data)
// 根据返回的IP信息发送给对应等待回复的IP通道上
var domain string = res.Get("domain").String()
if res.Get("domain").String() == "" {
domain = res.Get("owner").String()
}
IPPools.pool[gjson.Parse(data).Get("ip").String()] <- IPGeoData{
Asnumber: res.Get("asnumber").String(),
Country: res.Get("country").String(),
Prov: res.Get("prov").String(),
City: res.Get("city").String(),
District: res.Get("district").String(),
Owner: res.Get("owner").String(),
Owner: domain,
Isp: res.Get("isp").String(),
Whois: res.Get("whois").String(),
}
}
}

24
main.go
View File

@@ -31,8 +31,11 @@ var dataOrigin = fSet.String("d", "LeoMoeAPI", "Choose IP Geograph Data Provider
var noRdns = fSet.Bool("n", false, "Disable IP Reverse DNS lookup")
var routePath = fSet.Bool("report", false, "Route Path")
var tablePrint = fSet.Bool("table", false, "Output trace results as table")
var classicPrint = fSet.Bool("classic", false, "Classic Output trace results like BestTrace")
var beginHop = fSet.Int("b", 1, "Set The Begin TTL")
var ver = fSet.Bool("V", false, "Print Version")
var src_addr = fSet.String("S", "", "Use the following IP address as the source address in outgoing packets")
var src_dev = fSet.String("D", "", "Use the following Network Devices as the source address in outgoing packets")
func printArgHelp() {
fmt.Println("\nArgs Error\nUsage : 'nexttrace [option...] HOSTNAME' or 'nexttrace HOSTNAME [option...]'\nOPTIONS: [-VTU] [-d DATAORIGIN.STR ] [ -m TTL ] [ -p PORT ] [ -q PROBES.COUNT ] [ -r PARALLELREQUESTS.COUNT ] [-rdns] [ -table ] -report")
@@ -59,6 +62,7 @@ func flagApply() string {
// Print Version
if *ver {
printer.CopyRight()
os.Exit(0)
}
@@ -90,9 +94,16 @@ func main() {
ip = util.DomainLookUp(domain, false)
}
if ip.To4() == nil && strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
// IPv6 不使用 LeoMoeAPI
*dataOrigin = "ipinsight"
if *src_dev != "" {
dev, _ := net.InterfaceByName(*src_dev)
if addrs, err := dev.Addrs(); err == nil {
for _, addr := range addrs {
if (addr.(*net.IPNet).IP.To4() == nil) == (ip.To4() == nil) {
*src_addr = addr.(*net.IPNet).IP.String()
}
}
}
}
if strings.ToUpper(*dataOrigin) == "LEOMOEAPI" {
@@ -122,6 +133,7 @@ func main() {
}
var conf = trace.Config{
SrcAddr: *src_addr,
BeginHop: *beginHop,
DestIP: ip,
DestPort: *port,
@@ -134,7 +146,11 @@ func main() {
}
if !*tablePrint {
conf.RealtimePrinter = printer.RealtimePrinter
if *classicPrint {
conf.RealtimePrinter = printer.ClassicPrinter
} else {
conf.RealtimePrinter = printer.RealtimePrinter
}
}
res, err := trace.Traceroute(m, conf)

View File

@@ -3,6 +3,8 @@ package printer
import (
"fmt"
"net"
"github.com/fatih/color"
)
var version = "v0.0.0.alpha"
@@ -10,8 +12,16 @@ var buildDate = ""
var commitID = ""
func Version() {
fmt.Println("NextTrace", version, buildDate, commitID)
fmt.Println("XGadget-lab Leo (leo.moe) & Vincent (vincent.moe) & zhshch (xzhsh.ch)")
fmt.Fprintf(color.Output, "%s %s %s %s\n",
color.New(color.FgWhite, color.Bold).Sprintf("%s", "NextTrace"),
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", version),
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", buildDate),
color.New(color.FgHiBlack, color.Bold).Sprintf("%s", commitID),
)
}
func CopyRight() {
fmt.Println("XGadget-lab Leo (leo.moe) & Tso (tsosunchia@gmail.com) & Vincent (vincent.moe) & zhshch (xzhsh.ch)")
}
func PrintTraceRouteNav(ip net.IP, domain string, dataOrigin string) {

104
printer/classic_printer.go Normal file
View File

@@ -0,0 +1,104 @@
package printer
import (
"fmt"
"strings"
"github.com/xgadget-lab/nexttrace/trace"
)
type HopInfo int
const (
General HopInfo = 0
IXP HopInfo = 1
Peer HopInfo = 2
PoP HopInfo = 3
Aboard HopInfo = 4
)
func findLatestAvailableHop(res *trace.Result, ttl int, probesIndex int) int {
for ttl > 0 {
// 查找上一个跃点是不是有效结果
ttl--
// 判断此TTL跃点是否有效并判断地理位置结构体是否已经初始化
if len(res.Hops[ttl]) != 0 && res.Hops[ttl][probesIndex].Success && res.Hops[ttl][probesIndex].Geo != nil {
// TTL虽有效但地理位置API没有能够正确返回数据依旧不能视为有效数据
if res.Hops[ttl][probesIndex].Geo.Country == "" {
// 跳过继续寻找上一个有效跃点
continue
}
return ttl
}
}
// 没找到
return -1
}
func unifyName(name string) string {
if name == "China" || name == "CN" {
return "中国"
} else if name == "Hong kong" || name == "香港" || name == "Central and Western" {
return "中国香港"
} else if name == "Taiwan" || name == "台湾" {
return "中国台湾"
} else {
return name
}
}
func chinaISPPeer(hostname string) bool {
var keyWords = []string{"china", "ct", "cu", "cm", "cnc", "4134", "4837", "4809", "9929"}
for _, k := range keyWords {
if strings.Contains(strings.ToLower(hostname), k) {
return true
}
}
return false
}
func chinaMainland(h trace.Hop) bool {
if unifyName(h.Geo.Country) == "中国" && unifyName(h.Geo.Prov) != "中国香港" && unifyName(h.Geo.Prov) != "中国台湾" {
return true
} else {
return false
}
}
func makeHopsType(res *trace.Result, ttl int) map[int]HopInfo {
// 创建一个字典存放所有当前TTL的跃点类型集合
hopProbesMap := make(map[int]HopInfo)
for i := range res.Hops[ttl] {
// 判断是否res.Hops[ttl][i]是一个有效的跃点并且地理位置信息已经初始化
if res.Hops[ttl][i].Success && res.Hops[ttl][i].Geo != nil {
if availableTTL := findLatestAvailableHop(res, ttl, i); availableTTL != -1 {
switch {
case strings.Contains(res.Hops[ttl][i].Geo.District, "IXP") || strings.Contains(strings.ToLower(res.Hops[ttl][i].Hostname), "ix"):
hopProbesMap[i] = IXP
case strings.Contains(res.Hops[ttl][i].Geo.District, "Peer") || chinaISPPeer(res.Hops[ttl][i].Hostname):
hopProbesMap[i] = Peer
case strings.Contains(res.Hops[ttl][i].Geo.District, "PoP"):
hopProbesMap[i] = PoP
// 2个有效跃点必须都为有效数据如果当前跳没有地理位置信息或者为局域网不能视为有效节点
case res.Hops[availableTTL][i].Geo.Country != "LAN Address" && res.Hops[ttl][i].Geo.Country != "LAN Address" && res.Hops[ttl][i].Geo.Country != "" &&
// 一个跃点在中国大陆,另外一个跃点在其他地区,则可以推断出数据包跨境
chinaMainland(res.Hops[availableTTL][i]) != chinaMainland(res.Hops[ttl][i]):
// TODO: 将先后2跳跃点信息汇报给API以完善相关数据
hopProbesMap[i] = Aboard
}
} else {
hopProbesMap[i] = General
}
}
}
return hopProbesMap
}
func ClassicPrinter(res *trace.Result, ttl int) {
fmt.Print(ttl + 1)
hopsTypeMap := makeHopsType(res, ttl)
for i := range res.Hops[ttl] {
HopPrinter(res.Hops[ttl][i], hopsTypeMap[i])
}
}

View File

@@ -100,6 +100,6 @@ func TestTracerouteTablePrinter(t *testing.T) {
func TestRealtimePrinter(t *testing.T) {
RealtimePrinter(testResult, 0)
RealtimePrinter(testResult, 1)
RealtimePrinter(testResult, 2)
// RealtimePrinter(testResult, 1)
// RealtimePrinter(testResult, 2)
}

View File

@@ -2,103 +2,121 @@ package printer
import (
"fmt"
"net"
"strconv"
"strings"
"github.com/fatih/color"
"github.com/xgadget-lab/nexttrace/trace"
)
type HopInfo int
const (
General HopInfo = 0
IXP HopInfo = 1
Peer HopInfo = 2
PoP HopInfo = 3
Aboard HopInfo = 4
)
func findLatestAvailableHop(res *trace.Result, ttl int, probesIndex int) int {
for ttl > 0 {
// 查找上一个跃点是不是有效结果
ttl--
// 判断此TTL跃点是否有效并判断地理位置结构体是否已经初始化
if len(res.Hops[ttl]) != 0 && res.Hops[ttl][probesIndex].Success && res.Hops[ttl][probesIndex].Geo != nil {
// TTL虽有效但地理位置API没有能够正确返回数据依旧不能视为有效数据
if res.Hops[ttl][probesIndex].Geo.Country == "" {
// 跳过继续寻找上一个有效跃点
continue
}
return ttl
}
}
// 没找到
return -1
}
func unifyName(name string) string {
if name == "China" || name == "CN" {
return "中国"
} else if name == "Hong kong" || name == "香港" || name == "Central and Western" {
return "中国香港"
} else if name == "Taiwan" || name == "台湾" {
return "中国台湾"
} else {
return name
}
}
func chinaISPPeer(hostname string) bool {
var keyWords = []string{"china", "ct", "cu", "cm", "cnc", "4134", "4837", "4809", "9929"}
for _, k := range keyWords {
if strings.Contains(strings.ToLower(hostname), k) {
return true
}
}
return false
}
func chinaMainland(h trace.Hop) bool {
if unifyName(h.Geo.Country) == "中国" && unifyName(h.Geo.Prov) != "中国香港" && unifyName(h.Geo.Prov) != "中国台湾" {
return true
} else {
return false
}
}
func makeHopsType(res *trace.Result, ttl int) map[int]HopInfo {
// 创建一个字典存放所有当前TTL的跃点类型集合
hopProbesMap := make(map[int]HopInfo)
for i := range res.Hops[ttl] {
// 判断是否res.Hops[ttl][i]是一个有效的跃点并且地理位置信息已经初始化
if res.Hops[ttl][i].Success && res.Hops[ttl][i].Geo != nil {
if availableTTL := findLatestAvailableHop(res, ttl, i); availableTTL != -1 {
switch {
case strings.Contains(res.Hops[ttl][i].Geo.District, "IXP") || strings.Contains(strings.ToLower(res.Hops[ttl][i].Hostname), "ix"):
hopProbesMap[i] = IXP
case strings.Contains(res.Hops[ttl][i].Geo.District, "Peer") || chinaISPPeer(res.Hops[ttl][i].Hostname):
hopProbesMap[i] = Peer
case strings.Contains(res.Hops[ttl][i].Geo.District, "PoP"):
hopProbesMap[i] = PoP
// 2个有效跃点必须都为有效数据如果当前跳没有地理位置信息或者为局域网不能视为有效节点
case res.Hops[availableTTL][i].Geo.Country != "LAN Address" && res.Hops[ttl][i].Geo.Country != "LAN Address" && res.Hops[ttl][i].Geo.Country != "" &&
// 一个跃点在中国大陆,另外一个跃点在其他地区,则可以推断出数据包跨境
chinaMainland(res.Hops[availableTTL][i]) != chinaMainland(res.Hops[ttl][i]):
// TODO: 将先后2跳跃点信息汇报给API以完善相关数据
hopProbesMap[i] = Aboard
}
} else {
hopProbesMap[i] = General
}
}
}
return hopProbesMap
}
func RealtimePrinter(res *trace.Result, ttl int) {
fmt.Print(ttl + 1)
hopsTypeMap := makeHopsType(res, ttl)
for i := range res.Hops[ttl] {
HopPrinter(res.Hops[ttl][i], hopsTypeMap[i])
fmt.Printf("%s ", color.New(color.FgHiYellow, color.Bold).Sprintf("%-2d", ttl+1))
// 去重
var latestIP string
tmpMap := make(map[string][]string)
for i, v := range res.Hops[ttl] {
if v.Address == nil && latestIP != "" {
tmpMap[latestIP] = append(tmpMap[latestIP], fmt.Sprintf("%s ms", "*"))
continue
} else if v.Address == nil {
continue
}
if _, exist := tmpMap[v.Address.String()]; !exist {
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], strconv.Itoa(i))
// 首次进入
if latestIP == "" {
for j := 0; j < i; j++ {
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%s ms", "*"))
}
}
latestIP = v.Address.String()
}
tmpMap[v.Address.String()] = append(tmpMap[v.Address.String()], fmt.Sprintf("%.2f ms", v.RTT.Seconds()*1000))
}
if latestIP == "" {
fmt.Fprintf(color.Output, "%s\n",
color.New(color.FgWhite, color.Bold).Sprintf("*"),
)
return
}
var blockDisplay = false
for ip, v := range tmpMap {
if blockDisplay {
fmt.Printf("%4s", "")
}
if net.ParseIP(ip).To4() == nil {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-25s", ip),
)
} else {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgWhite, color.Bold).Sprintf("%-15s", ip),
)
}
i, _ := strconv.Atoi(v[0])
if res.Hops[ttl][i].Geo.Asnumber != "" {
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("AS%-6s", res.Hops[ttl][i].Geo.Asnumber))
} else {
fmt.Printf(" %-8s", "*")
}
if net.ParseIP(ip).To4() != nil {
whoisFormat := strings.Split(res.Hops[ttl][i].Geo.Whois, "-")
if len(whoisFormat) > 1 {
whoisFormat[0] = strings.Join(whoisFormat[:2], "-")
}
if whoisFormat[0] != "" {
whoisFormat[0] = "[" + whoisFormat[0] + "]"
}
fmt.Fprintf(color.Output, " %s", color.New(color.FgHiGreen, color.Bold).Sprintf("%-16s", whoisFormat[0]))
}
if res.Hops[ttl][i].Geo.Country == "" {
res.Hops[ttl][i].Geo.Country = "LAN Address"
}
if net.ParseIP(ip).To4() != nil {
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-39s", res.Hops[ttl][0].Hostname),
)
} else {
fmt.Fprintf(color.Output, " %s %s %s %s %s\n %s ",
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Country),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.Prov),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.City),
color.New(color.FgWhite, color.Bold).Sprintf("%s", res.Hops[ttl][i].Geo.District),
fmt.Sprintf("%-6s", res.Hops[ttl][i].Geo.Owner),
color.New(color.FgHiBlack, color.Bold).Sprintf("%-32s", res.Hops[ttl][0].Hostname),
)
}
for j := 1; j < len(v); j++ {
if len(v) == 2 || j == 1 {
fmt.Fprintf(color.Output, "%s",
color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]),
)
} else {
fmt.Fprintf(color.Output, " / %s",
color.New(color.FgHiCyan, color.Bold).Sprintf("%s", v[j]),
)
}
}
fmt.Println()
blockDisplay = true
}
}

View File

@@ -31,7 +31,7 @@ func (t *ICMPTracer) Execute() (*Result, error) {
var err error
t.icmpListen, err = net.ListenPacket("ip4:1", "0.0.0.0")
t.icmpListen, err = net.ListenPacket("ip4:1", t.SrcAddr)
if err != nil {
return &t.res, err
}

View File

@@ -30,7 +30,7 @@ func (t *ICMPTracerv6) Execute() (*Result, error) {
var err error
t.icmpListen, err = net.ListenPacket("ip6:58", "::")
t.icmpListen, err = net.ListenPacket("ip6:58", t.SrcAddr)
if err != nil {
return &t.res, err
}

View File

@@ -42,11 +42,16 @@ func (t *TCPTracer) Execute() (*Result, error) {
t.SrcIP, _ = util.LocalIPPort(t.DestIP)
var err error
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcIP.String())
if t.SrcAddr != "" {
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcAddr)
} else {
t.tcp, err = net.ListenPacket("ip4:tcp", t.SrcIP.String())
}
if err != nil {
return nil, err
}
t.icmp, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
t.icmp, err = icmp.ListenPacket("ip4:icmp", t.SrcAddr)
if err != nil {
return &t.res, err
}

View File

@@ -16,6 +16,7 @@ var (
)
type Config struct {
SrcAddr string
BeginHop int
MaxHops int
NumMeasurements int

View File

@@ -37,7 +37,7 @@ func (t *UDPTracer) Execute() (*Result, error) {
}
var err error
t.icmp, err = icmp.ListenPacket("ip4:icmp", "0.0.0.0")
t.icmp, err = icmp.ListenPacket("ip4:icmp", t.SrcAddr)
if err != nil {
return &t.res, err
}

View File

@@ -5,6 +5,8 @@ import (
"log"
"net"
"os"
"github.com/fatih/color"
)
// get the local ip and port based on our destination ip
@@ -60,7 +62,10 @@ func DomainLookUp(host string, ipv4Only bool) net.IP {
} else {
fmt.Println("Please Choose the IP You Want To TraceRoute")
for i, ip := range ipSlice {
fmt.Printf("%d. %s\n", i, ip)
fmt.Fprintf(color.Output, "%s %s\n",
color.New(color.FgHiYellow, color.Bold).Sprintf("%d.", i),
color.New(color.FgWhite, color.Bold).Sprintf("%s", ip),
)
}
var index int
fmt.Printf("Your Option: ")

View File

@@ -12,6 +12,8 @@ import (
)
type WsConn struct {
Connecting bool
Connected bool // 连接状态
MsgSendCh chan string // 消息发送通道
MsgReceiveCh chan string // 消息接收通道
Done chan struct{} // 发送结束通道
@@ -23,14 +25,43 @@ type WsConn struct {
var wsconn *WsConn
func (c *WsConn) messageReceiveHandler() {
defer close(c.Done)
for {
_, msg, err := c.Conn.ReadMessage()
if err != nil {
return
func (c *WsConn) keepAlive() {
go func() {
// 开启一个定时器
for {
<-time.After(time.Second * 54)
if c.Connected {
c.Conn.WriteMessage(websocket.TextMessage, []byte("ping"))
}
}
}()
for {
if !c.Connected && !c.Connecting {
c.Connecting = true
c.recreateWsConn()
// log.Println("WebSocket 连接意外断开,正在尝试重连...")
// return
}
// 降低检测频率,优化 CPU 占用情况
<-time.After(200 * time.Millisecond)
}
}
func (c *WsConn) messageReceiveHandler() {
// defer close(c.Done)
for {
if c.Connected {
_, msg, err := c.Conn.ReadMessage()
if err != nil {
// 读取信息出错,连接已经意外断开
// log.Println(err)
c.Connected = false
return
}
if string(msg) != "pong" {
c.MsgReceiveCh <- string(msg)
}
}
c.MsgReceiveCh <- string(msg)
}
}
@@ -39,20 +70,26 @@ func (c *WsConn) messageSendHandler() {
// 循环监听发送
select {
case <-c.Done:
log.Println("发送协程已经退出")
return
case t := <-c.MsgSendCh:
err := c.Conn.WriteMessage(websocket.TextMessage, []byte(t))
if err != nil {
log.Println("write:", err)
return
// log.Println(t)
if !c.Connected {
c.MsgReceiveCh <- `{"ip":"` + t + `", "asnumber":"API服务端异常"}`
} else {
err := c.Conn.WriteMessage(websocket.TextMessage, []byte(t))
if err != nil {
log.Println("write:", err)
return
}
}
// 来自终端的中断运行请求
case <-c.Interrupt:
// 向 websocket 发起关闭连接任务
err := c.Conn.WriteMessage(websocket.CloseMessage, websocket.FormatCloseMessage(websocket.CloseNormalClosure, ""))
if err != nil {
log.Println("write close:", err)
return
// log.Println("write close:", err)
os.Exit(1)
}
select {
// 等到了结果,直接退出
@@ -66,6 +103,27 @@ func (c *WsConn) messageSendHandler() {
}
}
func (c *WsConn) recreateWsConn() {
u := url.URL{Scheme: "wss", Host: "api.leo.moe", Path: "/v2/ipGeoWs"}
// log.Printf("connecting to %s", u.String())
ws, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
c.Conn = ws
if err != nil {
log.Println("dial:", err)
// <-time.After(time.Second * 1)
c.Connected = false
c.Connecting = false
return
} else {
c.Connected = true
}
c.Connecting = false
c.Done = make(chan struct{})
go c.messageReceiveHandler()
}
func createWsConn() *WsConn {
// 设置终端中断通道
interrupt := make(chan os.Signal, 1)
@@ -75,17 +133,28 @@ func createWsConn() *WsConn {
// log.Printf("connecting to %s", u.String())
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
wsconn = &WsConn{
Conn: c,
Connected: true,
Connecting: false,
MsgSendCh: make(chan string, 10),
MsgReceiveCh: make(chan string, 10),
}
if err != nil {
log.Fatal("dial:", err)
log.Println("dial:", err)
// <-time.After(time.Second * 1)
wsconn.Connected = false
wsconn.Done = make(chan struct{})
go wsconn.keepAlive()
go wsconn.messageSendHandler()
return wsconn
}
// defer c.Close()
// 将连接写入WsConn方便随时可取
wsconn = &WsConn{
Conn: c,
MsgSendCh: make(chan string),
MsgReceiveCh: make(chan string),
}
wsconn.Done = make(chan struct{})
go wsconn.keepAlive()
go wsconn.messageReceiveHandler()
go wsconn.messageSendHandler()
return wsconn