https://blog.kuoruan.com/110.html
经常有朋友抱怨 Kcptun 服务端安装繁琐,我花时间写了一个一键安装更新脚本。
参考了 @teddysun 的SS一键安装脚本,感谢 @静逸轩 提供的测试服务器。
理论上脚本支持:CentOS 6+、Ubuntu 12+和Debian 7+,但是由于测试不完全,可能存在安装失败的情况。如果你在使用过程中遇到任何问题,请反馈给我!反馈请加QQ群:43391448
脚本已更新到v16,请以前版本的朋友更新一下:
请先切换到 kcptun.sh 文件目录下运行
1
|
./kcptun.sh update
|
更新日志:
- 支持自定义实例启动用户;
- 支持删除实例;
- 各种优化;
- 完善提示信息
同时更新了 init 脚本
- 修复 CentOS 上找不到 Supervisor 执行文件的问题;
- 由于 Supervisor 的版本不同, 执行文件的目录可能不一样, 如果你出现无法安装或启动失败的问题, 请及时联系我
脚本的功能有:
- 一键安装,自动下载最新版本,支持所有参数配置;
- 一键更新,一键卸载,完全自动化;
- 支持用户手动输入版本安装;
- 支持更新配置,自动输出推荐参数;
- 使用 Supervisor 管理进程,更加便捷;
- 自动配置服务,并随开机自启;
- 脚本提示全中文,更快锁定问题。
用 Putty 下中文显示可能会出现乱码一片,请自行解决乱码问题;推荐使用 Xshell,手机上可以用 JuiceSSH,都是非常好用的 SSH 客户端。
使用脚本安装服务端
注意:在配置之前请确认一下你的加速地址,大部分不能加速都是由于加速地址配置错误。
鉴于大部分朋友是用来加速 Shadowsocks,下面以 Shadowsocks 为例,Shadowsocks 正确安装运行在当前服务器上。
首先找到你的 Shadowsocks 端口,比如我的 Shadowsocks 端口为 8388,然后在命令行输入以下命令:
1
|
netstat –nl | grep 8388
|
如果提示以上命令不存在,请输入:
1
|
ss –nl | grep 8388
|
注:将 8388 替换为你的 Shadowsocks 端口。
然后你会看到类似下面的输出(着重看显示为红色的部分):
情况一:
1
2
|
tcp6 0 0 :::8388 :::* LISTEN
udp6 0 0 :::8388 :::*
|
情况2:
1
2
|
tcp 0 0 127.0.0.1:8388 :::* LISTEN
udp 0 0 127.0.0.1:8388 :::*
|
情况3:
1
2
|
tcp 0 0 0.0.0.0:8388 :::* LISTEN
udp 0 0 0.0.0.0:8388 :::*
|
情况4(假如 10.10.10.10 是当前服务器IP):
1
2
|
tcp 0 0 10.10.10.10:8388 :::* LISTEN
udp 0 0 10.10.10.10:8388 :::*
|
若为情况1、情况2和情况3,那么你的加速地址可以为:加速 IP 127.0.0.1,加速端口 8388(你的 Shadowsocks 端口)
若为情况4,那么你的加速地址为:加速IP 10.10.10.10(你的服务器IP),加速端口8388(你的 Shadowsocks 端口)
使用方法:
1
2
3
|
wget —no–check–certificate https://raw.githubusercontent.com/kuoruan/kcptun_installer/master/kcptun.sh
chmod +x ./kcptun.sh
./kcptun.sh
|
1.设置 Kcptun 的服务端端口:
1
2
|
请输入 Kcptun Server 端口 [1–65535]:
(默认: 29900):
|
请输入一个未被占用的端口,Kcptun 运行时将使用此端口。
2.设置加速的 IP:
1
2
|
请输入需要加速的 IP [0.0.0.0 ~ 255.255.255.255]:
(默认: 127.0.0.1):
|
填入上面获取到的加速 IP。
3.设置需要加速的端口:
1
2
|
请输入需要加速的端口 [1–65535]:
(默认: 12948):
|
填入上面获取到的加速端口。
程序会检查当前是不是有程序占用着此端口,如果你的 Shadowsocks 没在运行,或者没有软件使用此端口,会弹出如下提示:
1
|
当前没有软件使用此端口, 确定加速此端口?(y/n)
|
如果你确认 Shadowsocks 运行时会使用此端口,那么输入 “y” 回车即可。
4.设置 Kcptun 密码:
1
2
|
请输入 Kcptun 密码:
(如果不想使用密码请留空):
|
这就是说,你可以为 Kcptun 单独设置一个密码,防止被他人恶意使用。这个密码和 SS 的密码没有半毛钱关系,请不要把它们混淆了。
再提一句,Kcptun 和 Shadowsocks 没有任何关系,请不要脑补它们之间存在任何联系,Kcptun 你可以理解为一款网络加速软件,只不过它是通过将 TCP 协议转换为 UDP 协议,然后再通过大量的发送数据包,浪费了带宽以换取网速的提升。它能加速所有以 TCP 协议传输数据的软件,不单单是 Shadowsocks。只是大家都用来……你懂的
回到上面的密码设置问题,如果你这里选择直接回车,也就是代表你不自定义密码。但是 Kcptun 有一个默认的密码,这个密码是: it‘s a secrect。
如果你这里选择不设置密码,那么客户端的参数里边就不需要加 –key 这一参数,但是 Kcptun 还是会用上面一个密码做默认值。
5.禁用压缩
1
2
|
是否禁用数据压缩?
(默认: 不禁用) [y/n]:
|
这个选项是什么意思呢?Kcptun 默认是启用压缩的,如果你这里设置为 y,也就是配置为 nocomp:true,那么就是禁用压缩。
许多朋友这里设置的是保持默认(启用压缩),而偏偏在软件之中设置为禁用压缩,当然就连不上咯。
其他配置项不用我说了,如果你了解它是干什么的,可以自定义配置。如果不知道,那么直接回车使用默认参数。
但是,使用默认参数,是有可能浪费大量流量的,你会发现你的流量像泄洪一样快速减少,你需要会调节参数,套用官方的说明
简易自我调优方法:
- 同时在两端逐步增大 client rcvwnd 和 server sndwnd ;
- 尝试下载,观察如果带宽利用率(服务器+客户端两端都要观察)接近物理带宽则停止,否则跳转到第一步。
任何事物都是有两面性的,选择了速度,就只有放弃流量。那么有没有既快得像火箭,燃料又省得像煤油灯的方法呢?呵呵!
各参数详细信息请查看:https://github.com/xtaci/kcptun
如果你用国内服务器安装,可能会出现文件下载失败。这是由于脚本会到 Github 下载文件,而 Github 的某些下载地址在国内你懂的……所以文件下载失败真不是我的锅。
如果安装成功,应该能看到如下输出信息:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
恭喜, Kcptun 服务端配置完毕!
正在获取当前安装的 Kcptun 版本...
服务器IP: 10.10.10.10
端口: 29900
加速地址: 127.0.0.1:8388
密码: 123456
加密方式 Crypt: salsa20
当前安装的 Kcptun 版本为: v20160922
推荐的客户端配置为:
{
“localaddr”: “:8388”,
“remoteaddr”: “10.10.10.10:29900”,
“key”: “123456”,
“crypt”: “salsa20”,
“mode”: “fast”,
“mtu”: 1350,
“sndwnd”: 1024,
“rcvwnd”: 1024,
“datashard”: 10,
“parityshard”: 3,
“dscp”: 0,
“conn”: 1,
“autoexpire”: 60,
“nocomp”: false
}
手机端参数可以使用:
–autoexpire 60 –key “123456” –crypt “salsa20”
其他参数请自行计算或设置, 详细信息可以查看: https://github.com/xtaci/kcptun
Kcptun 安装目录: /usr/share/kcptun
Kcptun 日志文件目录: /var/log/kcptun/
|
请将以上的提示信息复制保存下来,后面配置客户端会用到这些提示信息。
安装之后,Kcptun 服务交由 Supervisor 管理。
Supervisor 相关命令:
1
|
service supervisord {start|stop|restart|status}
|
Kcptun 相关命令:
1
|
supervisorctl {start|stop|restart|status} kcptun
|
Supervisor 启动的时候会同时启动 Kcptun,运行 kcptun 相关命令时先确保 Supervisor 已启动。
手动配置的方法请看这里:小内存福音,Kcptun Shadowsocks加速方案
客户端配置
1.先到下载一个启动 Kcptun 的工具。请注意,这只是用来启动 Kcptun 的工具,而不是 Kcptun 客户端。
https://github.com/dfdragon/kcptun_gclient/releases
2.然后下载服务端对应版本的 Kcptun(保存下来的提示信息里有):
1
|
当前安装的 Kcptun 版本为: v20160922
|
https://github.com/xtaci/kcptun/releases
32位系统下载:kcptun-windows-386-20160922.tar.gz
64位系统下载:kcptun-windows-amd64-20160922.tar.gz
注意看红字的版本号和服务端版本一致。然后将它们解压到一起:
1
2
3
|
kcptun_gclient.exe — Kcptun 启动工具
client_windows_amd64.exe — Kcptun 客户端程序
server_windows_amd64.exe — Kcptun 服务端程序
|
打开 Kcptun 启动工具,界面如下,请按序号操作。
1.直接导入配置文件
我们可以将推荐参数保存为文件,找到如下这部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
{
“localaddr”: “:8388”,
“remoteaddr”: “10.10.10.10:29900”,
“key”: “123456”,
“crypt”: “salsa20”,
“mode”: “fast”,
“mtu”: 1350,
“sndwnd”: 1024,
“rcvwnd”: 1024,
“datashard”: 10,
“parityshard”: 3,
“dscp”: 0,
“conn”: 1,
“autoexpire”: 60,
“nocomp”: false
}
|
新建一个记事本文件,文件名随意(比如 config.txt 或者 config.json),然后将上面大括号里的内容复制到文件里边(包括大括号),确保它是标准的 json 格式(可以到这里校验格式 http://www.json.cn/)。
然后勾选“使用配置文件”,选择你新建的文件即可,下面的参数区域直接留空,点击启动。
2.手动配置参数
手动配置的时候只需要看保存下来的提示信息上面一部分(有标红部分,非常显眼):
1
2
3
4
|
服务器IP: 10.10.10.10
端口: 29900
加速地址: 127.0.0.1:8388
加密方式 Crypt: salsa20
|
为了规避错误,遵循较少配置原则,在配置服务端时没有修改过的选项都不需要配置。配置完毕,点击启动。
几项说明:
- 本地监听端口,这个端口你可以随意设置,不是必须设置为 Shadowsocsk 的端口;
- KCP服务器地址为你的服务器IP地址,不是 127.0.0.1,端口为服务端 Kcptun 的端口;
- 如果你想使用IPv6协议,在填写服务器IP地址的时候需要用 [] 将IPv6地址括起来,如:[2000:0:0:0:0:0:0:1];
- 通信密钥是你配置的 Kcptun 密码,不是 Shadowsocks 的密码;在配置 Kcptun 的时候,不用管 Shadowsocks 的配置参数;
- 参数区配置的时候,只需要配置你修改过的部分就行了,其他部分都不用改,除非你了解每项参数的意义;
- 日志区非常重要,在排查问题的时候,这是必看部分;
- 右下角为 Kcptun 最低需求版本,更新服务端之后,也需要更新本地客户端,只需要替换客户端文件即可。
Shadowsocks 配置
在 Shadowsocks 客户端中添加一个选项,服务器IP固定填写 127.0.0.1,服务器端口填写 Kcptun 启动工具中配置的“本地监听端口”(即这里的 8388),密码和加密配置的是 Shadowsocks 的密码和加密。
基本原则,配置 Kcptun 的时候不用管 Shadowsocks 的参数,配置 Shadowsocks 的时候不用管 Kcptun 的参数,别把它们的配置参数搞混了。
将代理切换到新建的选项上,尝试访问。查看 Kcptun 启动工具中的日志区,会有大量的如下信息输出:
1
2
3
4
5
6
7
|
2016/09/24 11:57:15 stream closed
2016/09/24 11:57:15 stream opened
2016/09/24 11:57:17 stream closed
2016/09/24 11:57:17 stream closed
2016/09/24 11:57:18 stream closed
2016/09/24 11:57:19 stream opened
2016/09/24 11:57:19 stream closed
|
再用 shelll 看看服务端日志,如果有相同的信息输出,说明配置正确,并能正常使用。查看服务端日志使用:
1
|
./kcptun.sh log
|
Android 端请看这里:Shadowsocks-Android客户端上的KCP配置说明
OpenWrt 上使用:OpenWrt平台Kcptun Web管理界面
关于安装错误
很多朋友发邮件告诉我说出现了安装错误,错误日志如下:
1
2
3
4
5
6
|
Traceback (most recent call last):
File “/usr/bin/easy_install”, line 5, in <module>
from pkg_resources import load_entry_point
File “build/bdist.linux-x86_64/egg/pkg_resources.py”, line 2565, in <module>
File “build/bdist.linux-x86_64/egg/pkg_resources.py”, line 519, in resolve
pkg_resources.DistributionNotFound: distribute==0.6.10
|
而但凡出现这种错误的都是出于某种目的,自己升级了 python 版本,但是又没处理好,那么只需要手动做一个软链接:
1
|
ln –s /usr/local/python2.7/bin/easy_install /usr/bin/easy_install
|
注:其中的 /usr/local/python2.7 应该为你安装的新版 python 的路径。
其他说明
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
请使用: kcptun.sh <option>
可使用的参数 <option> 包括:
install 安装
uninstall 卸载
update 检查更新
manual 自定义 Kcptun 版本安装
help 查看脚本使用说明
add 添加一个实例, 多用户使用
reconfig <id> 重新配置实例
show <id> 显示实例详细配置
log <id> 显示实例日志
注: 上述参数中的 <id> 可选, 代表的是实例的序号
可使用 1, 2, 3 ... 分别对应 kcptun, kcptun2, kcptun3 ...
若不指定 <id>, 则默认为 1
Supervisor 命令:
service supervisord {start|stop|restart|status}
{启动|关闭|重启|查看状态}
Kcptun 相关命令:
supervisorctl {start|stop|restart|status} kcptun<id>
{启动|关闭|重启|查看状态}
|
kcptun.sh 源码
#!/bin/bash : <<-'EOF' Copyright 2016 Xingwang Liao <[email protected]> Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. EOF PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin export PATH ## 定义常量 SHELL_VERSION=16 CONFIG_VERSION=5 INIT_VERSION=3 CUR_DIR=`pwd` # 当前目录 KCPTUN_INSTALL_DIR=/usr/share/kcptun # kcptun 默认安装目录 KCPTUN_LOG_DIR=/var/log/kcptun # kcptun 日志目录 KCPTUN_RELEASES_URL="https://api.github.com/repos/xtaci/kcptun/releases" KCPTUN_TAGS_URL="https://github.com/xtaci/kcptun/tags" SHELL_VERSION_INFO_URL="https://raw.githubusercontent.com/kuoruan/kcptun_installer/master/kcptun.json" JQ_LINUX32="https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux32" JQ_LINUX64="https://github.com/stedolan/jq/releases/download/jq-1.5/jq-linux64" JQ=/usr/bin/jq ## 参数默认值 # associative array declare -Ar DEFAULT=( [LISTEN]=29900 [TARGET_IP]="127.0.0.1" [TARGET_PORT]=12948 [KEY]="it's a secrect" [CRYPT]="aes" [MODE]="fast" [MTU]=1350 [SNDWND]=1024 [RCVWND]=1024 [DATASHARD]=10 [PARITYSHARD]=3 [DSCP]=0 [NOCOMP]=false [NODELAY]=0 [INTERVAL]=20 [RESEND]=2 [NC]=1 [ACKNODELAY]=false [SOCKBUF]=4194304 [KEEPALIVE]=10 [RUN_USER]="root" ) declare -ar CRYPTS=( "aes" "aes-128" "aes-192" "salsa20" "blowfish" "twofish" "cast5" "3des" "tea" "xtea" "xor" "none" ) declare -ar MODES=( "normal" "fast" "fast2" "fast3" "manual" ) # 初始化参数 listen_port=${DEFAULT[LISTEN]} target_ip=${DEFAULT[TARGET_IP]} target_port=${DEFAULT[TARGET_PORT]} key=${DEFAULT[KEY]} crypt=${DEFAULT[CRYPT]} mode=${DEFAULT[MODE]} mtu=${DEFAULT[MTU]} sndwnd=${DEFAULT[SNDWND]} rcvwnd=${DEFAULT[RCVWND]} datashard=${DEFAULT[DATASHARD]} parityshard=${DEFAULT[PARITYSHARD]} dscp=${DEFAULT[DSCP]} nocomp=${DEFAULT[NOCOMP]} nodelay=${DEFAULT[NODELAY]} interval=${DEFAULT[INTERVAL]} resend=${DEFAULT[RESEND]} nc=${DEFAULT[NC]} acknodelay=${DEFAULT[ACKNODELAY]} sockbuf=${DEFAULT[SOCKBUF]} keepalive=${DEFAULT[KEEPALIVE]} run_user=${DEFAULT[RUN_USER]} current_id="" clear cat >&2 <<-'EOF' ######################################################### # Kcptun 服务端一键安装脚本 # # 该脚本支持 Kcptun 服务端的安装、更新、卸载及配置 # # 脚本作者: Index <[email protected]> # # 作者博客: https://blog.kuoruan.com/ # # Github: https://github.com/kuoruan/kcptun_installer # # QQ交流群: 43391448, 68133628 # ######################################################### EOF # 检查命令是否存在 command_exists() { command -v "$@" >/dev/null 2>&1 } # 检查变量是否为数字 is_number() { expr $1 + 1 >/dev/null 2>&1 } # 获取实例数量 get_instance_count() { ls -l /etc/supervisor/conf.d/ | grep "^-" | awk '{print $9}' | grep -cP "^kcptun\d*\.conf$" } # 获取当前配置文件 get_current_config_file() { echo "${KCPTUN_INSTALL_DIR}/server-config${current_id}.json" } # 获取当前日志文件 get_current_log_file() { echo "${KCPTUN_LOG_DIR}/server${current_id}.log" } # 获取当前实例的 Supervisor 配置文件 get_current_supervisor_config_file() { echo "/etc/supervisor/conf.d/kcptun"${current_id}".conf" } # 获取当前监听端口 get_current_listen_port() { local config_file="$(get_current_config_file)" if [ -f "$config_file" ]; then local listen=$($JQ -r ".listen" "$config_file") local current_listen_port=$(cut -d ':' -f2 <<< "$listen") if [ -n "$current_listen_port" ] && is_number $current_listen_port; then echo "$current_listen_port" fi fi } # 检查当前用户是否拥有管理员权限 permission_check() { if [ $EUID -ne 0 ]; then cat >&2 <<-'EOF' 权限错误, 请使用 root 用户运行此脚本! EOF exit 1 fi } # 检查并获取系统信息 linux_check() { if $(grep -qi "CentOS" /etc/issue) || $(grep -q "CentOS" /etc/*-release); then OS="CentOS" elif $(grep -qi "Ubuntu" /etc/issue) || $(grep -q "Ubuntu" /etc/*-release); then OS="Ubuntu" elif $(grep -qi "Debian" /etc/issue) || $(grep -q "Debian" /etc/*-release); then OS="Debian" else cat >&2 <<-'EOF' 本脚本仅支持 CentOS 6+, Debian 7+ 或者 Ubuntu 12+, 其他系统请向脚本作者反馈以寻求支持! EOF exit 1 fi OS_VSRSION=$(grep -oEh "[0-9]+" /etc/*-release | head -n 1) || { cat >&2 <<-'EOF' 无法获取操作系统版本, 请联系脚本作者反馈错误! EOF exit 1 } if [ "$OS" = "CentOS" -a $OS_VSRSION -lt 6 ]; then cat >&2 <<-'EOF' 暂不支持 CentOS 6 以下版本, 请升级系统或向脚本作者反馈以寻求支持! EOF exit 1 fi if [ "$OS" = "Ubuntu" -a $OS_VSRSION -lt 12 ]; then cat >&2 <<-'EOF' 暂不支持 Ubuntu 12 以下版本, 请升级系统或向脚本作者反馈以寻求支持! EOF exit 1 fi if [ "$OS" = "Debian" -a $OS_VSRSION -lt 7 ]; then cat >&2 <<-'EOF' 暂不支持 Debian 7 以下版本, 请升级系统或向脚本作者反馈以寻求支持! EOF exit 1 fi } # 获取系统位数 get_arch() { if $(uname -m | grep -q "64") || $(getconf LONG_BIT | grep -q "64"); then ARCH=64 SPRUCE_TYPE="linux-amd64" FILE_SUFFIX="linux_amd64" else ARCH=32 SPRUCE_TYPE="linux-386" FILE_SUFFIX="linux_386" fi } # 获取服务器的IP地址 get_server_ip() { SERVER_IP=$(ip addr | grep -Eo "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" | \ grep -Ev "^192\.168|^172\.1[6-9]\.|^172\.2[0-9]\.|^172\.3[0-2]\.|^10\.|^127\.|^255\.|^0\." | \ head -n 1) [ -z "$SERVER_IP" ] && SERVER_IP=$(wget -q -O - ipv4.icanhazip.com) } # 禁用 selinux disable_selinux() { if [ -s /etc/selinux/config ] && $(grep -q "SELINUX=enforcing" /etc/selinux/config); then sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config setenforce 0 fi } # 非正常退出 exit_with_error() { cat >&2 <<-'EOF' Kcptun 服务端安装或配置过程中出现错误! 希望你能记录下错误信息, 然后将错误信息发送给我 QQ群: 43391448, 68133628 邮箱: [email protected] 博客: https://blog.kuoruan.com/ EOF exit 1 } # 任意键继续 any_key_to_continue() { SAVEDSTTY=`stty -g` stty -echo stty cbreak dd if=/dev/tty bs=1 count=1 2> /dev/null stty -raw stty echo stty $SAVEDSTTY } # 检查是否已经安装 installed_check() { if [ -s /etc/supervisord.conf ]; then cat >&2 <<-EOF 检测到你曾经通过其他方式安装过 Supervisor , 这会和本脚本安装的 Supervisor 产生冲突 推荐你备份当前 Supervisor 配置后卸载原有版本 已安装的 Supervisor 配置文件路径为: /etc/supervisord.conf 通过本脚本安装的 Supervisor 配置文件路径为: /etc/supervisor/supervisord.conf 你可以使用以下命令来卸载清理原有版本: mv /etc/supervisord.conf /etc/supervisord.conf.bak $([ "${OS}" = "CentOS" ] && echo -n "yum remove supervisor" || echo -n "apt-get remove --purge supervisor") 然后, 请重新运行脚本安装 EOF exit_with_error fi if [ -d /etc/supervisor/conf.d/ ]; then local instance_count=$(get_instance_count) if [ $instance_count -gt 0 ] && [ -d "$KCPTUN_INSTALL_DIR" ]; then cat >&2 <<-EOF 检测到你已安装 Kcptun 服务端, 已配置的实例个数为 ${instance_count} 个 EOF while : do cat >&2 <<-'EOF' 请选择你希望的操作: (1) 覆盖安装 (2) 重新配置 (3) 添加实例(多用户) (4) 检查更新 (5) 查看配置 (6) 查看日志输出 (7) 自定义版本安装 (8) 删除实例 (9) 完全卸载 (10) 退出脚本 EOF read -p "(默认: 1) 请选择 [1~10]: " sel echo [ -z "$sel" ] && sel=1 case $sel in 1) echo "开始覆盖安装 Kcptun 服务端..." return 0 ;; 2) select_instance reconfig_instance ;; 3) add_instance ;; 4) check_update ;; 5) select_instance show_instance_config ;; 6) select_instance show_instance_log ;; 7) manual_install ;; 8) select_instance del_instance ;; 9) uninstall_kcptun ;; 10) ;; *) echo "输入有误, 请输入有效数字 1~10!" continue ;; esac exit 0 done fi fi } # 检测端口是否被占用 check_port() { [ $# -lt 1 ] && return 1 local port=$1 if command_exists netstat; then return $(netstat -ntul | grep -qE "[0-9:]:${port}\s") elif command_exists ss; then return $(ss -ntul | grep -qE "[0-9:]:${port}\s") else return 1 fi } # 设置 Kcptun 端口 set_listen_port() { while : do cat >&2 <<-'EOF' 请输入 Kcptun 服务端运行端口 [1~65535] EOF read -p "(默认: ${DEFAULT[LISTEN]}): " input echo if [ -n "$input" ]; then if is_number $input && [ $input -ge 1 -a $input -le 65535 ]; then listen_port="$input" else echo "输入有误, 请输入 1~65535 之间的数字!" continue fi fi current_listen_port=$(get_current_listen_port) if check_port $listen_port && [ "$listen_port" != "$current_listen_port" ]; then echo "端口已被占用, 请重新输入!" continue fi cat >&2 <<-EOF --------------------------- 端口 = ${listen_port} --------------------------- EOF break done } # 禁用 IPv6 set_disable_ipv6() { while : do cat >&2 <<-'EOF' 是否禁用 IPv6? EOF read -p "(默认: 不禁用) [y/n]: " yn echo if [ -n "$yn" ]; then case ${yn:0:1} in y|Y) listen_addr=$SERVER_IP ;; n|N) unset listen_addr ;; *) echo "输入有误, 请重新输入!" continue ;; esac fi cat >&2 <<-EOF --------------------------- $([ -z "${listen_addr}" ] && echo "不禁用IPv6" || echo "禁用IPv6") --------------------------- EOF break done } # 设置加速的ip地址 set_target_ip() { while : do cat >&2 <<-'EOF' 请输入需要加速的 IP [0.0.0.0 ~ 255.255.255.255] EOF read -p "(默认: ${DEFAULT[TARGET_IP]}): " input echo if [ -n "$input" ]; then grep -qE '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$' <<< "$input" if [ $? -ne 0 ]; then echo "IP 地址格式有误, 请重新输入!" continue fi target_ip="$input" fi cat >&2 <<-EOF --------------------------- 加速 IP = ${target_ip} --------------------------- EOF break done } # 设置加速的端口 set_target_port() { while : do cat >&2 <<-'EOF' 请输入需要加速的端口 [1~65535] EOF read -p "(默认: ${DEFAULT[TARGET_PORT]}): " input echo if [ -n "$input" ]; then if is_number $input && [ $input -ge 1 -a $input -le 65535 ]; then if [ $input -eq $listen_port ]; then echo "加速端口不能和 Kcptun 端口一致!" continue fi target_port=$input else echo "输入有误, 请输入 1~65535 之间的数字!" continue fi fi if [ "$target_ip" = "${DEFAULT[TARGET_IP]}" ]; then if ! check_port $target_port; then read -p "当前没有软件使用此端口, 确定加速此端口? [y/n]: " yn [ -z "$yn" ] && yn="y" case ${yn:0:1} in y|Y) ;; *) continue ;; esac fi fi cat >&2 <<-EOF --------------------------- 加速端口 = ${target_port} --------------------------- EOF break done } # 设置 Kcptun 密码 set_key() { cat >&2 <<-'EOF' 请输入 Kcptun 密码 EOF read -p "(默认密码: ${DEFAULT[KEY]}): " input echo [ -n "$input" ] && key="$input" cat >&2 <<-EOF --------------------------- 密码 = ${key} --------------------------- EOF } # 设置加密方式 set_crypt() { while : do cat >&2 <<-'EOF' 请选择加密方式(crypt): EOF for ((i=0; i<${#CRYPTS[@]}; i++)); do echo "($(($i + 1))) ${CRYPTS[$i]}" done read -p "(默认: ${DEFAULT[CRYPT]}) 请选择 [1~$i]: " sel echo if [ -n "$sel" ]; then if is_number $sel && [ $sel -ge 1 -a $sel -le $i ]; then crypt=${CRYPTS[$(($sel - 1))]} else echo "请输入有效数字 1~$i!" continue fi fi cat >&2 <<-EOF ----------------------------- 加密方式 = ${crypt} ----------------------------- EOF break done } # 设置加速模式 set_mode() { while : do cat >&2 <<-'EOF' 请选择加速模式(mode): EOF for ((i=0; i<${#MODES[@]}; i++)); do echo "($(($i + 1))) ${MODES[$i]}" done read -p "(默认: ${DEFAULT[MODE]}) 请选择 [1~$i]: " sel echo if [ -n "$sel" ]; then if is_number $sel && [ $sel -ge 1 -a $sel -le $i ]; then mode=${MODES[$(($sel - 1))]} else echo "请输入有效数字 1~$i!" continue fi fi cat >&2 <<-EOF --------------------------- 加速模式 = ${mode} --------------------------- EOF break done [ "$mode" = "manual" ] && set_manual_parameters } # 设置手动挡参数 set_manual_parameters() { while : do cat >&2 <<-'EOF' 请设置手动挡参数(预设值或手动设置): (1) 策略1: 通过超时重传+快速重传, 响应速度优先 (最大化响应时间, 适用于网页访问) (2) 策略2-1: 仅仅通过超时重传, 带宽效率优先 (有效载比优先, 适用于视频观看) (3) 策略2-2: 同上, 与 策略2-1 参数略不相同 (4) 策略3: 尽可能通过 FEC 纠删, 最大化传输速度 (较为中庸, 兼顾网页和视频) (5) 自定义手动挡参数 EOF read -p "(默认: 5) 请选择 [1~5]: " sel echo [ -z "$sel" ] && sel=5 case $sel in 1) nodelay=1 interval=20 resend=2 nc=1 ;; 2) nodelay=1 interval=40 resend=0 nc=1 ;; 3) nodelay=0 interval=20 resend=0 nc=1 ;; 4) nodelay=0 interval=40 resend=0 nc=1 datashard=5 parityshard=2 ;; 5) set_manual_detail_parameters break ;; *) echo "请输入有效数字 1~5!" continue ;; esac cat >&2 <<-EOF --------------------------- nodelay = ${nodelay} interval = ${interval} resend = ${resend} nc = ${nc} datashard = ${datashard} parityshard = ${parityshard} --------------------------- EOF break done } # 设置手动模式详细参数 set_manual_detail_parameters() { cat >&2 <<-'EOF' 开始配置手动挡参数... EOF set_nodelay set_interval set_resend set_nc } set_nodelay() { while : do cat >&2 <<-'EOF' 是否启用 nodelay 模式? EOF read -p "(默认: 不启用) [y/n]: " yn echo if [ -n "$yn" ]; then case ${yn:0:1} in y|Y) nodelay=1 ;; n|N) nodelay=0 ;; * ) echo "输入有误, 请重新输入!" continue ;; esac fi cat >&2 <<-EOF --------------------------- nodelay = ${nodelay} --------------------------- EOF break done } set_interval() { while : do cat >&2 <<-'EOF' 请设置协议内部工作的 interval EOF read -p "(单位: ms, 默认: ${DEFAULT[INTERVAL]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -le 0 ]; then echo "输入有误, 请输入大于0的数字!" continue fi interval=$input fi cat >&2 <<-EOF --------------------------- interval = ${interval} --------------------------- EOF break done } set_resend() { while : do cat >&2 <<-'EOF' 是否启用快速重传模式(resend)? (1) 不启用 (2) 启用 (3) 2次ACK跨越重传 EOF read -p "(默认: 3) 请选择 [1~3]: " sel echo if [ -n "$sel" ]; then case $sel in 1) resend=0 ;; 2) resend=1 ;; 3) resend=2 ;; *) cat >&2 <<-'EOF' 请输入有效数字 1~3! EOF continue ;; esac fi cat >&2 <<-EOF --------------------------- resend = ${resend} --------------------------- EOF break done } set_nc() { while : do cat >&2 <<-'EOF' 是否关闭流控(nc)? EOF read -p "(默认: 关闭) [y/n]: " yn echo if [ -n "$yn" ]; then case ${yn:0:1} in y|Y) nc=1 ;; n|N) nc=0 ;; *) echo "输入有误, 请重新输入!" continue ;; esac fi cat >&2 <<-EOF --------------------------- nc = ${nc} --------------------------- EOF break done } set_mtu() { while : do cat >&2 <<-'EOF' 请设置 UDP 数据包的 MTU (最大传输单元)值 EOF read -p "(默认: ${DEFAULT[MTU]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -le 0 ]; then echo "输入有误, 请输入大于0的数字!" continue fi mtu=$input fi cat >&2 <<-EOF --------------------------- MTU = ${mtu} --------------------------- EOF break done } set_sndwnd() { while : do cat >&2 <<-'EOF' 请设置发送窗口大小(sndwnd) EOF read -p "(数据包数量, 默认: ${DEFAULT[SNDWND]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -le 0 ]; then echo "输入有误, 请输入大于0的数字!" continue fi sndwnd=$input fi cat >&2 <<-EOF --------------------------- sndwnd = ${sndwnd} --------------------------- EOF break done } set_rcvwnd() { while : do cat >&2 <<-'EOF' 请设置接收窗口大小(rcvwnd) EOF read -p "(数据包数量, 默认: ${DEFAULT[RCVWND]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -le 0 ]; then echo "输入有误, 请输入大于0的数字!" continue fi rcvwnd=$input fi cat >&2 <<-EOF --------------------------- rcvwnd = ${rcvwnd} --------------------------- EOF break done } set_datashard() { while : do cat >&2 <<-'EOF' 请设置前向纠错 datashard EOF read -p "(默认: ${DEFAULT[DATASHARD]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -lt 0 ]; then echo "输入有误, 请输入大于等于0的数字!" continue fi datashard=$input fi cat >&2 <<-EOF --------------------------- datashard = ${datashard} --------------------------- EOF break done } set_parityshard() { while : do cat >&2 <<-'EOF' 请设置前向纠错 parityshard EOF read -p "(默认: ${DEFAULT[PARITYSHARD]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -lt 0 ]; then echo "输入有误, 请输入大于等于0的数字!" continue fi parityshard=$input fi cat >&2 <<-EOF --------------------------- parityshard = ${parityshard} --------------------------- EOF break done } set_dscp() { while : do cat >&2 <<-'EOF' 请设置差分服务代码点(DSCP) EOF read -p "(默认: ${DEFAULT[DSCP]}): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -lt 0 ]; then echo "输入有误, 请输入大于等于0的数字!" continue fi dscp=$input fi cat >&2 <<-EOF --------------------------- DSCP = ${dscp} --------------------------- EOF break done } set_nocomp() { while : do cat >&2 <<-'EOF' 是否禁用数据压缩? EOF read -p "(默认: 不禁用) [y/n]: " yn echo if [ -n "$yn" ]; then case ${yn:0:1} in y|Y) nocomp=true ;; n|N) nocomp=false ;; *) echo "输入有误, 请重新输入!" continue ;; esac fi cat >&2 <<-EOF --------------------------- nocomp = ${nocomp} --------------------------- EOF break done } # 设置隐藏参数 set_hidden_parameters() { cat >&2 <<-'EOF' 开始配置隐藏参数... EOF set_acknodelay set_sockbuf set_keepalive } set_acknodelay() { while : do cat >&2 <<-'EOF' 是否启用 acknodelay 模式? EOF read -p "(默认: 不启用) [y/n]: " yn echo if [ -n "$yn" ]; then case ${yn:0:1} in y|Y) acknodelay="true" ;; n|N) acknodelay="false" ;; *) echo "输入有误, 请重新输入!" continue ;; esac fi cat >&2 <<-EOF --------------------------- acknodelay = ${acknodelay} --------------------------- EOF break done } set_sockbuf() { while : do cat >&2 <<-'EOF' 请设置 UDP 收发缓冲区大小(sockbuf) EOF read -p "(单位: MB, 默认: $((${DEFAULT[SOCKBUF]} / 1024 / 1024))): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -le 0 ]; then echo "输入有误, 请输入大于0的数字!" continue fi sockbuf=$(($input * 1024 * 1024)) fi cat >&2 <<-EOF --------------------------- sockbuf = ${sockbuf} --------------------------- EOF break done } set_keepalive() { while : do cat >&2 <<-'EOF' 请设置 Keepalive 的间隔时间 EOF read -p "(单位: s, 默认值: ${DEFAULT[KEEPALIVE]}, 前值: 5): " input echo if [ -n "$input" ]; then if ! is_number $input || [ $input -le 0 ]; then echo "输入有误, 请输入大于0的数字!" continue fi keepalive=$input fi cat >&2 <<-EOF --------------------------- keepalive = ${keepalive} --------------------------- EOF break done } # 设置运行用户 set_run_user() { while : do cat >&2 <<-'EOF' 请设置以哪个用户运行当前 Kcptun 实例 EOF read -p "(默认: ${DEFAULT[RUN_USER]}): " input echo if [ -n "$input" ]; then if ! grep -q "^${input}:" /etc/passwd; then echo "未发现该用户, 请先使用 useradd 手动创建用户 ${input}" continue fi if [ "$input" != "${DEFAULT[RUN_USER]}" -a "$listen_port" -lt 1024 ]; then cat >&2 <<-EOF 你设置的 Kcptun 监听端口为 ${listen_port} 由于 linux 系统限制, 仅有 root 用户能启用小于 1024 的端口 脚本已自动将用户设置为 root EOF input=${DEFAULT[RUN_USER]} fi run_user=$input fi cat >&2 <<-EOF --------------------------- 用户 = ${run_user} --------------------------- EOF break done } # 设置参数 set_kcptun_config() { echo echo "开始配置参数..." set_listen_port set_disable_ipv6 set_target_ip set_target_port set_key set_crypt set_mode set_mtu set_sndwnd set_rcvwnd set_datashard set_parityshard set_dscp set_nocomp set_run_user while : do cat >&2 <<-'EOF' 是否调整隐藏参数? EOF read -p "(默认: 否) [y/n]: " yn echo if [ -n "$yn" ]; then case ${yn:0:1} in y|Y) set_hidden_parameters ;; n|N) ;; *) echo "输入有误, 请重新输入!" continue ;; esac fi break done echo "配置完成, 按任意键继续...或者 Ctrl+C 取消" any_key_to_continue } # 安装需要的依赖软件 install_dependence() { cat >&2 <<-'EOF' 正在安装依赖软件... EOF if [ "$OS" = "CentOS" ]; then yum makecache yum update -y ca-certificates yum install -y curl wget python-setuptools tar else apt-get -y update apt-get -y install curl wget python-setuptools tar || { cat >&2 <<-'EOF' 安装依赖软件包失败! EOF exit_with_error } fi [ ! -x "$JQ" ] && install_jq if ! easy_install supervisor; then cat >&2 <<-'EOF' 安装 Supervisor 失败! EOF exit_with_error fi [ -d /etc/supervisor/conf.d ] || mkdir -p /etc/supervisor/conf.d if [ ! -s /etc/supervisor/supervisord.conf ]; then if ! command_exists echo_supervisord_conf; then cat >&2 <<-'EOF' 未找到 echo_supervisord_conf, 无法自动创建 Supervisor 配置文件! 可能是当前安装的 supervisor 版本过低 EOF exit_with_error else if ! echo_supervisord_conf > /etc/supervisor/supervisord.conf; then cat >&2 <<-'EOF' 创建 Supervisor 配置文件失败! EOF exit_with_error fi fi fi } # 安装 Json 解析工具 JQ install_jq() { cat >&2 <<-'EOF' 正在安装 Json 解析工具 jq ... EOF local jq_url if [ "$ARCH" = '64' ]; then jq_url="$JQ_LINUX64" else jq_url="$JQ_LINUX32" fi if ! wget --no-check-certificate -O "$JQ" "$jq_url"; then cat >&2 <<-EOF 自动安装 jq 失败, 请手动下载安装! wget --no-check-certificate -O ${JQ} ${jq_url} chmod a+x ${JQ} 安装完成之后请重新运行脚本 EOF exit_with_error fi chmod a+x "$JQ" if [ ! -x "$JQ" ]; then cat >&2 <<-EOF 无法设置执行权限, 请手动设置: chmod a+x ${JQ} 设置好之后请重新运行脚本 EOF exit_with_error fi } # 通过网络获取需要的信息 get_kcptun_version_info() { cat >&2 <<-'EOF' 正在获取网络信息... EOF [ ! -x "$JQ" ] && install_jq local request_version=$1 local kcptun_release_content if [ -n "$request_version" ]; then kcptun_release_content=$(curl --silent --insecure --fail $KCPTUN_RELEASES_URL | $JQ -r ".[] | select(.tag_name == \"${request_version}\")") else kcptun_release_content=$(curl --silent --insecure --fail $KCPTUN_RELEASES_URL | $JQ -r ".[0]") fi if [ -n "$kcptun_release_content" ]; then kcptun_release_name=$($JQ -r ".name" <<< "$kcptun_release_content") kcptun_release_tag_name=$($JQ -r ".tag_name" <<< "$kcptun_release_content") kcptun_release_prerelease=$($JQ -r ".prerelease" <<< "$kcptun_release_content") kcptun_release_html_url=$($JQ -r ".html_url" <<< "$kcptun_release_content") kcptun_release_download_url=$($JQ -r ".assets[] | select(.name | contains(\"$SPRUCE_TYPE\")) | .browser_download_url" <<< "$kcptun_release_content" | head -n 1) || { cat >&2 <<-'EOF' 获取 Kcptun 下载地址失败, 请重试... EOF exit_with_error } else if [ -n "$request_version" ]; then return 2 else cat >&2 <<-'EOF' 获取 Kcptun 版本信息失败, 请检查你的网络连接! EOF exit_with_error fi fi } # 获取shell脚本更新 get_shell_version_info() { local shell_version_content=$(curl --silent --insecure --fail $SHELL_VERSION_INFO_URL) if [ $? -eq 0 ]; then new_shell_version=$($JQ -r ".shell_version" <<< "$shell_version_content" | grep -oE "[0-9]+" ) new_config_version=$($JQ -r ".config_version" <<< "$shell_version_content" | grep -oE "[0-9]+" ) new_init_version=$($JQ -r ".init_version" <<< "$shell_version_content" | grep -oE "[0-9]+") shell_change_log=$($JQ -r ".change_log" <<< "$shell_version_content") config_change_log=$($JQ -r ".config_change_log" <<< "$shell_version_content") init_change_log=$($JQ -r ".init_change_log" <<< "$shell_version_content") new_shell_url=$($JQ -r ".shell_url" <<< "$shell_version_content") else new_shell_version=0 new_config_version=0 new_init_version=0 fi } # 下载文件 download_file(){ cat >&2 <<-'EOF' 开始下载文件... EOF cd "$CUR_DIR" if [ `pwd` != "$CUR_DIR" ]; then cat >&2 <<-'EOF' 切换目录失败... EOF exit_with_error fi kcptun_file_name="kcptun-${kcptun_release_tag_name}.tar.gz" if [ -f "$kcptun_file_name" ] && tar -tf "$kcptun_file_name" &>/dev/null; then cat >&2 <<-'EOF' 已找到 Kcptun 文件压缩包, 跳过下载... EOF return 0 fi if ! wget --no-check-certificate -c -t 3 -O "$kcptun_file_name" "$kcptun_release_download_url"; then cat >&2 <<-EOF 下载 Kcptun 文件压缩包失败, 你可以尝试手动下载文件: 1. 下载 ${kcptun_release_download_url} 2. 将文件重命名为 ${file_name} 3. 上传文件至脚本当前目录 ${CUR_DIR} 4. 重新运行脚本开始安装 EOF exit_with_error fi } # 解压文件 unpack_file() { cat >&2 <<-'EOF' 开始解压文件... EOF cd "$CUR_DIR" [ -d "$KCPTUN_INSTALL_DIR" ] || mkdir -p "$KCPTUN_INSTALL_DIR" tar -zxf "$kcptun_file_name" -C "$KCPTUN_INSTALL_DIR" local kcptun_server_exec="$KCPTUN_INSTALL_DIR"/server_"$FILE_SUFFIX" if [ -f "$kcptun_server_exec" ]; then if ! chmod a+x "$kcptun_server_exec"; then cat >&2 <<-'EOF' 无法设置执行权限... EOF exit_with_error fi else cat >&2 <<-'EOF' 未在解压文件中找到 Kcptun 服务端执行文件, 请重试! EOF exit_with_error fi } # 创建配置文件 config_kcptun() { cat >&2 <<-'EOF' 正在写入配置... EOF if [ -f /etc/supervisor/supervisord.conf ]; then # sed -i 's/^\[include\]$/&\nfiles = \/etc\/supervisor\/conf.d\/\*\.conf/; \ # t;$a [include]\nfiles = /etc/supervisor/conf.d/*.conf' /etc/supervisor/supervisord.conf $(grep -q "^files\s*=\s*\/etc\/supervisor\/conf\.d\/\*\.conf$" /etc/supervisor/supervisord.conf) || { if grep -q "^\[include\]$" /etc/supervisor/supervisord.conf; then sed -i '/^\[include\]$/a files = \/etc\/supervisor\/conf.d\/\*\.conf' /etc/supervisor/supervisord.conf else sed -i '$a [include]\nfiles = /etc/supervisor/conf.d/*.conf' /etc/supervisor/supervisord.conf fi } # 创建文件夹 [ -d "$KCPTUN_INSTALL_DIR" ] || mkdir -p "$KCPTUN_INSTALL_DIR" local config_file="$(get_current_config_file)" local supervisor_config_file="$(get_current_supervisor_config_file)" cat > "$config_file"<<-EOF { "listen": "${listen_addr}:${listen_port}", "target": "${target_ip}:${target_port}", "key": "${key}", "crypt": "${crypt}", "mode": "${mode}", "mtu": ${mtu}, "sndwnd": ${rcvwnd}, "rcvwnd": ${sndwnd}, "datashard": ${datashard}, "parityshard": ${parityshard}, "dscp": ${dscp}, "nocomp": ${nocomp}, "acknodelay": ${acknodelay}, "nodelay": ${nodelay}, "interval": ${interval}, "resend": ${resend}, "nc": ${nc}, "sockbuf": ${sockbuf}, "keepalive": ${keepalive} } EOF cat > "$supervisor_config_file"<<-EOF [program:kcptun${current_id}] user=${run_user} directory=${KCPTUN_INSTALL_DIR} command=${KCPTUN_INSTALL_DIR}/server_${FILE_SUFFIX} -c "${config_file}" process_name=%(program_name)s autostart=true redirect_stderr=true stdout_logfile=$(get_current_log_file) stdout_logfile_maxbytes=1MB stdout_logfile_backups=0 EOF else cat >&2 <<-'EOF' 未找到 Supervisor 配置文件! 请先使用脚本安装 Kcptun 服务端 EOF exit_with_error fi } # 下载服务脚本 downlod_init_script() { cat >&2 <<-'EOF' 开始下载服务脚本... EOF local init_file_url if [ "$OS" = "CentOS" ]; then init_file_url="https://raw.githubusercontent.com/kuoruan/kcptun_installer/master/redhat.init" else init_file_url="https://raw.githubusercontent.com/kuoruan/kcptun_installer/master/ubuntu.init" fi if ! wget --no-check-certificate -O /etc/init.d/supervisord "$init_file_url"; then cat >&2 <<-'EOF' 下载 Supervisor init 脚本失败! EOF exit_with_error fi if ! chmod a+x /etc/init.d/supervisord; then cat >&2 <<-'EOF' 设置执行权限失败... EOF exit_with_error fi } # 安装服务 install_service() { cat >&2 <<-'EOF' 正在配置系统服务... EOF if [ "$OS" = "CentOS" ]; then chkconfig --add supervisord chkconfig supervisord on else update-rc.d -f supervisord defaults fi restart_supervisor } # 设置防火墙 config_firewall() { cat >&2 <<-'EOF' 开始设置防火墙... EOF if command_exists iptables; then if service iptables status >/dev/null 2>&1; then if [ -n "$current_listen_port" ]; then iptables -D INPUT -p udp --dport ${current_listen_port} -j ACCEPT >/dev/null 2>&1 fi iptables -nL | grep "$listen_port" | grep -q "ACCEPT" if [ $? -ne 0 ]; then iptables -I INPUT -p udp --dport ${listen_port} -j ACCEPT service iptables save service iptables restart fi cat >&2 <<-EOF UDP 端口 ${listen_port} 已开放! EOF else cat >&2 <<-EOF iptables 未启动或未配置 如有必要, 请手动添加端口 ${listen_port} 的防火墙规则: iptables -I INPUT -p udp --dport ${listen_port} -j ACCEPT service iptables save service iptables restart EOF fi else cat >&2 <<-EOF iptables 未安装, 跳过配置 EOF fi if command_exists firewall-cmd; then if systemctl status firewalld >/dev/null 2>&1; then if [ -n "$current_listen_port" ]; then firewall-cmd --zone=public --remove-port=${current_listen_port}/udp >/dev/null 2>&1 fi firewall-cmd --zone=public --query-port=${listen_port}/udp >/dev/null 2>&1 if [ $? -ne 0 ]; then firewall-cmd --permanent --zone=public --add-port=${listen_port}/udp firewall-cmd --reload fi cat >&2 <<-EOF UDP 端口 ${listen_port} 已开放! EOF else cat >&2 <<-EOF firewalld 未启动或未配置 如果有必要, 请手动添加端口 ${listen_port} 的防火墙规则: firewall-cmd --permanent --zone=public --add-port=${listen_port}/udp firewall-cmd --reload EOF fi else cat >&2 <<-'EOF' firewalld 未安装, 跳过配置 EOF fi } # 安装清理 install_cleanup() { cat >&2 <<-'EOF' 正在清理无用文件... EOF cd "$CUR_DIR" rm -f "$kcptun_file_name" rm -f "$KCPTUN_INSTALL_DIR"/client_"$FILE_SUFFIX" } # 显示配置信息 show_config_info() { echo echo -e "服务器IP: \033[41;37m ${SERVER_IP} \033[0m" echo -e "端口: \033[41;37m ${listen_port} \033[0m" echo -e "加速地址: ${target_ip}:${target_port}" [ "$key" != "${DEFAULT[KEY]}" ] && echo -e "密码: \033[41;37m ${key} \033[0m" [ "$crypt" != "${DEFAULT[CRYPT]}" ] && echo -e "crypt: \033[41;37m ${crypt} \033[0m" [ "$mode" != "${DEFAULT[MODE]}" ] && echo -e "mode: \033[41;37m ${mode} \033[0m" [ "$mtu" != "${DEFAULT[MTU]}" ] && echo -e "mtu: \033[41;37m ${mtu} \033[0m" [ "$sndwnd" != "${DEFAULT[SNDWND]}" ] && echo -e "sndwnd: \033[41;37m ${sndwnd} \033[0m" [ "$rcvwnd" != "${DEFAULT[RCVWND]}" ] && echo -e "rcvwnd: \033[41;37m ${rcvwnd} \033[0m" [ "$datashard" != "${DEFAULT[DATASHARD]}" ] && echo -e "datashard: \033[41;37m ${datashard} \033[0m" [ "$parityshard" != "${DEFAULT[PARITYSHARD]}" ] && echo -e "parityshard: \033[41;37m ${parityshard} \033[0m" [ "$dscp" != "${DEFAULT[DSCP]}" ] && echo -e "dscp: \033[41;37m ${dscp} \033[0m" [ "$nocomp" != "${DEFAULT[NOCOMP]}" ] && echo -e "nocomp: \033[41;37m ${nocomp} \033[0m" [ "$nodelay" != "${DEFAULT[NODELAY]}" ] && echo -e "nodelay: \033[41;37m ${nodelay} \033[0m" [ "$interval" != "${DEFAULT[INTERVAL]}" ] && echo -e "interval: \033[41;37m ${interval} \033[0m" [ "$resend" != "${DEFAULT[RESEND]}" ] && echo -e "resend: \033[41;37m ${resend} \033[0m" [ "$nc" != "${DEFAULT[NC]}" ] && echo -e "nc: \033[41;37m ${nc} \033[0m" [ "$acknodelay" != "${DEFAULT[ACKNODELAY]}" ] && echo -e "acknodelay: \033[41;37m ${acknodelay} \033[0m" [ "$sockbuf" != "${DEFAULT[SOCKBUF]}" ] && echo -e "sockbuf: \033[41;37m ${sockbuf} \033[0m" [ "$keepalive" != "${DEFAULT[KEEPALIVE]}" ] && echo -e "keepalive: \033[41;37m ${keepalive} \033[0m" } # 处理手机端参数 generate_mobile_args() { kcptun_mobile_args="-autoexpire 60" [ "$key" != "${DEFAULT[KEY]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -key \"${key}\"" [ "$crypt" != "${DEFAULT[CRYPT]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -crypt \"${crypt}\"" [ "$mode" != "${DEFAULT[MODE]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -mode \"${mode}\"" [ "$mtu" != "${DEFAULT[MTU]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -mtu ${mtu}" [ "$datashard" != "${DEFAULT[DATASHARD]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -datashard ${datashard}" [ "$parityshard" != "${DEFAULT[PARITYSHARD]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -parityshard ${parityshard}" [ "$dscp" != "${DEFAULT[DSCP]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -dscp ${dscp}" [ "$nocomp" != "${DEFAULT[NOCOMP]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -nocomp" [ "$nodelay" != "${DEFAULT[NODELAY]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -nodelay ${nodelay}" [ "$interval" != "${DEFAULT[INTERVAL]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -interval ${interval}" [ "$resend" != "${DEFAULT[RESEND]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -resend ${resend}" [ "$nc" != "${DEFAULT[NC]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -nc ${nc}" [ "$acknodelay" != "${DEFAULT[ACKNODELAY]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -acknodelay" [ "$sockbuf" != "${DEFAULT[SOCKBUF]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -sockbuf ${sockbuf}" [ "$keepalive" != "${DEFAULT[KEEPALIVE]}" ] && kcptun_mobile_args="${kcptun_mobile_args} -keepalive ${keepalive}" } show_recommend_config() { cat >&2 <<-EOF 可使用的客户端配置文件为: { "localaddr": ":${target_port}", "remoteaddr": "${SERVER_IP}:${listen_port}", "key": "${key}", "crypt": "${crypt}", "mode": "${mode}", "conn": 1, "autoexpire": 60, "mtu": ${mtu}, "sndwnd": ${rcvwnd}, "rcvwnd": ${sndwnd}, "datashard": ${datashard}, "parityshard": ${parityshard}, "dscp": ${dscp}, "nocomp": ${nocomp}, "acknodelay": ${acknodelay}, "nodelay": ${nodelay}, "interval": ${interval}, "resend": ${resend}, "nc": ${nc}, "sockbuf": ${sockbuf}, "keepalive": ${keepalive} } 手机端参数可以使用: ${kcptun_mobile_args} 各参数详细说明可以查看: https://github.com/xtaci/kcptun EOF } # 显示安装信息 show_installed_info() { show_config_info show_installed_version show_recommend_config cat >&2 <<-EOF Kcptun 安装目录: ${KCPTUN_INSTALL_DIR} Kcptun 日志目录: ${KCPTUN_LOG_DIR} 已将 Supervisor 加入开机自启, Kcptun 服务端会随 Supervisor 的启动而启动 更多使用说明: ${0} help 欢迎访问扩软博客: https://blog.kuoruan.com/ 我们的QQ群: 43391448 尽情使用吧! EOF } get_new_instance_id() { if [ -f "/etc/supervisor/conf.d/kcptun.conf" ]; then local i=2 while [ -f "/etc/supervisor/conf.d/kcptun${i}.conf" ] do let i++ done echo "$i" fi } # 添加实例 add_instance() { permission_check linux_check get_arch get_server_ip cat >&2 <<-'EOF' 你选择了添加实例, 正在开始操作... EOF current_id="$(get_new_instance_id)" set_kcptun_config config_kcptun config_firewall restart_supervisor get_installed_version generate_mobile_args cat >&2 <<-EOF 恭喜, 实例 kcptun${current_id} 添加成功! EOF show_config_info show_installed_version show_recommend_config } # 删除实例 del_instance() { permission_check linux_check get_arch if [ -n "$1" ]; then if is_number $1; then if [ $1 -ne 1 ]; then current_id="$1" fi else cat >&2 <<-EOF 参数有误, 请使用 $0 del <id> <id> 为实例ID, 当前共有 $(get_instance_count) 个实例 EOF exit 1 fi fi cat >&2 <<-EOF 你选择了删除实例 kcptun${current_id}, 实例配置文件删除后无法恢复 按任意键继续...或者 Ctrl+C 取消 EOF any_key_to_continue local supervisor_config_file="$(get_current_supervisor_config_file)" if [ ! -f "$supervisor_config_file" ]; then cat >&2 <<-EOF 你选择的实例 kcptun${current_id} 不存在! EOF exit 1 fi rm -f "$supervisor_config_file" rm -f "$(get_current_config_file)" rm -f "$(get_current_log_file)" restart_supervisor cat >&2 <<-EOF 实例 kcptun${current_id} 删除成功! EOF } # 安装 Kcptun install_kcptun() { permission_check linux_check installed_check disable_selinux get_arch get_server_ip set_kcptun_config install_dependence get_kcptun_version_info download_file unpack_file config_kcptun downlod_init_script install_service config_firewall install_cleanup get_installed_version generate_mobile_args show_installed_info } # 重新下载 kcptun update_kcptun() { download_file unpack_file restart_supervisor install_cleanup show_installed_version cat >&2 <<-EOF 恭喜, Kcptun 服务端更新完毕! EOF } #手动安装 manual_install() { permission_check linux_check get_arch cat >&2 <<-'EOF' 你选择了自定义版本安装, 正在开始操作... EOF local tag_name=$1 while : do if [ -z "$tag_name" ]; then cat >&2 <<-'EOF' 请输入你想安装的 Kcptun 版本的完整 TAG EOF read -p "(例如: v20160904): " tag_name echo if $(grep -qE "\w+" <<< "$tag_name"); then if [ "$tag_name" = "SNMP_Milestone" ]; then echo "不支持此版本, 请重新输入!" unset tag_name continue fi local version_num version_num=$(grep -oE "[0-9]+" <<< "$tag_name") || version_num=0 if [ ${#version_num} -eq 8 -a $version_num -le 20160826 ]; then echo "暂不支持安装 v20160826 及以前版本" continue fi else echo "输入无效, 请重新输入!" continue fi fi get_kcptun_version_info $tag_name if [ $? -eq 2 ]; then cat >&2 <<-EOF 未找到对应版本下载地址 (TAG: ${tag_name}), 请重新输入! 你可以前往: ${KCPTUN_TAGS_URL} 查看所有可用 TAG EOF unset tag_name continue else cat >&2 <<-EOF 已找到 Kcptun 版本信息, TAG: ${tag_name} 请按任意键继续安装... EOF any_key_to_continue update_kcptun break fi done } # 选择一个实例 select_instance() { if [ $(get_instance_count) -gt 1 ]; then cat >&2 <<-'EOF' 当前有多个 Kcptun 实例 (按最后修改时间排序): EOF local files=$(ls -lt /etc/supervisor/conf.d/ | grep "^-" | awk '{print $9}' | grep -P "^kcptun\d*\.conf$") local i=0 local array=() for file in $files do let i++ array[$i]=$(grep -oP "\d+" <<<"$file") echo "(${i}) "${file%.*} done while : do read -p "(默认: 1) 请选择 [1~${i}]: " sel if [ -n "$sel" ]; then if is_number $sel && [ $sel -ge 1 -a $sel -le $i ]; then current_id=${array[$sel]} else cat >&2 <<-EOF 请输入有效数字 1~${i}! EOF continue fi fi break done fi } # 加载实例配置信息 load_instance_config() { local config_file="$(get_current_config_file)" if [ ! -s "$config_file" ]; then cat >&2 <<-'EOF' 实例配置文件不存在或为空, 请检查! EOF exit 1 fi [ ! -x "$JQ" ] && install_jq if ! $($JQ -r '.' "$config_file" >/dev/null 2>&1); then cat >&2 <<-EOF 实例配置文件存在错误, 请检查! 配置文件路径: ${config_file} EOF exit 1 fi local lines=$($JQ -r 'to_entries | map("\(.key)=\(.value | @sh)") | .[]' "$config_file") while read -r line do eval "$line" done <<< "$lines" [ -n "$listen" ] && listen_port=$(cut -d ':' -f2 <<< "$listen") if [ -n "$target" ]; then target_ip=$(cut -d ':' -f1 <<< "$target") target_port=$(cut -d ':' -f2 <<< "$target") fi } # 显示配置信息 show_instance_config() { permission_check get_arch if [ -n "$1" ]; then if is_number $1; then if [ $1 -ne 1 ]; then current_id="$1" fi else cat >&2 <<-EOF 参数有误, 请使用 $0 show <id> <id> 为实例ID, 当前共有 $(get_instance_count) 个实例 EOF exit 1 fi fi cat >&2 <<-EOF 你选择了查看实例 kcptun${current_id} 的配置, 正在读取... EOF get_server_ip load_instance_config get_installed_version generate_mobile_args cat >&2 <<-EOF 实例 kcptun${current_id} 的配置信息如下: EOF show_config_info show_installed_version show_recommend_config } # 显示实例日志 show_instance_log() { permission_check if [ -n "$1" ]; then if is_number $1; then if [ $1 -ne 1 ]; then current_id="$1" fi else cat >&2 <<-EOF 参数有误, 请使用 $0 log <id> <id> 为实例ID, 当前共有 $(get_instance_count) 个实例 EOF exit 1 fi fi cat >&2 <<-EOF 你选择了查看实例 kcptun${current_id} 的日志, 正在读取... EOF local log_file="$(get_current_log_file)" if [ -f "$log_file" ]; then cat >&2 <<-EOF 实例 kcptun${current_id} 的日志信息如下: 注: 日志实时刷新, 按 Ctrl+C 退出日志查看 EOF tail -n 20 -f "$log_file" else cat >&2 <<-EOF 未找到实例 kcptun${current_id} 的日志文件... EOF exit 1 fi } show_installed_version() { cat >&2 <<-EOF 当前安装的 Kcptun 版本为: ${installed_kcptun_version} $([ -n "$kcptun_release_html_url" ] && echo "请前往 ${kcptun_release_html_url} 手动下载客户端文件") EOF } get_installed_version() { cat >&2 <<-'EOF' 正在获取当前安装的 Kcptun 信息... EOF local kcptun_server_exec="$KCPTUN_INSTALL_DIR"/server_"$FILE_SUFFIX" if [ -x "$kcptun_server_exec" ]; then installed_kcptun_version=$(${kcptun_server_exec} -v | awk '{printf $3}') else unset installed_kcptun_version cat >&2 <<-'EOF' 未找到已安装的 Kcptun 服务端执行文件, 或许你并没有安装 Kcptun? 请运行脚本来重新安装 Kcptun 服务端 EOF exit_with_error fi } # 检查更新 check_update() { permission_check linux_check get_arch cat >&2 <<-EOF 你选择了检查更新, 正在开始操作... EOF local shell_path=$0 get_shell_version_info if [ -n "$new_shell_version" -a $new_shell_version -gt $SHELL_VERSION ]; then cat >&2 <<-EOF 发现一键安装脚本更新, 版本号: ${new_shell_version} $(echo -e "更新说明: \n${shell_change_log}") 按任意键开始更新, 或者 Ctrl+C 取消 EOF any_key_to_continue echo "正在更新一键安装脚本..." mv -f "$shell_path" "$shell_path".bak if wget --no-check-certificate -O "$shell_path" "$new_shell_url"; then chmod a+x "$shell_path" sed -i -r "s/^CONFIG_VERSION=[0-9]+/CONFIG_VERSION=${CONFIG_VERSION}/" "$shell_path" sed -i -r "s/^INIT_VERSION=[0-9]+/INIT_VERSION=${INIT_VERSION}/" "$shell_path" rm -f "$shell_path".bak clear cat >&2 <<-EOF 安装脚本已更新到 v${new_shell_version}, 正在运行新的脚本... EOF bash $shell_path update exit 0 else mv -f "$shell_path".bak $shell_path cat >&2 <<-'EOF' 下载新的一键安装脚本失败... EOF fi else cat >&2 <<-'EOF' 未发现一键安装脚本更新... EOF fi get_installed_version get_kcptun_version_info local cur_tag_name="$installed_kcptun_version" if [ -n "$cur_tag_name" ] && is_number $cur_tag_name && [ ${#cur_tag_name} -eq 8 ]; then cur_tag_name=v"$cur_tag_name" fi if [ -n "$kcptun_release_tag_name" -a "$kcptun_release_tag_name" != "$cur_tag_name" ]; then cat >&2 <<-EOF 发现 Kcptun 新版本 ${kcptun_release_tag_name} $(echo -e "更新说明: \n${kcptun_release_name}") $([ "$kcptun_release_prerelease" = "true" ] && echo -e "\033[41;37m 注意: 该版本为预览版, 请谨慎更新 \033[0m") 按任意键开始更新, 或者 Ctrl+C 取消 EOF any_key_to_continue echo "正在自动更新 Kcptun..." update_kcptun else cat >&2 <<-'EOF' 未发现 Kcptun 更新... EOF fi if [ -n "$new_config_version" -a $new_config_version -gt $CONFIG_VERSION ]; then cat >&2 <<-EOF 发现 Kcptun 配置更新, 版本号: v${new_config_version}, 需要重新设置 Kcptun... $(echo -e "更新说明: \n${config_change_log}") 按任意键开始配置, 或者 Ctrl+C 取消 EOF any_key_to_continue reconfig_instance sed -i "s/^CONFIG_VERSION=${CONFIG_VERSION}/CONFIG_VERSION=${new_config_version}/" "$shell_path" else cat >&2 <<-'EOF' 未发现 Kcptun 配置更新... EOF fi if [ -n "$new_init_version" -a $new_init_version -gt $INIT_VERSION ]; then cat >&2 <<-EOF 发现服务启动脚本文件更新, 版本号: v${new_init_version} $(echo -e "更新说明: \n${init_change_log}") 按任意键开始更新, 或者 Ctrl+C 取消 EOF any_key_to_continue echo "正在自动更新启动脚本..." downlod_init_script [ "$OS" = "CentOS" -a $OS_VSRSION -eq 7 ] && systemctl daemon-reload sed -i "s/^INIT_VERSION=${INIT_VERSION}/INIT_VERSION=${new_init_version}/" "$shell_path" cat >&2 <<-EOF 服务启动脚本已更新到 v${new_init_version}, 可能需要重启服务器才能生效! EOF else cat >&2 <<-'EOF' 未发现服务启动脚本更新... EOF fi cat >&2 <<-'EOF' 正在更新 Supervisor... EOF easy_install -U supervisor >/dev/null 2>&1 cat >&2 <<-'EOF' 更新操作已完成! EOF } # 卸载 Kcptun uninstall_kcptun() { permission_check linux_check cat >&2 <<-'EOF' 你选择了卸载 Kcptun 服务端 按任意键继续...或者 Ctrl+C 取消 EOF any_key_to_continue echo "正在卸载 Kcptun 服务端并停止 Supervisor..." service supervisord stop if [ "$OS" = "CentOS" ]; then if yum list installed | grep -q "^jq\."; then yum remove -y jq fi else if dpkg -l | grep -q "\sjq\s"; then apt-get remove --purge -y jq fi fi rm -f "$JQ" rm -f "/etc/supervisor/conf.d/kcptun*.conf" rm -rf "$KCPTUN_INSTALL_DIR" rm -rf "$KCPTUN_LOG_DIR" cat >&2 <<-'EOF' 是否同时卸载 Supervisor ? 注意: Supervisor 的配置文件将同时被删除 EOF while : do read -p "(默认: 不卸载) 请选择 [y/n]: " yn [ -z "$yn" ] && yn="n" case ${yn:0:1} in y|Y) ;; n|N) break ;; *) echo "输入有误, 请重新输入!" continue ;; esac if [ "$OS" = "CentOS" ]; then chkconfig supervisord off else update-rc.d -f supervisord remove fi rm -rf "$(easy_install -mxN supervisor | grep 'Using.*supervisor.*\.egg' | awk '{print $2}')" rm -f /usr/local/bin/echo_supervisord_conf rm -f /usr/local/bin/pidproxy rm -f /usr/local/bin/supervisorctl rm -f /usr/local/bin/supervisord rm -rf /etc/supervisor/ rm -rf /etc/init.d/supervisord break done cat >&2 <<-EOF Kcptun 服务端卸载完成, 欢迎再次使用。 注意: 脚本没有自动卸载 python-setuptools (包含 easy_install) 如有需要, 你可以使用: $([ "${OS}" = "CentOS" ] && echo -n "yum remove python-setuptools" || echo -n "apt-get remove --purge python-setuptools") 来手动卸载 EOF } # 重启 Supervisor restart_supervisor() { if [ -x /etc/init.d/supervisord ]; then if [ -d "$KCPTUN_LOG_DIR" ]; then rm -f "$KCPTUN_LOG_DIR"/* else mkdir -p "$KCPTUN_LOG_DIR" fi if ! service supervisord restart; then cat >&2 <<-'EOF' 重启 Supervisor 失败, Kcptun 无法正常启动! EOF exit_with_error fi else cat >&2 <<-'EOF' 未找到 Supervisor 服务, 请手动检查! EOF exit_with_error fi } # 重新配置 reconfig_instance() { permission_check linux_check if [ -n "$1" ]; then if is_number $1; then if [ $1 -ne 1 ]; then current_id="$1" fi else cat >&2 <<-EOF 参数有误, 请使用 $0 reconfig <id> <id> 为实例ID, 当前共有 $(get_instance_count) 个实例 EOF exit 1 fi fi cat >&2 <<-EOF 你选择了重新配置实例 kcptun${current_id}, 正在开始操作... EOF if [ ! -f "$(get_current_supervisor_config_file)" ]; then cat >&2 <<-EOF 你选择的实例 kcptun${current_id} 不存在! EOF exit 1 fi get_arch get_server_ip while : do cat >&2 <<-'EOF' 请选择操作: (1) 重新配置实例所有选项 (2) 直接修改实例配置文件 EOF read -p "(默认: 1) 请选择: " sel echo if [ -n "$sel" ]; then case ${sel:0:1} in 1) ;; 2) echo "正在打开配置文件, 请手动修改..." local config_file="$(get_current_config_file)" if [ -f "$config_file" ]; then if command_exists vim; then vim "$config_file" load_instance_config break elif command_exists vi; then vi "$config_file" load_instance_config break elif command_exists gedit; then gedit "$config_file" load_instance_config break else echo "未找到可用的编辑器, 正在进入全新配置..." fi else echo "配置文件不存在, 正在进入全新配置..." fi ;; *) echo "输入有误, 请重新输入!" continue ;; esac fi set_kcptun_config config_kcptun config_firewall break done restart_supervisor cat >&2 <<-'EOF' 恭喜, Kcptun 服务端配置已更新! EOF get_installed_version generate_mobile_args show_config_info show_installed_version show_recommend_config } usage() { cat >&2 <<-EOF 请使用: $0 <option> 可使用的参数 <option> 包括: install 安装 uninstall 卸载 update 检查更新 manual 自定义 Kcptun 版本安装 help 查看脚本使用说明 add 添加一个实例, 多用户使用 reconfig <id> 重新配置实例 show <id> 显示实例详细配置 log <id> 显示实例日志 del <id> 删除一个实例 注: 上述参数中的 <id> 可选, 代表的是实例的ID 可使用 1, 2, 3 ... 分别对应实例 kcptun, kcptun2, kcptun3 ... 若不指定 <id>, 则默认为 1 Supervisor 命令: service supervisord {start|stop|restart|status} {启动|关闭|重启|查看状态} Kcptun 相关命令: supervisorctl {start|stop|restart|status} kcptun<id> {启动|关闭|重启|查看状态} EOF } # 初始化脚本动作 action=${1:-"install"} case $action in install) install_kcptun ;; uninstall) uninstall_kcptun ;; update) check_update ;; reconfig) reconfig_instance $2 ;; manual) manual_install $2 ;; show) show_instance_config $2 ;; log) show_instance_log $2 ;; add) add_instance ;; del) del_instance $2 ;; help|*) usage ;; esac exit 0