Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5ee659dd41 | ||
|
|
1a596f55f4 | ||
|
|
889a904dca | ||
|
|
172dcf6481 | ||
|
|
ff92c70b40 | ||
|
|
af1d28ae5a | ||
|
|
4a7d143c69 | ||
|
|
7ff69f16d5 | ||
|
|
7eb037c4dd | ||
|
|
a87f6af812 | ||
|
|
7574f9bfd3 | ||
|
|
d5b5cf683b | ||
|
|
fded353b68 | ||
|
|
832dbed064 | ||
|
|
4b660fa02c | ||
|
|
59e6ebee05 | ||
|
|
c1a895426d | ||
|
|
ebdcc9ae8a | ||
|
|
2c076f7a34 | ||
|
|
a1a0fbd060 | ||
|
|
bc4214e4a8 | ||
|
|
61e6dfe87b | ||
|
|
4448a2658d | ||
|
|
c9a312f12b | ||
|
|
2bb48495d3 | ||
|
|
06f0a6f80b | ||
|
|
ea895ad824 | ||
|
|
a72bf0c9b5 | ||
|
|
2b83dbda07 | ||
|
|
c35ef339af | ||
|
|
c96d4b534e | ||
|
|
6216049c14 | ||
|
|
0d98020e59 | ||
|
|
14ea6f8a71 | ||
|
|
214669eaf9 | ||
|
|
48d2cad6ac | ||
|
|
67cd5a4e22 | ||
|
|
f49392c95e | ||
|
|
4c9370a252 | ||
|
|
ef3cf3d982 | ||
|
|
8e5f405336 | ||
|
|
2aa6887860 | ||
|
|
9d13ec9350 | ||
|
|
ef9d08567f | ||
|
|
66ebcf2773 | ||
|
|
67a08b43ed | ||
|
|
46ce8a2e51 | ||
|
|
fd6a6e4a64 | ||
|
|
1de3c1fa8a | ||
|
|
cc821b6eaf | ||
|
|
6f8ce90c23 | ||
|
|
32be3e836b | ||
|
|
664a79e488 | ||
|
|
74d874b0db | ||
|
|
58874784f3 | ||
|
|
f92bae1d03 | ||
|
|
a1aa89ea1c | ||
|
|
52c75f68ba | ||
|
|
57fce73ca6 | ||
|
|
13e980cdff |
@@ -0,0 +1,16 @@
|
||||
language: go
|
||||
go:
|
||||
- 1.6
|
||||
before_install:
|
||||
- go get github.com/mattn/goveralls
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
install:
|
||||
- go get github.com/xtaci/kcptun/client
|
||||
- go get github.com/xtaci/kcptun/server
|
||||
before_script:
|
||||
script:
|
||||
- cd $HOME/gopath/src/github.com/xtaci/kcptun/client
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- cd $HOME/gopath/src/github.com/xtaci/kcptun/server
|
||||
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||
- exit 0
|
||||
@@ -1,5 +1,8 @@
|
||||
FROM golang:latest
|
||||
FROM golang:alpine
|
||||
MAINTAINER xtaci <daniel820313@gmail.com>
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add git
|
||||
RUN go get github.com/xtaci/kcptun/client
|
||||
RUN go get github.com/xtaci/kcptun/server
|
||||
EXPOSE 29900/udp
|
||||
|
||||
@@ -0,0 +1,160 @@
|
||||
# <img src="logo.png" alt="kcptun" height="60px" />
|
||||
[![GoDoc][1]][2] [![Release][13]][14] [![Powered][17]][18] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16]
|
||||
[1]: https://godoc.org/github.com/xtaci/kcptun?status.svg
|
||||
[2]: https://godoc.org/github.com/xtaci/kcptun
|
||||
[3]: https://travis-ci.org/xtaci/kcptun.svg?branch=master
|
||||
[4]: https://travis-ci.org/xtaci/kcptun
|
||||
[5]: https://goreportcard.com/badge/github.com/xtaci/kcptun
|
||||
[6]: https://goreportcard.com/report/github.com/xtaci/kcptun
|
||||
[7]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[8]: https://raw.githubusercontent.com/xtaci/kcptun/master/LICENSE.md
|
||||
[9]: https://img.shields.io/github/stars/xtaci/kcptun.svg
|
||||
[10]: https://github.com/xtaci/kcptun/stargazers
|
||||
[11]: https://img.shields.io/github/forks/xtaci/kcptun.svg
|
||||
[12]: https://github.com/xtaci/kcptun/network
|
||||
[13]: https://img.shields.io/github/release/xtaci/kcptun.svg
|
||||
[14]: https://github.com/xtaci/kcptun/releases/latest
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800
|
||||
[16]: https://github.com/xtaci/kcptun/releases
|
||||
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
|
||||
[18]: https://github.com/skywind3000/kcp
|
||||
[19]: https://img.shields.io/docker/pulls/xtaci/kcptun.svg?maxAge=1800
|
||||
[20]: https://hub.docker.com/r/xtaci/kcptun/
|
||||
|
||||
A tool for converting tcp stream into kcp+udp stream, :zap: ***[download address](https://github.com/xtaci/kcptun/releases/latest)***:zap:
|
||||
|
||||

|
||||
|
||||
***kcptun is based on [kcp-go](https://github.com/xtaci/kcp-go)***
|
||||
|
||||
### *QuickStart* :lollipop:
|
||||
```
|
||||
Server Side: ./server_linux_amd64 -t "127.0.0.1:1080" -l ":554" -mode fast2 // forwarding to local port 1080
|
||||
Client Side: ./client_darwin_amd64 -r "SERVERIP:554" -l ":1080" -mode fast2 // listening on port 1080
|
||||
```
|
||||
|
||||
### *Performance* :lollipop:
|
||||
<img src="fast.png" alt="fast.com" height="256px" />
|
||||
Speed tested with: https://fast.com
|
||||
|
||||
### *Usage* :lollipop:
|
||||

|
||||

|
||||
|
||||
### *Applications* :lollipop:
|
||||
1. Real-time gaming.
|
||||
2. Cross-ISP data exchange in PRC.
|
||||
3. Other lossy network.
|
||||
|
||||
### *Parameters Recommended* :lollipop:
|
||||
```
|
||||
Test Environment: China Telecom 100M ADSL(100mbps up/8mbps down)
|
||||
SERVER: -mtu 1400 -sndwnd 2048 -rcvwnd 2048 -mode fast2
|
||||
CLIENT: -mtu 1400 -sndwnd 256 -rcvwnd 2048 -mode fast2 -dscp 46
|
||||
```
|
||||
|
||||
*How to optimize*:
|
||||
> Step 1:Increase client rcvwnd & server sndwnd simultaneously & gradually。
|
||||
> Step 2:Try download something and observer, if the bandwidth usage is close the limit then stop, otherwise goto step 1.
|
||||
|
||||
### *Traffic Control* :lollipop:
|
||||
***Intended audience : for those server's bandwidth is quite limited.***
|
||||
|
||||
Example: To limit outgoing bandwidth to 32mbit/s on server.
|
||||
```
|
||||
root@kcptun:~# cat tc.sh
|
||||
tc qdisc del dev eth0 root
|
||||
tc qdisc add dev eth0 root handle 1: htb
|
||||
tc class add dev eth0 parent 1: classid 1:1 htb rate 32mbit
|
||||
tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 10 fw flowid 1:1
|
||||
iptables -t mangle -A POSTROUTING -o eth0 -j MARK --set-mark 10
|
||||
root@kcptun:~#
|
||||
```
|
||||
|
||||
### *DSCP* :lollipop:
|
||||
Differentiated services or DiffServ is a computer networking architecture that specifies a simple, scalable and coarse-grained mechanism for classifying and managing network traffic and providing quality of service (QoS) on modern IP networks. DiffServ can, for example, be used to provide low-latency to critical network traffic such as voice or streaming media while providing simple best-effort service to non-critical services such as web traffic or file transfers.
|
||||
|
||||
DiffServ uses a 6-bit differentiated services code point (DSCP) in the 8-bit differentiated services field (DS field) in the IP header for packet classification purposes. The DS field and ECN field replace the outdated IPv4 TOS field.[1]
|
||||
|
||||
setting each side with ```-dscp value```.
|
||||
|
||||
### *Embeded Mode* :lollipop:
|
||||
Latency:
|
||||
*fast3 >* ***[fast2]*** *> fast > normal > default*
|
||||
Payload Ratio:
|
||||
*default > normal > fast >* ***[fast2]*** *> fast3*
|
||||
Parameters in middle is balanced for latency & payload ratio, the faster you get the more wasteful you are.
|
||||
Manual control is supported with hidden parameters, you must understand KCP protocol before doing this.
|
||||
```
|
||||
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20
|
||||
```
|
||||
|
||||
### *Forward Error Correction* :lollipop:
|
||||
In coding theory, the Reed–Solomon code belongs to the class of non-binary cyclic error-correcting codes. The Reed–Solomon code is based on univariate polynomials over finite fields.
|
||||
|
||||
It is able to detect and correct multiple symbol errors. By adding t check symbols to the data, a Reed–Solomon code can detect any combination of up to t erroneous symbols, or correct up to ⌊t/2⌋ symbols. As an erasure code, it can correct up to t known erasures, or it can detect and correct combinations of errors and erasures. Furthermore, Reed–Solomon codes are suitable as multiple-burst bit-error correcting codes, since a sequence of b + 1 consecutive bit errors can affect at most two symbols of size b. The choice of t is up to the designer of the code, and may be selected within wide limits.
|
||||
|
||||

|
||||
|
||||
Setting parameters of RS-Code with ```-datashard 10 -parityshard 3```
|
||||
|
||||
### *Snappy Stream Compression* :lollipop:
|
||||
> Snappy is a compression/decompression library. It does not aim for maximum
|
||||
> compression, or compatibility with any other compression library; instead,
|
||||
> it aims for very high speeds and reasonable compression. For instance,
|
||||
> compared to the fastest mode of zlib, Snappy is an order of magnitude faster
|
||||
> for most inputs, but the resulting compressed files are anywhere from 20% to
|
||||
> 100% bigger.
|
||||
|
||||
> Reference: http://google.github.io/snappy/
|
||||
|
||||
disable compression by setting ```-nocomp``` on both side.
|
||||
|
||||
### *SNMP* :lollipop:
|
||||
```go
|
||||
// Snmp defines network statistics indicator
|
||||
type Snmp struct {
|
||||
BytesSent uint64 // payload bytes sent
|
||||
BytesReceived uint64
|
||||
MaxConn uint64
|
||||
ActiveOpens uint64
|
||||
PassiveOpens uint64
|
||||
CurrEstab uint64
|
||||
InErrs uint64
|
||||
InCsumErrors uint64 // checksum errors
|
||||
InSegs uint64
|
||||
OutSegs uint64
|
||||
OutBytes uint64 // udp bytes sent
|
||||
RetransSegs uint64
|
||||
FastRetransSegs uint64
|
||||
EarlyRetransSegs uint64
|
||||
LostSegs uint64
|
||||
RepeatSegs uint64
|
||||
FECRecovered uint64
|
||||
FECErrs uint64
|
||||
FECSegs uint64 // fec segments received
|
||||
}
|
||||
```
|
||||
|
||||
Sending a signal by ```kill -SIGUSR1 pid``` will give SNMP information for KCP,useful for fine-grained adjustment.
|
||||
Of which ```RetransSegs,FastRetransSegs,LostSegs,OutSegs``` is the most useful.
|
||||
|
||||
### *Donations* :dollar:
|
||||

|
||||
|
||||
All donations to this project will be used on the R&D of [gonet/2](http://gonet2.github.io/).
|
||||
|
||||
### *References* :paperclip:
|
||||
1. https://github.com/skywind3000/kcp -- KCP - A Fast and Reliable ARQ Protocol.
|
||||
2. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go.
|
||||
3. https://en.wikipedia.org/wiki/Differentiated_services -- DSCP.
|
||||
4. http://google.github.io/snappy/ -- A fast compressor/decompressor.
|
||||
5. https://www.backblaze.com/blog/reed-solomon/ -- Reed-Solomon Explained.
|
||||
6. http://www.qualcomm.cn/products/raptorq -- RaptorQ Forward Error Correction Scheme for Object Delivery.
|
||||
7. https://en.wikipedia.org/wiki/PBKDF2 -- Key stretching.
|
||||
8. http://blog.appcanary.com/2016/encrypt-or-compress.html -- Should you encrypt or compress first?
|
||||
9. https://github.com/hashicorp/yamux -- Connection multiplexing library.
|
||||
10. https://tools.ietf.org/html/rfc6937 -- Proportional Rate Reduction for TCP.
|
||||
11. https://tools.ietf.org/html/rfc5827 -- Early Retransmit for TCP and Stream Control Transmission Protocol (SCTP).
|
||||
12. http://http2.github.io/ -- What is HTTP/2?
|
||||
13. http://www.lartc.org/ -- Linux Advanced Routing & Traffic Control
|
||||
@@ -1,18 +1,128 @@
|
||||
# *kcptun*
|
||||
TCP流转换为KCP+UDP流,:zap:***[下载地址](https://github.com/xtaci/kcptun/releases/latest)***:zap:工作示意图:
|
||||
# <img src="logo.png" alt="kcptun" height="60px" />
|
||||
[![GoDoc][1]][2] [![Release][13]][14] [![Powered][17]][18] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16]
|
||||
[1]: https://godoc.org/github.com/xtaci/kcptun?status.svg
|
||||
[2]: https://godoc.org/github.com/xtaci/kcptun
|
||||
[3]: https://travis-ci.org/xtaci/kcptun.svg?branch=master
|
||||
[4]: https://travis-ci.org/xtaci/kcptun
|
||||
[5]: https://goreportcard.com/badge/github.com/xtaci/kcptun
|
||||
[6]: https://goreportcard.com/report/github.com/xtaci/kcptun
|
||||
[7]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[8]: https://raw.githubusercontent.com/xtaci/kcptun/master/LICENSE.md
|
||||
[9]: https://img.shields.io/github/stars/xtaci/kcptun.svg
|
||||
[10]: https://github.com/xtaci/kcptun/stargazers
|
||||
[11]: https://img.shields.io/github/forks/xtaci/kcptun.svg
|
||||
[12]: https://github.com/xtaci/kcptun/network
|
||||
[13]: https://img.shields.io/github/release/xtaci/kcptun.svg
|
||||
[14]: https://github.com/xtaci/kcptun/releases/latest
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=1800
|
||||
[16]: https://github.com/xtaci/kcptun/releases
|
||||
[17]: https://img.shields.io/badge/KCP-Powered-blue.svg
|
||||
[18]: https://github.com/skywind3000/kcp
|
||||
[19]: https://img.shields.io/docker/pulls/xtaci/kcptun.svg?maxAge=2592000
|
||||
[20]: https://hub.docker.com/r/xtaci/kcptun/
|
||||
***TCP端口加速器,用于[kcp-go](https://github.com/xtaci/kcp-go)协议测试 :zap: [官方下载地址](https://github.com/xtaci/kcptun/releases/latest):zap:***
|
||||
|
||||

|
||||
[English Readme](README.en.md)
|
||||
### *快速设定* :lollipop:
|
||||
```
|
||||
服务器: ./server_linux_amd64 -t "127.0.0.1:8388" -l ":554" -mode fast2 // 转发到服务器的本地8388端口
|
||||
客户端: ./client_darwin_amd64 -r "服务器IP地址:554" -l ":8388" -mode fast2 // 监听客户端的本地8388端口
|
||||
注: 服务器端需要有服务监听8388端口
|
||||
```
|
||||
|
||||
***kcptun是[kcp](https://github.com/xtaci/kcp-go)协议的一个简单应用,可以用于任意tcp网络程序的传输承载,以提高网络流畅度,降低掉线情况。***
|
||||
### *速度对比* :lollipop:
|
||||
<img src="fast.png" alt="fast.com" height="256px" />
|
||||
测速网站: https://fast.com
|
||||
|
||||
### *使用方法* :lollipop:
|
||||
在Mac OS X El Capitan下的帮助输出:
|
||||
|
||||
### *使用の方法* :lollipop:
|
||||

|
||||

|
||||
|
||||
### *适用范围限定* :lollipop:
|
||||
1. 实时网络游戏的数据传输
|
||||
2. 跨运营商的流量传输
|
||||
3. 其他高丢包通信链路的TCP承载
|
||||
### *推荐参数* :lollipop:
|
||||
```
|
||||
适用大部分ADSL接入(非对称上下行)的参数(实验环境电信100M ADSL)
|
||||
其它带宽请按比例调整,比如 50M ADSL,把 CLIENT 的 -sndwnd -rcvwnd 减掉一半,SERVER 不变
|
||||
|
||||
SERVER: -mtu 1400 -sndwnd 2048 -rcvwnd 2048 -mode fast2
|
||||
CLIENT: -mtu 1400 -sndwnd 256 -rcvwnd 2048 -mode fast2 -dscp 46
|
||||
*巭孬嫑乱动*
|
||||
```
|
||||
|
||||
*简易自我调优方法*:
|
||||
> 第一步:同时在两端逐步增大client rcvwnd和server sndwnd;
|
||||
> 第二步:尝试下载,观察如果带宽利用率(服务器+客户端两端都要观察)接近物理带宽则停止,否则跳转到第一步。
|
||||
|
||||
*带宽计算公式*:
|
||||
```
|
||||
在不丢包的情况下,有最大-rcvwnd 个数据包在网络上正在向你传输,以平均数据包大小avgsize计算,在任意时刻,有:
|
||||
|
||||
network_cap = rcvwnd*avgsize
|
||||
|
||||
数据流向你,这个值再除以ping值(rtt),等于最大带宽使用量。
|
||||
|
||||
max_bandwidth = network_cap/rtt = rcvwnd*avgsize/rtt
|
||||
|
||||
举例,设rcvwnd = 1024, avgsize = 1KB, rtt = 400ms,则:
|
||||
|
||||
max_bandwidth = 1024 * 1KB / 400ms = 2.5MB/s ~= 25Mbps
|
||||
|
||||
(注:以上计算不包括前向纠错的数据量)
|
||||
|
||||
前向纠错是最大带宽量的一个固定比例增加:
|
||||
|
||||
max_bandwidth_fec = max_bandwidth*(datashard+parityshard)/datashard
|
||||
|
||||
举例,设datashard = 10 , partiyshard = 3,则:
|
||||
|
||||
max_bandwidth_fec = max_bandwidth * (10 + 3) /10 = 1.3*max_bandwidth = 1.3 * 25Mbps = 32.5Mbps
|
||||
```
|
||||
|
||||
### *流量控制* :lollipop:
|
||||
***必要性: 针对流量敏感的服务器,做双保险。***
|
||||
|
||||
> 基本原则: SERVER的发送速率不能超过ADSL下行带宽,否则只会浪费您的服务器带宽。
|
||||
|
||||
在server通过linux tc,可以限制服务器发送带宽。
|
||||
举例: 用linux tc限制server发送带宽为32mbit/s:
|
||||
```
|
||||
root@kcptun:~# cat tc.sh
|
||||
tc qdisc del dev eth0 root
|
||||
tc qdisc add dev eth0 root handle 1: htb
|
||||
tc class add dev eth0 parent 1: classid 1:1 htb rate 32mbit
|
||||
tc filter add dev eth0 protocol ip parent 1:0 prio 1 handle 10 fw flowid 1:1
|
||||
iptables -t mangle -A POSTROUTING -o eth0 -j MARK --set-mark 10
|
||||
root@kcptun:~#
|
||||
```
|
||||
其中eth0为网卡,有些服务器为ens3,有些为p2p1,通过ifconfig查询修改。
|
||||
|
||||
|
||||
### *DSCP* :lollipop:
|
||||
DSCP差分服务代码点(Differentiated Services Code Point),IETF于1998年12月发布了Diff-Serv(Differentiated Service)的QoS分类标准。它在每个数据包IP头部的服务类别TOS标识字节中,利用已使用的6比特和未使用的2比特,通过编码值来区分优先级。
|
||||
常用DSCP值可以参考[Wikipedia DSCP](https://en.wikipedia.org/wiki/Differentiated_services#Commonly_used_DSCP_values),至于有没有用,完全取决于数据包经过的设备。
|
||||
|
||||
通过 ```-dscp ``` 参数指定dscp值,两端可分别设定。
|
||||
|
||||
### *前向纠错* :lollipop:
|
||||
前向纠错采用Reed Solomon纠删码, 它的基本原理如下: 给定n个数据块d1, d2,…, dn,n和一个正整数m, RS根据n个数据块生成m个校验块, c1, c2,…, cm。 对于任意的n和m, 从n个原始数据块和m 个校验块中任取n块就能解码出原始数据, 即RS最多容忍m个数据块或者校验块同时丢失。
|
||||
|
||||

|
||||
|
||||
通过参数```-datashard 10 -parityshard 3``` 在两端同时设定。
|
||||
|
||||
### *Snappy数据流压缩* :lollipop:
|
||||
> Snappy is a compression/decompression library. It does not aim for maximum
|
||||
> compression, or compatibility with any other compression library; instead,
|
||||
> it aims for very high speeds and reasonable compression. For instance,
|
||||
> compared to the fastest mode of zlib, Snappy is an order of magnitude faster
|
||||
> for most inputs, but the resulting compressed files are anywhere from 20% to
|
||||
> 100% bigger.
|
||||
|
||||
> Reference: http://google.github.io/snappy/
|
||||
|
||||
通过参数 ```-nocomp``` 在两端同时设定以关闭压缩。
|
||||
|
||||
### *内置模式* :lollipop:
|
||||
响应速度:
|
||||
@@ -22,52 +132,69 @@ TCP流转换为KCP+UDP流,:zap:***[下载地址](https://github.com/xtaci/kcpt
|
||||
中间mode参数比较均衡,总之就是越快越浪费带宽,推荐模式 ***fast2***
|
||||
更高级的 ***手动档*** 需要理解KCP协议,并通过 ***隐藏参数*** 调整,例如:
|
||||
```
|
||||
-mode manual -nodelay 1 -resend 4 -nc 1 -interval 20 -fec 4
|
||||
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20
|
||||
```
|
||||
|
||||
### *SNMP* :lollipop:
|
||||
```go
|
||||
// Snmp defines network statistics indicator
|
||||
type Snmp struct {
|
||||
BytesSent uint64 // payload bytes sent
|
||||
BytesReceived uint64
|
||||
MaxConn uint64
|
||||
ActiveOpens uint64
|
||||
PassiveOpens uint64
|
||||
CurrEstab uint64
|
||||
InErrs uint64
|
||||
InCsumErrors uint64 // checksum errors
|
||||
InSegs uint64
|
||||
OutSegs uint64
|
||||
OutBytes uint64 // udp bytes sent
|
||||
RetransSegs uint64
|
||||
FastRetransSegs uint64
|
||||
LostSegs uint64
|
||||
RepeatSegs uint64
|
||||
FECRecovered uint64
|
||||
FECErrs uint64
|
||||
FECSegs uint64 // fec segments received
|
||||
BytesSent uint64 // payload bytes sent
|
||||
BytesReceived uint64
|
||||
MaxConn uint64
|
||||
ActiveOpens uint64
|
||||
PassiveOpens uint64
|
||||
CurrEstab uint64
|
||||
InErrs uint64
|
||||
InCsumErrors uint64 // checksum errors
|
||||
InSegs uint64
|
||||
OutSegs uint64
|
||||
OutBytes uint64 // udp bytes sent
|
||||
RetransSegs uint64
|
||||
FastRetransSegs uint64
|
||||
EarlyRetransSegs uint64
|
||||
LostSegs uint64
|
||||
RepeatSegs uint64
|
||||
FECRecovered uint64
|
||||
FECErrs uint64
|
||||
FECSegs uint64 // fec segments received
|
||||
}
|
||||
```
|
||||
|
||||
使用```kill -SIGUSR1 pid``` 可以在控制台打印出SNMP信息,通常用于精细调整***当前链路的有效载荷比***。
|
||||
观察```RetransSegs,FastRetransSegs,LostSegs,OutSegs```这几者的数值比例,用于参考调整```-mode manual,fec```的参数。
|
||||
|
||||
### *性能对比* :lollipop:
|
||||
```
|
||||
root@vultr:~# iperf -s
|
||||
------------------------------------------------------------
|
||||
Server listening on TCP port 5001
|
||||
TCP window size: 4.00 MByte (default)
|
||||
------------------------------------------------------------
|
||||
[ 4] local 172.7.7.1 port 5001 connected with 172.7.7.2 port 55453
|
||||
[ ID] Interval Transfer Bandwidth
|
||||
[ 4] 0.0-18.0 sec 5.50 MBytes 2.56 Mbits/sec <-- connection via kcptun
|
||||
[ 5] local 45.32.xxx.xxx port 5001 connected with 218.88.xxx.xxx port 17220
|
||||
[ 5] 0.0-17.9 sec 2.12 MBytes 997 Kbits/sec <-- direct connnection via tcp
|
||||
```
|
||||
### *故障排除* :lollipop:
|
||||
> Q: 客户端和服务器端***皆无*** ```stream opened```信息。
|
||||
> A: 连接客户端程序的端口设置错误。
|
||||
|
||||
> Q: 客户端有 ```stream opened```信息,服务器端没有。
|
||||
> A: 连接服务器的端口设置错误,或者被防火墙拦截。
|
||||
|
||||
> Q: 客户端服务器***皆有*** ```stream opened```信息,但无法通信。
|
||||
> A: 上层软件的设定错误。
|
||||
|
||||
### *免责申明* :warning:
|
||||
用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。
|
||||
|
||||
对免责声明的解释、修改及更新权均属于作者本人所有。
|
||||
|
||||
### *捐赠* :dollar:
|
||||

|
||||
|
||||
对该项目的捐款将用于[gonet/2](http://gonet2.github.io/)游戏服务器框架的研发。
|
||||
|
||||
### *参考资料* :paperclip:
|
||||
1. https://github.com/skywind3000/kcp -- KCP - A Fast and Reliable ARQ Protocol.
|
||||
2. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go.
|
||||
3. https://en.wikipedia.org/wiki/Differentiated_services -- DSCP.
|
||||
4. http://google.github.io/snappy/ -- A fast compressor/decompressor.
|
||||
5. https://www.backblaze.com/blog/reed-solomon/ -- Reed-Solomon Explained.
|
||||
6. http://www.qualcomm.cn/products/raptorq -- RaptorQ Forward Error Correction Scheme for Object Delivery.
|
||||
7. https://en.wikipedia.org/wiki/PBKDF2 -- Key stretching.
|
||||
8. http://blog.appcanary.com/2016/encrypt-or-compress.html -- Should you encrypt or compress first?
|
||||
9. https://github.com/hashicorp/yamux -- Connection multiplexing library.
|
||||
10. https://tools.ietf.org/html/rfc6937 -- Proportional Rate Reduction for TCP.
|
||||
11. https://tools.ietf.org/html/rfc5827 -- Early Retransmit for TCP and Stream Control Transmission Protocol (SCTP).
|
||||
12. http://http2.github.io/ -- What is HTTP/2?
|
||||
13. http://www.lartc.org/LARTC-zh_CN.GB2312.pdf -- Linux Advanced Routing & Traffic Control
|
||||
|
||||
@@ -22,8 +22,8 @@ for os in ${OSES[@]}; do
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
|
||||
env GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o server_${os}_${arch}${suffix} github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -o server_${os}_${arch}${suffix} github.com/xtaci/kcptun/server
|
||||
if $UPX; then upx -9 client_${os}_${arch}${suffix} server_${os}_${arch}${suffix};fi
|
||||
tar -zcf kcptun-${os}-${arch}-$VERSION.tar.gz client_${os}_${arch}${suffix} server_${os}_${arch}${suffix}
|
||||
$MD5 kcptun-${os}-${arch}-$VERSION.tar.gz
|
||||
@@ -33,8 +33,8 @@ done
|
||||
# ARM
|
||||
ARMS=(5 6 7)
|
||||
for v in ${ARMS[@]}; do
|
||||
env GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
done
|
||||
if $UPX; then upx -9 client_linux_arm* server_linux_arm*;fi
|
||||
tar -zcf kcptun-linux-arm-$VERSION.tar.gz client_linux_arm* server_linux_arm*
|
||||
|
||||
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 65 KiB |
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
@@ -8,14 +9,50 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/hashicorp/yamux"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
var VERSION = "SELFBUILD"
|
||||
var (
|
||||
// VERSION is injected by buildflags
|
||||
VERSION = "SELFBUILD"
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
func handleClient(p1, p2 net.Conn) {
|
||||
type compStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
func (c *compStream) Read(p []byte) (n int, err error) {
|
||||
return c.r.Read(p)
|
||||
}
|
||||
|
||||
func (c *compStream) Write(p []byte) (n int, err error) {
|
||||
n, err = c.w.Write(p)
|
||||
err = c.w.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *compStream) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func newCompStream(conn net.Conn) *compStream {
|
||||
c := new(compStream)
|
||||
c.conn = conn
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
log.Println("stream opened")
|
||||
defer log.Println("stream closed")
|
||||
defer p1.Close()
|
||||
@@ -71,11 +108,21 @@ func main() {
|
||||
Usage: "key for communcation, must be the same as kcptun server",
|
||||
EnvVar: "KCPTUN_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "methods for encryption: aes, tea, xor, none",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
Value: "fast",
|
||||
Usage: "mode for communication: fast3, fast2, fast, normal",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "conn",
|
||||
Value: 1,
|
||||
Usage: "establish N physical connections as specified by 'conn' to server",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
Value: 1350,
|
||||
@@ -91,14 +138,24 @@ func main() {
|
||||
Value: 1024,
|
||||
Usage: "set receive window size(num of packets)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "fec",
|
||||
Value: 4,
|
||||
Usage: "set FEC group size, must be the same as server",
|
||||
Name: "datashard",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
@@ -126,19 +183,15 @@ func main() {
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
myApp.Action = func(c *cli.Context) {
|
||||
myApp.Action = func(c *cli.Context) error {
|
||||
log.Println("version:", VERSION)
|
||||
addr, err := net.ResolveTCPAddr("tcp", c.String("localaddr"))
|
||||
checkError(err)
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
checkError(err)
|
||||
log.Println("listening on:", listener.Addr())
|
||||
|
||||
START_KCP:
|
||||
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
||||
|
||||
// kcp server
|
||||
kcpconn, err := kcp.DialWithOptions(c.Int("fec"), c.String("remoteaddr"), []byte(c.String("key")))
|
||||
checkError(err)
|
||||
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
|
||||
|
||||
switch c.String("mode") {
|
||||
@@ -152,49 +205,81 @@ func main() {
|
||||
nodelay, interval, resend, nc = 1, 10, 2, 1
|
||||
}
|
||||
|
||||
log.Println("listening on:", listener.Addr())
|
||||
log.Println("encryption:", c.String("crypt"))
|
||||
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
|
||||
log.Println("remote address:", c.String("remoteaddr"))
|
||||
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
|
||||
log.Println("compression:", !c.Bool("nocomp"))
|
||||
log.Println("mtu:", c.Int("mtu"))
|
||||
log.Println("fec:", c.Int("fec"))
|
||||
log.Println("datashard:", c.Int("datashard"), "parityshard:", c.Int("parityshard"))
|
||||
log.Println("acknodelay:", c.Bool("acknodelay"))
|
||||
log.Println("dscp:", c.Int("dscp"))
|
||||
log.Println("conn:", c.Int("conn"))
|
||||
|
||||
kcpconn.SetNoDelay(nodelay, interval, resend, nc)
|
||||
kcpconn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
|
||||
kcpconn.SetMtu(c.Int("mtu"))
|
||||
kcpconn.SetACKNoDelay(c.Bool("acknodelay"))
|
||||
kcpconn.SetDSCP(c.Int("dscp"))
|
||||
createConn := func() *yamux.Session {
|
||||
var block kcp.BlockCrypt
|
||||
switch c.String("crypt") {
|
||||
case "tea":
|
||||
block, _ = kcp.NewTEABlockCrypt(pass[:16])
|
||||
case "xor":
|
||||
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
|
||||
case "none":
|
||||
block, _ = kcp.NewNoneBlockCrypt(pass)
|
||||
default:
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
}
|
||||
kcpconn, err := kcp.DialWithOptions(c.String("remoteaddr"), block, c.Int("datashard"), c.Int("parityshard"))
|
||||
checkError(err)
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetNoDelay(nodelay, interval, resend, nc)
|
||||
kcpconn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
|
||||
kcpconn.SetMtu(c.Int("mtu"))
|
||||
kcpconn.SetACKNoDelay(c.Bool("acknodelay"))
|
||||
kcpconn.SetDSCP(c.Int("dscp"))
|
||||
|
||||
// stream multiplex
|
||||
var mux *yamux.Session
|
||||
config := &yamux.Config{
|
||||
AcceptBacklog: 256,
|
||||
EnableKeepAlive: true,
|
||||
KeepAliveInterval: 30 * time.Second,
|
||||
ConnectionWriteTimeout: 30 * time.Second,
|
||||
MaxStreamWindowSize: 16777216,
|
||||
LogOutput: os.Stderr,
|
||||
// stream multiplex
|
||||
config := &yamux.Config{
|
||||
AcceptBacklog: 256,
|
||||
EnableKeepAlive: true,
|
||||
KeepAliveInterval: 30 * time.Second,
|
||||
ConnectionWriteTimeout: 30 * time.Second,
|
||||
MaxStreamWindowSize: 16777216,
|
||||
LogOutput: os.Stderr,
|
||||
}
|
||||
var session *yamux.Session
|
||||
if c.Bool("nocomp") {
|
||||
session, err = yamux.Client(kcpconn, config)
|
||||
} else {
|
||||
session, err = yamux.Client(newCompStream(kcpconn), config)
|
||||
}
|
||||
checkError(err)
|
||||
return session
|
||||
}
|
||||
session, err := yamux.Client(kcpconn, config)
|
||||
checkError(err)
|
||||
mux = session
|
||||
|
||||
numconn := uint16(c.Int("conn"))
|
||||
var muxes []*yamux.Session
|
||||
for i := uint16(0); i < numconn; i++ {
|
||||
muxes = append(muxes, createConn())
|
||||
}
|
||||
|
||||
rr := uint16(0)
|
||||
for {
|
||||
p1, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
checkError(err)
|
||||
mux := muxes[rr%numconn]
|
||||
p2, err := mux.Open()
|
||||
if err != nil { // yamux failure
|
||||
log.Println(err)
|
||||
kcpconn.Close()
|
||||
p1.Close()
|
||||
goto START_KCP
|
||||
mux.Close()
|
||||
muxes[rr%numconn] = createConn()
|
||||
continue
|
||||
}
|
||||
go handleClient(p1, p2)
|
||||
rr++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
go sig_handler()
|
||||
go sigHandler()
|
||||
}
|
||||
|
||||
func sig_handler() {
|
||||
func sigHandler() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR1)
|
||||
|
||||
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 62 KiB |
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
@@ -8,15 +9,51 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/hashicorp/yamux"
|
||||
"github.com/urfave/cli"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
var VERSION = "SELFBUILD"
|
||||
var (
|
||||
// VERSION is injected by buildflags
|
||||
VERSION = "SELFBUILD"
|
||||
// SALT is use for pbkdf2 key expansion
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
type compStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
r *snappy.Reader
|
||||
}
|
||||
|
||||
func (c *compStream) Read(p []byte) (n int, err error) {
|
||||
return c.r.Read(p)
|
||||
}
|
||||
|
||||
func (c *compStream) Write(p []byte) (n int, err error) {
|
||||
n, err = c.w.Write(p)
|
||||
err = c.w.Flush()
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (c *compStream) Close() error {
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
func newCompStream(conn net.Conn) *compStream {
|
||||
c := new(compStream)
|
||||
c.conn = conn
|
||||
c.w = snappy.NewBufferedWriter(conn)
|
||||
c.r = snappy.NewReader(conn)
|
||||
return c
|
||||
}
|
||||
|
||||
// handle multiplex-ed connection
|
||||
func handleMux(conn *kcp.UDPSession, target string) {
|
||||
func handleMux(conn io.ReadWriteCloser, target string) {
|
||||
// stream multiplex
|
||||
var mux *yamux.Session
|
||||
config := &yamux.Config{
|
||||
@@ -50,7 +87,7 @@ func handleMux(conn *kcp.UDPSession, target string) {
|
||||
}
|
||||
}
|
||||
|
||||
func handleClient(p1, p2 net.Conn) {
|
||||
func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
log.Println("stream opened")
|
||||
defer log.Println("stream closed")
|
||||
defer p1.Close()
|
||||
@@ -99,6 +136,11 @@ func main() {
|
||||
Usage: "key for communcation, must be the same as kcptun client",
|
||||
EnvVar: "KCPTUN_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "methods for encryption: aes, tea, xor, none",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
Value: "fast",
|
||||
@@ -119,14 +161,24 @@ func main() {
|
||||
Value: 1024,
|
||||
Usage: "set receive window size(num of packets)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "fec",
|
||||
Value: 4,
|
||||
Usage: "set FEC group size, must be the same as client",
|
||||
Name: "datashard",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
@@ -154,7 +206,7 @@ func main() {
|
||||
Hidden: true,
|
||||
},
|
||||
}
|
||||
myApp.Action = func(c *cli.Context) {
|
||||
myApp.Action = func(c *cli.Context) error {
|
||||
log.Println("version:", VERSION)
|
||||
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
|
||||
switch c.String("mode") {
|
||||
@@ -168,30 +220,52 @@ func main() {
|
||||
nodelay, interval, resend, nc = 1, 10, 2, 1
|
||||
}
|
||||
|
||||
lis, err := kcp.ListenWithOptions(c.Int("fec"), c.String("listen"), []byte(c.String("key")))
|
||||
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
||||
var block kcp.BlockCrypt
|
||||
switch c.String("crypt") {
|
||||
case "tea":
|
||||
block, _ = kcp.NewTEABlockCrypt(pass[:16])
|
||||
case "xor":
|
||||
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
|
||||
case "none":
|
||||
block, _ = kcp.NewNoneBlockCrypt(pass)
|
||||
default:
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
}
|
||||
|
||||
lis, err := kcp.ListenWithOptions(c.String("listen"), block, c.Int("datashard"), c.Int("parityshard"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
log.Println("listening on ", lis.Addr())
|
||||
log.Println("encryption:", c.String("crypt"))
|
||||
log.Println("nodelay parameters:", nodelay, interval, resend, nc)
|
||||
log.Println("sndwnd:", c.Int("sndwnd"), "rcvwnd:", c.Int("rcvwnd"))
|
||||
log.Println("compression:", !c.Bool("nocomp"))
|
||||
log.Println("mtu:", c.Int("mtu"))
|
||||
log.Println("fec:", c.Int("fec"))
|
||||
log.Println("datashard:", c.Int("datashard"), "parityshard:", c.Int("parityshard"))
|
||||
log.Println("acknodelay:", c.Bool("acknodelay"))
|
||||
log.Println("dscp:", c.Int("dscp"))
|
||||
for {
|
||||
if conn, err := lis.Accept(); err == nil {
|
||||
log.Println("remote address:", conn.RemoteAddr())
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetNoDelay(nodelay, interval, resend, nc)
|
||||
conn.SetMtu(c.Int("mtu"))
|
||||
conn.SetWindowSize(c.Int("sndwnd"), c.Int("rcvwnd"))
|
||||
conn.SetACKNoDelay(c.Bool("acknodelay"))
|
||||
conn.SetDSCP(c.Int("dscp"))
|
||||
go handleMux(conn, c.String("target"))
|
||||
|
||||
if c.Bool("nocomp") {
|
||||
go handleMux(conn, c.String("target"))
|
||||
} else {
|
||||
go handleMux(newCompStream(conn), c.String("target"))
|
||||
}
|
||||
} else {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
@@ -12,10 +12,10 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
go sig_handler()
|
||||
go sigHandler()
|
||||
}
|
||||
|
||||
func sig_handler() {
|
||||
func sigHandler() {
|
||||
ch := make(chan os.Signal, 1)
|
||||
signal.Notify(ch, syscall.SIGUSR1)
|
||||
|
||||
|
||||