mirror of
https://github.com/xtaci/kcptun.git
synced 2024-04-21 12:32:32 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13e980cdff |
@@ -1,27 +0,0 @@
|
||||
问问题前先搜索ISSUE,并搞清楚下面的问题:
|
||||
|
||||
1. 检查 ```-key xxx``` 至少三遍, ***保证***两边一致。
|
||||
2. 保证```-nocomp, -datashard, -parityshard, -key, -crypt```两边一致。
|
||||
3. 是否在服务器端,正确设定了转发的目标服务器地址 ***--target***。
|
||||
4. 如果第3条不确定,尝试在服务器上telnet target port试试。
|
||||
5. 防火墙是否关闭了UDP通信。
|
||||
6. 两端的版本是否一致?
|
||||
7. 是不是最新版本?
|
||||
8. 两端分别是什么操作系统?
|
||||
9. 两端的输出日志是什么?
|
||||
|
||||
Before firing issue, make sure you figured out the following common questions.
|
||||
|
||||
PLEASE DO SEARCH FIRST.
|
||||
|
||||
1. Check your ```-key xxx``` for at least 3 times, ***MAKE SURE*** both sides share the same secret.
|
||||
2. ```-nocomp, -datashard, -parityshard, -key, -crypt``` ***must be the same*** on both side.
|
||||
3. Did you correctly set the ***-target*** on the server side?
|
||||
4. ***MAKE SURE*** ```telnet target port``` on your server successful(don't ask me why couldn't).
|
||||
5. Does your ***firewall allows UDP*** communications? (including your ISP Cable-Modem)
|
||||
6. Are you using the **same version** for both client & server
|
||||
7. Are you using the **latest release**?
|
||||
8. Which **OS** do you use?
|
||||
9. Which end for this issue related to, **client or server**?
|
||||
|
||||
|
||||
+3
-5
@@ -1,8 +1,6 @@
|
||||
FROM golang:alpine
|
||||
FROM golang:latest
|
||||
MAINTAINER xtaci <daniel820313@gmail.com>
|
||||
RUN apk update && \
|
||||
apk upgrade && \
|
||||
apk add git
|
||||
RUN go get github.com/xtaci/kcptun/client && go get github.com/xtaci/kcptun/server
|
||||
RUN go get github.com/xtaci/kcptun/client
|
||||
RUN go get github.com/xtaci/kcptun/server
|
||||
EXPOSE 29900/udp
|
||||
EXPOSE 12948
|
||||
|
||||
@@ -1,207 +1,83 @@
|
||||
# <img src="logo.png" alt="kcptun" height="54px" />
|
||||
[![Release][13]][14] [![Powered][17]][18] [![MIT licensed][11]][12] [![Build Status][3]][4] [![Go Report Card][5]][6] [![Downloads][15]][16] [![Docker][1]][2]
|
||||
[1]: https://images.microbadger.com/badges/image/xtaci/kcptun.svg
|
||||
[2]: https://microbadger.com/images/xtaci/kcptun
|
||||
# <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
|
||||
[11]: https://img.shields.io/badge/license-MIT-blue.svg
|
||||
[12]: 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
|
||||
[15]: https://img.shields.io/github/downloads/xtaci/kcptun/total.svg?maxAge=2592000
|
||||
[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+UDP流,:zap:***[官方下载地址](https://github.com/xtaci/kcptun/releases/latest)***:zap:工作示意图:
|
||||
|
||||
<img src="kcptun.png" alt="kcptun" height="300px"/>
|
||||

|
||||
|
||||
-
|
||||
|
||||
### QuickStart
|
||||
|
||||
Download precompiled [Releases](https://github.com/xtaci/kcptun/releases).
|
||||
***kcptun是[kcp-go](https://github.com/xtaci/kcp-go)的一个测试应用,可以用于任意tcp网络程序的传输承载(尤其用于游戏数据传输测试),用于优化丢包环境下的网络流畅度。***
|
||||
|
||||
### *快速设定* :lollipop:
|
||||
```
|
||||
KCP Client: ./client_darwin_amd64 -r "KCP_SERVER_IP:4000" -l ":8388" -mode fast2
|
||||
KCP Server: ./server_linux_amd64 -t "TARGET_IP:8388" -l ":4000" -mode fast2
|
||||
```
|
||||
The above commands will establish port forwarding for 8388/tcp as:
|
||||
|
||||
Application -> KCP Client(8388/tcp) -> KCP Server(4000/udp) -> Server(8388/tcp)
|
||||
|
||||
### Install from source
|
||||
|
||||
```
|
||||
$go get -u github.com/xtaci/kcptun/client
|
||||
$go get -u github.com/xtaci/kcptun/server
|
||||
服务器: ./server_linux_amd64 -t "127.0.0.1:1080" -l ":554" -mode fast2 // 转发到本地1080端口
|
||||
客户端: ./client_darwin_amd64 -r "服务器IP地址:554" -l ":1080" -mode fast2 // 监听本地1080端口
|
||||
```
|
||||
|
||||
All precompiled releases are genereated from `build-release.sh` script.
|
||||
### *使用の方法* :lollipop:
|
||||

|
||||

|
||||
|
||||
### Performance
|
||||
|
||||
<img src="fast.png" alt="fast.com" height="256px" />
|
||||
|
||||
### Basic Tuning Guide
|
||||
|
||||
#### Improving Thoughput
|
||||
|
||||
> **Q: I have a high speed network link, how to reach the maximum bandwidth?**
|
||||
|
||||
> **A:** Increase `-rcvwnd` on KCP Client and `-sndwnd` on KCP Server **simultaneously & gradually**, the mininum one decides the maximum transfer rate of the link, as `wnd * mtu / rtt`; Then try downloading something and to see if it meets your requirements.
|
||||
|
||||
#### Improving Latency
|
||||
|
||||
> **Q: I'm using kcptun for game, I don't want any lag happening.**
|
||||
|
||||
> **A:** Lag means packet loss for most of the time, lags can be improved by changing `-mode`.
|
||||
|
||||
> eg: `-mode fast3`
|
||||
|
||||
> Aggresiveness/Responsiveness on retransmission for embeded modes are:
|
||||
|
||||
> *fast3 > fast2 > fast > normal > default*
|
||||
|
||||
-
|
||||
|
||||
### Expert Tuning Guide
|
||||
|
||||
#### Overview
|
||||
|
||||
<p align="left"><img src="layeredparams.png" alt="params" height="450px"/></p>
|
||||
|
||||
#### Usage
|
||||
### *适用范围限定* :lollipop:
|
||||
1. 实时网络游戏的数据传输
|
||||
2. 跨运营商的流量传输
|
||||
3. 其他高丢包通信链路的TCP承载
|
||||
|
||||
### *推荐参数* :lollipop:
|
||||
```
|
||||
$ ./client_darwin_amd64 -h
|
||||
NAME:
|
||||
kcptun - client(with SMUX)
|
||||
|
||||
USAGE:
|
||||
client_darwin_amd64 [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
20170120
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--localaddr value, -l value local listen address (default: ":12948")
|
||||
--remoteaddr value, -r value kcp server address (default: "vps:29900")
|
||||
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
|
||||
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none (default: "aes")
|
||||
--mode value profiles: fast3, fast2, fast, normal (default: "fast")
|
||||
--conn value set num of UDP connections to server (default: 1)
|
||||
--autoexpire value set auto expiration time(in seconds) for a single UDP connection, 0 to disable (default: 0)
|
||||
--mtu value set maximum transmission unit for UDP packets (default: 1350)
|
||||
--sndwnd value set send window size(num of packets) (default: 128)
|
||||
--rcvwnd value set receive window size(num of packets) (default: 512)
|
||||
--datashard value, --ds value set reed-solomon erasure coding - datashard (default: 10)
|
||||
--parityshard value, --ps value set reed-solomon erasure coding - parityshard (default: 3)
|
||||
--dscp value set DSCP(6bit) (default: 0)
|
||||
--nocomp disable compression
|
||||
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
|
||||
--snmpperiod value snmp collect period, in seconds (default: 60)
|
||||
--log value specify a log file to output, default goes to stderr
|
||||
-c value config from json file, which will override the command from shell
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
|
||||
$ ./server_darwin_amd64 -h
|
||||
NAME:
|
||||
kcptun - server(with SMUX)
|
||||
|
||||
USAGE:
|
||||
server_darwin_amd64 [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
20170120
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS:
|
||||
--listen value, -l value kcp server listen address (default: ":29900")
|
||||
--target value, -t value target server address (default: "127.0.0.1:12948")
|
||||
--key value pre-shared secret between client and server (default: "it's a secrect") [$KCPTUN_KEY]
|
||||
--crypt value aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none (default: "aes")
|
||||
--mode value profiles: fast3, fast2, fast, normal (default: "fast")
|
||||
--mtu value set maximum transmission unit for UDP packets (default: 1350)
|
||||
--sndwnd value set send window size(num of packets) (default: 1024)
|
||||
--rcvwnd value set receive window size(num of packets) (default: 1024)
|
||||
--datashard value, --ds value set reed-solomon erasure coding - datashard (default: 10)
|
||||
--parityshard value, --ps value set reed-solomon erasure coding - parityshard (default: 3)
|
||||
--dscp value set DSCP(6bit) (default: 0)
|
||||
--nocomp disable compression
|
||||
--snmplog value collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log
|
||||
--snmpperiod value snmp collect period, in seconds (default: 60)
|
||||
--log value specify a log file to output, default goes to stderr
|
||||
-c value config from json file, which will override the command from shell
|
||||
--help, -h show help
|
||||
--version, -v print the version
|
||||
适用大部分ADSL接入(非对称上下行)的参数(实验环境电信100M ADSL)
|
||||
SERVER: -mtu 1400 -sndwnd 2048 -rcvwnd 2048 -mode fast2
|
||||
CLIENT: -mtu 1400 -sndwnd 256 -rcvwnd 2048 -mode fast2 -dscp 46
|
||||
```
|
||||
|
||||
#### Forward Error Correction
|
||||
*简易调优方法*:
|
||||
> 第一步:同时在两端逐步增大client rcvwnd和server sndwnd;
|
||||
> 第二步:尝试下载,观察如果带宽利用率接近物理带宽则停止,否则跳转到第一步。
|
||||
|
||||
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.
|
||||
### *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),至于有没有用,完全取决于数据包经过的设备。
|
||||
|
||||
### *内置模式* :lollipop:
|
||||
响应速度:
|
||||
*fast3 >* ***[fast2]*** *> fast > normal > default*
|
||||
有效载荷比:
|
||||
*default > normal > fast >* ***[fast2]*** *> fast3*
|
||||
中间mode参数比较均衡,总之就是越快越浪费带宽,推荐模式 ***fast2***
|
||||
更高级的 ***手动档*** 需要理解KCP协议,并通过 ***隐藏参数*** 调整,例如:
|
||||
```
|
||||
-mode manual -nodelay 1 -resend 2 -nc 1 -interval 20
|
||||
```
|
||||
|
||||
### *前向纠错* :lollipop:
|
||||
前向纠错采用Reed Solomon纠删码, 它的基本原理如下: 给定n个数据块d1, d2,…, dn,n和一个正整数m, RS根据n个数据块生成m个校验块, c1, c2,…, cm。 对于任意的n和m, 从n个原始数据块和m 个校验块中任取n块就能解码出原始数据, 即RS最多容忍m个数据块或者校验块同时丢失。
|
||||
|
||||

|
||||
|
||||
Setting parameters of RS-Code with ```-datashard m -parityshard n``` on both KCP Client & KCP Server.
|
||||
|
||||
#### DSCP
|
||||
|
||||
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.
|
||||
|
||||
setting each side with ```-dscp value```, Here are some [Commonly used DSCP values](https://en.wikipedia.org/wiki/Differentiated_services#Commonly_used_DSCP_values).
|
||||
|
||||
#### Security
|
||||
|
||||
No matter what encryption you are using for application layer, if you specify ```-crypt none``` to kcptun,
|
||||
the header will be ***PLAINTEXT*** to everyone; I suggest ```-crypt aes-128``` for encryption at least .
|
||||
|
||||
`-crypt` and `-key` must be the same on both KCP Client & KCP Server.
|
||||
|
||||
NOTICE: ```-crypt xor``` is also insecure, do not use this unless you know what you are doing.
|
||||
|
||||
Benchmarks for crypto algorithms supported by kcptun:
|
||||
|
||||
```
|
||||
BenchmarkAES128-4 200000 11182 ns/op
|
||||
BenchmarkAES192-4 200000 12699 ns/op
|
||||
BenchmarkAES256-4 100000 13757 ns/op
|
||||
BenchmarkTEA-4 50000 26441 ns/op
|
||||
BenchmarkSimpleXOR-4 3000000 441 ns/op
|
||||
BenchmarkBlowfish-4 30000 48036 ns/op
|
||||
BenchmarkNone-4 20000000 106 ns/op
|
||||
BenchmarkCast5-4 20000 60222 ns/op
|
||||
BenchmarkTripleDES-4 2000 878759 ns/op
|
||||
BenchmarkTwofish-4 20000 68501 ns/op
|
||||
BenchmarkXTEA-4 20000 77417 ns/op
|
||||
BenchmarkSalsa20-4 300000 4998 ns/op
|
||||
```
|
||||
|
||||
|
||||
|
||||
#### Memory Control
|
||||
|
||||
Routers, mobile devices are sensitive to memory consumption; by setting GOGC environment(eg: GOGC=20) will lower memory consumption.
|
||||
Reference: https://blog.golang.org/go15gc
|
||||
|
||||
#### Compression
|
||||
|
||||
kcptun has builtin snappy algorithms for compressing streams:
|
||||
通过```-datashard 10 -parityshard 3``` 可以调整Reed Solomon参数。
|
||||
|
||||
### *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,
|
||||
@@ -209,66 +85,52 @@ kcptun has builtin snappy algorithms for compressing streams:
|
||||
> for most inputs, but the resulting compressed files are anywhere from 20% to
|
||||
> 100% bigger.
|
||||
|
||||
> Reference: http://google.github.io/snappy/
|
||||
|
||||
Compression may save bandwidth for **PLAINTEXT** data, such as HTTP data.
|
||||
|
||||
Compression is enabled by default, you can disable it by setting ```-nocomp``` on both KCP Client & KCP Server.
|
||||
|
||||
#### SNMP
|
||||
Reference: http://google.github.io/snappy/
|
||||
|
||||
### *SNMP* :lollipop:
|
||||
```go
|
||||
// Snmp defines network statistics indicator
|
||||
type Snmp struct {
|
||||
BytesSent uint64 // raw bytes sent
|
||||
BytesReceived uint64
|
||||
MaxConn uint64
|
||||
ActiveOpens uint64
|
||||
PassiveOpens uint64
|
||||
CurrEstab uint64 // count of connections for now
|
||||
InErrs uint64 // udp read errors
|
||||
InCsumErrors uint64 // checksum errors from CRC32
|
||||
KCPInErrors uint64 // packet iput errors from kcp
|
||||
InSegs uint64
|
||||
OutSegs uint64
|
||||
InBytes uint64 // udp bytes received
|
||||
OutBytes uint64 // udp bytes sent
|
||||
RetransSegs uint64
|
||||
FastRetransSegs uint64
|
||||
EarlyRetransSegs uint64
|
||||
LostSegs uint64 // number of segs infered as lost
|
||||
RepeatSegs uint64 // number of segs duplicated
|
||||
FECRecovered uint64 // correct packets recovered from FEC
|
||||
FECErrs uint64 // incorrect packets recovered from FEC
|
||||
FECSegs uint64 // FEC segments received
|
||||
FECShortShards uint64 // number of data shards that's not enough for recovery
|
||||
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 `SIGUSR1` signal to KCP Client or KCP Server will dump SNMP information to console, just like `/proc/net/snmp`. You can use this information to do fine-grained tuning.
|
||||
使用```kill -SIGUSR1 pid``` 可以在控制台打印出SNMP信息,通常用于精细调整***当前链路的有效载荷比***。
|
||||
观察```RetransSegs,FastRetransSegs,LostSegs,OutSegs```这几者的数值比例,用于参考调整```-mode manual,fec```的参数。
|
||||
|
||||
### Manual Control
|
||||
### *性能对比* :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
|
||||
```
|
||||
|
||||
https://github.com/skywind3000/kcp/blob/master/README.en.md#protocol-configuration
|
||||
### *免责申明* :warning:
|
||||
用户以各种方式使用本软件(包括但不限于修改使用、直接使用、通过第三方使用)的过程中,不得以任何方式利用本软件直接或间接从事违反中国法律、以及社会公德的行为。软件的使用者需对自身行为负责,因使用软件引发的一切纠纷,由使用者承担全部法律及连带责任。作者不承担任何法律及连带责任。
|
||||
|
||||
`-mode manual -nodelay 1 -interval 20 -resend 2 -nc 1`
|
||||
|
||||
Low-level KCP configuration can be altered by using manual mode like above, make sure you really **UNDERSTAND** what these means before doing **ANY** manual settings.
|
||||
|
||||
### References
|
||||
|
||||
1. https://github.com/skywind3000/kcp -- KCP - A Fast and Reliable ARQ Protocol.
|
||||
1. https://github.com/xtaci/kcp-go/ -- A Production-Grade Reliable-UDP Library for golang
|
||||
1. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go.
|
||||
1. https://en.wikipedia.org/wiki/Differentiated_services -- DSCP.
|
||||
1. http://google.github.io/snappy/ -- A fast compressor/decompressor.
|
||||
1. https://www.backblaze.com/blog/reed-solomon/ -- Reed-Solomon Explained.
|
||||
1. http://www.qualcomm.cn/products/raptorq -- RaptorQ Forward Error Correction Scheme for Object Delivery.
|
||||
1. https://en.wikipedia.org/wiki/PBKDF2 -- Key stretching.
|
||||
1. http://blog.appcanary.com/2016/encrypt-or-compress.html -- Should you encrypt or compress first?
|
||||
1. https://github.com/hashicorp/yamux -- Connection multiplexing library.
|
||||
1. https://tools.ietf.org/html/rfc6937 -- Proportional Rate Reduction for TCP.
|
||||
1. https://tools.ietf.org/html/rfc5827 -- Early Retransmit for TCP and Stream Control Transmission Protocol (SCTP).
|
||||
1. http://http2.github.io/ -- What is HTTP/2?
|
||||
1. http://www.lartc.org/ -- Linux Advanced Routing & Traffic Control
|
||||
1. https://en.wikipedia.org/wiki/Noisy-channel_coding_theorem -- Noisy channel coding theorem
|
||||
对免责声明的解释、修改及更新权均属于作者本人所有。
|
||||
|
||||
+10
-28
@@ -1,13 +1,8 @@
|
||||
#!/bin/bash
|
||||
sum="sha1sum"
|
||||
|
||||
if ! hash sha1sum 2>/dev/null; then
|
||||
if ! hash shasum 2>/dev/null; then
|
||||
echo "I can't see 'sha1sum' or 'shasum'"
|
||||
echo "Please install one of them!"
|
||||
exit
|
||||
fi
|
||||
sum="shasum"
|
||||
MD5='md5sum'
|
||||
unamestr=`uname`
|
||||
if [[ "$unamestr" == 'Darwin' ]]; then
|
||||
MD5='md5'
|
||||
fi
|
||||
|
||||
UPX=false
|
||||
@@ -17,7 +12,6 @@ fi
|
||||
|
||||
VERSION=`date -u +%Y%m%d`
|
||||
LDFLAGS="-X main.VERSION=$VERSION -s -w"
|
||||
GCFLAGS=""
|
||||
|
||||
OSES=(linux darwin windows freebsd)
|
||||
ARCHS=(amd64 386)
|
||||
@@ -28,32 +22,20 @@ for os in ${OSES[@]}; do
|
||||
then
|
||||
suffix=".exe"
|
||||
fi
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_${os}_${arch}${suffix} github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=$os GOARCH=$arch go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_${os}_${arch}${suffix} github.com/xtaci/kcptun/server
|
||||
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
|
||||
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}
|
||||
$sum kcptun-${os}-${arch}-$VERSION.tar.gz
|
||||
$MD5 kcptun-${os}-${arch}-$VERSION.tar.gz
|
||||
done
|
||||
done
|
||||
|
||||
# ARM
|
||||
ARMS=(5 6 7)
|
||||
for v in ${ARMS[@]}; do
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_arm$v github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=$v go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_arm$v github.com/xtaci/kcptun/server
|
||||
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
|
||||
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*
|
||||
$sum kcptun-linux-arm-$VERSION.tar.gz
|
||||
|
||||
#MIPS32LE
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mipsle github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mipsle github.com/xtaci/kcptun/server
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o client_linux_mips github.com/xtaci/kcptun/client
|
||||
env CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "$LDFLAGS" -gcflags "$GCFLAGS" -o server_linux_mips github.com/xtaci/kcptun/server
|
||||
|
||||
if $UPX; then upx -9 client_linux_mips* server_linux_mips*;fi
|
||||
tar -zcf kcptun-linux-mipsle-$VERSION.tar.gz client_linux_mipsle server_linux_mipsle
|
||||
tar -zcf kcptun-linux-mips-$VERSION.tar.gz client_linux_mips server_linux_mips
|
||||
$sum kcptun-linux-mipsle-$VERSION.tar.gz
|
||||
$sum kcptun-linux-mips-$VERSION.tar.gz
|
||||
$MD5 kcptun-linux-arm-$VERSION.tar.gz
|
||||
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 66 KiB |
@@ -1,45 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Config for client
|
||||
type Config struct {
|
||||
LocalAddr string `json:"localaddr"`
|
||||
RemoteAddr string `json:"remoteaddr"`
|
||||
Key string `json:"key"`
|
||||
Crypt string `json:"crypt"`
|
||||
Mode string `json:"mode"`
|
||||
Conn int `json:"conn"`
|
||||
AutoExpire int `json:"autoexpire"`
|
||||
ScavengeTTL int `json:"scavengettl"`
|
||||
MTU int `json:"mtu"`
|
||||
SndWnd int `json:"sndwnd"`
|
||||
RcvWnd int `json:"rcvwnd"`
|
||||
DataShard int `json:"datashard"`
|
||||
ParityShard int `json:"parityshard"`
|
||||
DSCP int `json:"dscp"`
|
||||
NoComp bool `json:"nocomp"`
|
||||
AckNodelay bool `json:"acknodelay"`
|
||||
NoDelay int `json:"nodelay"`
|
||||
Interval int `json:"interval"`
|
||||
Resend int `json:"resend"`
|
||||
NoCongestion int `json:"nc"`
|
||||
SockBuf int `json:"sockbuf"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
Log string `json:"log"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
SnmpPeriod int `json:"snmpperiod"`
|
||||
}
|
||||
|
||||
func parseJSONConfig(config *Config, path string) error {
|
||||
file, err := os.Open(path) // For read access.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return json.NewDecoder(file).Decode(config)
|
||||
}
|
||||
+101
-324
@@ -2,23 +2,19 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/hashicorp/yamux"
|
||||
"github.com/urfave/cli"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/smux"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -28,11 +24,6 @@ var (
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
// global recycle buffer
|
||||
var copyBuf sync.Pool
|
||||
|
||||
const bufSize = 4096
|
||||
|
||||
type compStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
@@ -61,31 +52,23 @@ func newCompStream(conn net.Conn) *compStream {
|
||||
return c
|
||||
}
|
||||
|
||||
func handleClient(sess *smux.Session, p1 io.ReadWriteCloser) {
|
||||
func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
log.Println("stream opened")
|
||||
defer log.Println("stream closed")
|
||||
defer p1.Close()
|
||||
p2, err := sess.OpenStream()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer p2.Close()
|
||||
|
||||
// start tunnel
|
||||
p1die := make(chan struct{})
|
||||
go func() {
|
||||
buf := copyBuf.Get().([]byte)
|
||||
io.CopyBuffer(p1, p2, buf)
|
||||
io.Copy(p1, p2)
|
||||
close(p1die)
|
||||
copyBuf.Put(buf)
|
||||
}()
|
||||
|
||||
p2die := make(chan struct{})
|
||||
go func() {
|
||||
buf := copyBuf.Get().([]byte)
|
||||
io.CopyBuffer(p2, p1, buf)
|
||||
io.Copy(p2, p1)
|
||||
close(p2die)
|
||||
copyBuf.Put(buf)
|
||||
}()
|
||||
|
||||
// wait for tunnel termination
|
||||
@@ -97,23 +80,16 @@ func handleClient(sess *smux.Session, p1 io.ReadWriteCloser) {
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Printf("%+v\n", err)
|
||||
log.Println(err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
copyBuf.New = func() interface{} {
|
||||
return make([]byte, bufSize)
|
||||
}
|
||||
if VERSION == "SELFBUILD" {
|
||||
// add more log flags for debugging
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
myApp := cli.NewApp()
|
||||
myApp.Name = "kcptun"
|
||||
myApp.Usage = "client(with SMUX)"
|
||||
myApp.Usage = "kcptun client"
|
||||
myApp.Version = VERSION
|
||||
myApp.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@@ -129,38 +105,28 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "it's a secrect",
|
||||
Usage: "pre-shared secret between client and server",
|
||||
Usage: "key for communcation, must be the same as kcptun server",
|
||||
EnvVar: "KCPTUN_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none",
|
||||
Usage: "methods for encryption: aes, tea, xor, none",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
Value: "fast",
|
||||
Usage: "profiles: fast3, fast2, fast, normal, manual",
|
||||
Usage: "mode for communication: fast3, fast2, fast, normal",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "conn",
|
||||
Value: 1,
|
||||
Usage: "set num of UDP connections to server",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "autoexpire",
|
||||
Value: 0,
|
||||
Usage: "set auto expiration time(in seconds) for a single UDP connection, 0 to disable",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "scavengettl",
|
||||
Value: 600,
|
||||
Usage: "set how long an expired connection can live(in sec), -1 to disable",
|
||||
Usage: "establish N physical connections as specified by 'conn' to server",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
Value: 1350,
|
||||
Usage: "set maximum transmission unit for UDP packets",
|
||||
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sndwnd",
|
||||
@@ -169,32 +135,31 @@ func main() {
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "rcvwnd",
|
||||
Value: 512,
|
||||
Value: 1024,
|
||||
Usage: "set receive window size(num of packets)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "datashard,ds",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard,ps",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
Value: 0,
|
||||
Usage: "set DSCP(6bit)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.IntFlag{
|
||||
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",
|
||||
Hidden: true,
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
Value: 0,
|
||||
Usage: "set DSCP(6bit)",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "nodelay",
|
||||
@@ -203,7 +168,7 @@ func main() {
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "interval",
|
||||
Value: 50,
|
||||
Value: 40,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
@@ -216,290 +181,102 @@ func main() {
|
||||
Value: 0,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sockbuf",
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "snmplog",
|
||||
Value: "",
|
||||
Usage: "collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "snmpperiod",
|
||||
Value: 60,
|
||||
Usage: "snmp collect period, in seconds",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "log",
|
||||
Value: "",
|
||||
Usage: "specify a log file to output, default goes to stderr",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "c",
|
||||
Value: "", // when the value is not empty, the config path must exists
|
||||
Usage: "config from json file, which will override the command from shell",
|
||||
},
|
||||
}
|
||||
myApp.Action = func(c *cli.Context) error {
|
||||
config := Config{}
|
||||
config.LocalAddr = c.String("localaddr")
|
||||
config.RemoteAddr = c.String("remoteaddr")
|
||||
config.Key = c.String("key")
|
||||
config.Crypt = c.String("crypt")
|
||||
config.Mode = c.String("mode")
|
||||
config.Conn = c.Int("conn")
|
||||
config.AutoExpire = c.Int("autoexpire")
|
||||
config.ScavengeTTL = c.Int("scavengettl")
|
||||
config.MTU = c.Int("mtu")
|
||||
config.SndWnd = c.Int("sndwnd")
|
||||
config.RcvWnd = c.Int("rcvwnd")
|
||||
config.DataShard = c.Int("datashard")
|
||||
config.ParityShard = c.Int("parityshard")
|
||||
config.DSCP = c.Int("dscp")
|
||||
config.NoComp = c.Bool("nocomp")
|
||||
config.AckNodelay = c.Bool("acknodelay")
|
||||
config.NoDelay = c.Int("nodelay")
|
||||
config.Interval = c.Int("interval")
|
||||
config.Resend = c.Int("resend")
|
||||
config.NoCongestion = c.Int("nc")
|
||||
config.SockBuf = c.Int("sockbuf")
|
||||
config.KeepAlive = c.Int("keepalive")
|
||||
config.Log = c.String("log")
|
||||
config.SnmpLog = c.String("snmplog")
|
||||
config.SnmpPeriod = c.Int("snmpperiod")
|
||||
|
||||
if c.String("c") != "" {
|
||||
err := parseJSONConfig(&config, c.String("c"))
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// log redirect
|
||||
if config.Log != "" {
|
||||
f, err := os.OpenFile(config.Log, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
checkError(err)
|
||||
defer f.Close()
|
||||
log.SetOutput(f)
|
||||
}
|
||||
|
||||
switch config.Mode {
|
||||
case "normal":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 50, 2, 1
|
||||
case "fast":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 40, 2, 1
|
||||
case "fast2":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 30, 2, 1
|
||||
case "fast3":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1
|
||||
}
|
||||
|
||||
myApp.Action = func(c *cli.Context) {
|
||||
log.Println("version:", VERSION)
|
||||
addr, err := net.ResolveTCPAddr("tcp", config.LocalAddr)
|
||||
addr, err := net.ResolveTCPAddr("tcp", c.String("localaddr"))
|
||||
checkError(err)
|
||||
listener, err := net.ListenTCP("tcp", addr)
|
||||
checkError(err)
|
||||
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
||||
|
||||
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
|
||||
var block kcp.BlockCrypt
|
||||
switch config.Crypt {
|
||||
case "tea":
|
||||
block, _ = kcp.NewTEABlockCrypt(pass[:16])
|
||||
case "xor":
|
||||
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
|
||||
case "none":
|
||||
block, _ = kcp.NewNoneBlockCrypt(pass)
|
||||
case "aes-128":
|
||||
block, _ = kcp.NewAESBlockCrypt(pass[:16])
|
||||
case "aes-192":
|
||||
block, _ = kcp.NewAESBlockCrypt(pass[:24])
|
||||
case "blowfish":
|
||||
block, _ = kcp.NewBlowfishBlockCrypt(pass)
|
||||
case "twofish":
|
||||
block, _ = kcp.NewTwofishBlockCrypt(pass)
|
||||
case "cast5":
|
||||
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
|
||||
case "3des":
|
||||
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
|
||||
case "xtea":
|
||||
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
|
||||
case "salsa20":
|
||||
block, _ = kcp.NewSalsa20BlockCrypt(pass)
|
||||
default:
|
||||
config.Crypt = "aes"
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
// kcp server
|
||||
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
|
||||
|
||||
switch c.String("mode") {
|
||||
case "normal":
|
||||
nodelay, interval, resend, nc = 0, 30, 2, 1
|
||||
case "fast":
|
||||
nodelay, interval, resend, nc = 0, 20, 2, 1
|
||||
case "fast2":
|
||||
nodelay, interval, resend, nc = 1, 20, 2, 1
|
||||
case "fast3":
|
||||
nodelay, interval, resend, nc = 1, 10, 2, 1
|
||||
}
|
||||
|
||||
log.Println("listening on:", listener.Addr())
|
||||
log.Println("encryption:", config.Crypt)
|
||||
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
log.Println("remote address:", config.RemoteAddr)
|
||||
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
|
||||
log.Println("compression:", !config.NoComp)
|
||||
log.Println("mtu:", config.MTU)
|
||||
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
|
||||
log.Println("acknodelay:", config.AckNodelay)
|
||||
log.Println("dscp:", config.DSCP)
|
||||
log.Println("sockbuf:", config.SockBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("conn:", config.Conn)
|
||||
log.Println("autoexpire:", config.AutoExpire)
|
||||
log.Println("scavengettl:", config.ScavengeTTL)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
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("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"))
|
||||
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SockBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
createConn := func() (*smux.Session, error) {
|
||||
kcpconn, err := kcp.DialWithOptions(config.RemoteAddr, block, config.DataShard, config.ParityShard)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "createConn()")
|
||||
}
|
||||
kcpconn.SetStreamMode(true)
|
||||
kcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
kcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
kcpconn.SetMtu(config.MTU)
|
||||
kcpconn.SetACKNoDelay(config.AckNodelay)
|
||||
|
||||
if err := kcpconn.SetDSCP(config.DSCP); err != nil {
|
||||
log.Println("SetDSCP:", err)
|
||||
}
|
||||
if err := kcpconn.SetReadBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetReadBuffer:", err)
|
||||
}
|
||||
if err := kcpconn.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
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.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 session *smux.Session
|
||||
if config.NoComp {
|
||||
session, err = smux.Client(kcpconn, smuxConfig)
|
||||
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 = smux.Client(newCompStream(kcpconn), smuxConfig)
|
||||
session, err = yamux.Client(newCompStream(kcpconn), config)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "createConn()")
|
||||
}
|
||||
log.Println("connection:", kcpconn.LocalAddr(), "->", kcpconn.RemoteAddr())
|
||||
return session, nil
|
||||
checkError(err)
|
||||
return session
|
||||
}
|
||||
|
||||
// wait until a connection is ready
|
||||
waitConn := func() *smux.Session {
|
||||
for {
|
||||
if session, err := createConn(); err == nil {
|
||||
return session
|
||||
} else {
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
}
|
||||
numconn := uint16(c.Int("conn"))
|
||||
var muxes []*yamux.Session
|
||||
for i := uint16(0); i < numconn; i++ {
|
||||
muxes = append(muxes, createConn())
|
||||
}
|
||||
|
||||
numconn := uint16(config.Conn)
|
||||
muxes := make([]struct {
|
||||
session *smux.Session
|
||||
ttl time.Time
|
||||
}, numconn)
|
||||
|
||||
for k := range muxes {
|
||||
muxes[k].session = waitConn()
|
||||
muxes[k].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
}
|
||||
|
||||
chScavenger := make(chan *smux.Session, 128)
|
||||
go scavenger(chScavenger, config.ScavengeTTL)
|
||||
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
rr := uint16(0)
|
||||
for {
|
||||
p1, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
checkError(err)
|
||||
idx := rr % numconn
|
||||
|
||||
// do auto expiration && reconnection
|
||||
if muxes[idx].session.IsClosed() || (config.AutoExpire > 0 && time.Now().After(muxes[idx].ttl)) {
|
||||
chScavenger <- muxes[idx].session
|
||||
muxes[idx].session = waitConn()
|
||||
muxes[idx].ttl = time.Now().Add(time.Duration(config.AutoExpire) * time.Second)
|
||||
mux := muxes[rr%numconn]
|
||||
p2, err := mux.Open()
|
||||
if err != nil { // yamux failure
|
||||
log.Println(err)
|
||||
p1.Close()
|
||||
mux.Close()
|
||||
muxes[rr%numconn] = createConn()
|
||||
continue
|
||||
}
|
||||
|
||||
go handleClient(muxes[idx].session, p1)
|
||||
go handleClient(p1, p2)
|
||||
rr++
|
||||
}
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
type scavengeSession struct {
|
||||
session *smux.Session
|
||||
ts time.Time
|
||||
}
|
||||
|
||||
func scavenger(ch chan *smux.Session, ttl int) {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
var sessionList []scavengeSession
|
||||
for {
|
||||
select {
|
||||
case sess := <-ch:
|
||||
sessionList = append(sessionList, scavengeSession{sess, time.Now()})
|
||||
log.Println("session marked as expired")
|
||||
case <-ticker.C:
|
||||
var newList []scavengeSession
|
||||
for k := range sessionList {
|
||||
s := sessionList[k]
|
||||
if s.session.NumStreams() == 0 || s.session.IsClosed() {
|
||||
log.Println("session normally closed")
|
||||
s.session.Close()
|
||||
} else if ttl >= 0 && time.Since(s.ts) >= time.Duration(ttl)*time.Second {
|
||||
log.Println("session reached scavenge ttl")
|
||||
s.session.Close()
|
||||
} else {
|
||||
newList = append(newList, sessionList[k])
|
||||
}
|
||||
}
|
||||
sessionList = newList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func snmpLogger(path string, interval int) {
|
||||
if path == "" || interval == 0 {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
f, err := os.OpenFile(time.Now().Format(path), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
w := csv.NewWriter(f)
|
||||
// write header in empty file
|
||||
if stat, err := f.Stat(); err == nil && stat.Size() == 0 {
|
||||
if err := w.Write(append([]string{"Unix"}, kcp.DefaultSnmp.Header()...)); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
kcp.DefaultSnmp.Reset()
|
||||
w.Flush()
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 33 KiB After Width: | Height: | Size: 20 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 59 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 6.8 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
@@ -1,43 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Config for server
|
||||
type Config struct {
|
||||
Listen string `json:"listen"`
|
||||
Target string `json:"target"`
|
||||
Key string `json:"key"`
|
||||
Crypt string `json:"crypt"`
|
||||
Mode string `json:"mode"`
|
||||
MTU int `json:"mtu"`
|
||||
SndWnd int `json:"sndwnd"`
|
||||
RcvWnd int `json:"rcvwnd"`
|
||||
DataShard int `json:"datashard"`
|
||||
ParityShard int `json:"parityshard"`
|
||||
DSCP int `json:"dscp"`
|
||||
NoComp bool `json:"nocomp"`
|
||||
AckNodelay bool `json:"acknodelay"`
|
||||
NoDelay int `json:"nodelay"`
|
||||
Interval int `json:"interval"`
|
||||
Resend int `json:"resend"`
|
||||
NoCongestion int `json:"nc"`
|
||||
SockBuf int `json:"sockbuf"`
|
||||
KeepAlive int `json:"keepalive"`
|
||||
Log string `json:"log"`
|
||||
SnmpLog string `json:"snmplog"`
|
||||
SnmpPeriod int `json:"snmpperiod"`
|
||||
Pprof bool `json:"pprof"`
|
||||
}
|
||||
|
||||
func parseJSONConfig(config *Config, path string) error {
|
||||
file, err := os.Open(path) // For read access.
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
return json.NewDecoder(file).Decode(config)
|
||||
}
|
||||
+73
-236
@@ -2,24 +2,19 @@ package main
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/pbkdf2"
|
||||
|
||||
"github.com/golang/snappy"
|
||||
"github.com/hashicorp/yamux"
|
||||
"github.com/urfave/cli"
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/smux"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -29,11 +24,6 @@ var (
|
||||
SALT = "kcp-go"
|
||||
)
|
||||
|
||||
// global recycle buffer
|
||||
var copyBuf sync.Pool
|
||||
|
||||
const bufSize = 4096
|
||||
|
||||
type compStream struct {
|
||||
conn net.Conn
|
||||
w *snappy.Writer
|
||||
@@ -63,29 +53,35 @@ func newCompStream(conn net.Conn) *compStream {
|
||||
}
|
||||
|
||||
// handle multiplex-ed connection
|
||||
func handleMux(conn io.ReadWriteCloser, config *Config) {
|
||||
func handleMux(conn io.ReadWriteCloser, target string) {
|
||||
// stream multiplex
|
||||
smuxConfig := smux.DefaultConfig()
|
||||
smuxConfig.MaxReceiveBuffer = config.SockBuf
|
||||
smuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second
|
||||
|
||||
mux, err := smux.Server(conn, smuxConfig)
|
||||
var mux *yamux.Session
|
||||
config := &yamux.Config{
|
||||
AcceptBacklog: 256,
|
||||
EnableKeepAlive: true,
|
||||
KeepAliveInterval: 30 * time.Second,
|
||||
ConnectionWriteTimeout: 30 * time.Second,
|
||||
MaxStreamWindowSize: 16777216,
|
||||
LogOutput: os.Stderr,
|
||||
}
|
||||
m, err := yamux.Server(conn, config)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
mux = m
|
||||
defer mux.Close()
|
||||
|
||||
for {
|
||||
p1, err := mux.AcceptStream()
|
||||
p1, err := mux.Accept()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
p2, err := net.DialTimeout("tcp", config.Target, 5*time.Second)
|
||||
p2, err := net.DialTimeout("tcp", target, 5*time.Second)
|
||||
if err != nil {
|
||||
p1.Close()
|
||||
log.Println(err)
|
||||
continue
|
||||
return
|
||||
}
|
||||
go handleClient(p1, p2)
|
||||
}
|
||||
@@ -100,18 +96,14 @@ func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
// start tunnel
|
||||
p1die := make(chan struct{})
|
||||
go func() {
|
||||
buf := copyBuf.Get().([]byte)
|
||||
io.CopyBuffer(p1, p2, buf)
|
||||
io.Copy(p1, p2)
|
||||
close(p1die)
|
||||
copyBuf.Put(buf)
|
||||
}()
|
||||
|
||||
p2die := make(chan struct{})
|
||||
go func() {
|
||||
buf := copyBuf.Get().([]byte)
|
||||
io.CopyBuffer(p2, p1, buf)
|
||||
io.Copy(p2, p1)
|
||||
close(p2die)
|
||||
copyBuf.Put(buf)
|
||||
}()
|
||||
|
||||
// wait for tunnel termination
|
||||
@@ -121,25 +113,11 @@ func handleClient(p1, p2 io.ReadWriteCloser) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(err error) {
|
||||
if err != nil {
|
||||
log.Printf("%+v\n", err)
|
||||
os.Exit(-1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
rand.Seed(int64(time.Now().Nanosecond()))
|
||||
copyBuf.New = func() interface{} {
|
||||
return make([]byte, bufSize)
|
||||
}
|
||||
if VERSION == "SELFBUILD" {
|
||||
// add more log flags for debugging
|
||||
log.SetFlags(log.LstdFlags | log.Lshortfile)
|
||||
}
|
||||
myApp := cli.NewApp()
|
||||
myApp.Name = "kcptun"
|
||||
myApp.Usage = "server(with SMUX)"
|
||||
myApp.Usage = "kcptun server"
|
||||
myApp.Version = VERSION
|
||||
myApp.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
@@ -155,23 +133,23 @@ func main() {
|
||||
cli.StringFlag{
|
||||
Name: "key",
|
||||
Value: "it's a secrect",
|
||||
Usage: "pre-shared secret between client and server",
|
||||
Usage: "key for communcation, must be the same as kcptun client",
|
||||
EnvVar: "KCPTUN_KEY",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "crypt",
|
||||
Value: "aes",
|
||||
Usage: "aes, aes-128, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none",
|
||||
Usage: "methods for encryption: aes, tea, xor, none",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "mode",
|
||||
Value: "fast",
|
||||
Usage: "profiles: fast3, fast2, fast, normal, manual",
|
||||
Usage: "mode for communication: fast3, fast2, fast, normal",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "mtu",
|
||||
Value: 1350,
|
||||
Usage: "set maximum transmission unit for UDP packets",
|
||||
Usage: "set MTU of UDP packets, suggest 'tracepath' to discover path mtu",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sndwnd",
|
||||
@@ -183,30 +161,29 @@ func main() {
|
||||
Value: 1024,
|
||||
Usage: "set receive window size(num of packets)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "datashard,ds",
|
||||
Name: "datashard",
|
||||
Value: 10,
|
||||
Usage: "set reed-solomon erasure coding - datashard",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "parityshard,ps",
|
||||
Name: "parityshard",
|
||||
Value: 3,
|
||||
Usage: "set reed-solomon erasure coding - parityshard",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "dscp",
|
||||
Value: 0,
|
||||
Usage: "set DSCP(6bit)",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "nocomp",
|
||||
Usage: "disable compression",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "acknodelay",
|
||||
Usage: "flush ack immediately when a packet is received",
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "nodelay",
|
||||
Value: 0,
|
||||
@@ -214,7 +191,7 @@ func main() {
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "interval",
|
||||
Value: 50,
|
||||
Value: 40,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
@@ -227,205 +204,65 @@ func main() {
|
||||
Value: 0,
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sockbuf",
|
||||
Value: 4194304, // socket buffer size in bytes
|
||||
Hidden: true,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "keepalive",
|
||||
Value: 10, // nat keepalive interval in seconds
|
||||
Hidden: true,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "snmplog",
|
||||
Value: "",
|
||||
Usage: "collect snmp to file, aware of timeformat in golang, like: ./snmp-20060102.log",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "snmpperiod",
|
||||
Value: 60,
|
||||
Usage: "snmp collect period, in seconds",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "pprof",
|
||||
Usage: "start profiling server on :6060",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "log",
|
||||
Value: "",
|
||||
Usage: "specify a log file to output, default goes to stderr",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "c",
|
||||
Value: "", // when the value is not empty, the config path must exists
|
||||
Usage: "config from json file, which will override the command from shell",
|
||||
},
|
||||
}
|
||||
myApp.Action = func(c *cli.Context) error {
|
||||
config := Config{}
|
||||
config.Listen = c.String("listen")
|
||||
config.Target = c.String("target")
|
||||
config.Key = c.String("key")
|
||||
config.Crypt = c.String("crypt")
|
||||
config.Mode = c.String("mode")
|
||||
config.MTU = c.Int("mtu")
|
||||
config.SndWnd = c.Int("sndwnd")
|
||||
config.RcvWnd = c.Int("rcvwnd")
|
||||
config.DataShard = c.Int("datashard")
|
||||
config.ParityShard = c.Int("parityshard")
|
||||
config.DSCP = c.Int("dscp")
|
||||
config.NoComp = c.Bool("nocomp")
|
||||
config.AckNodelay = c.Bool("acknodelay")
|
||||
config.NoDelay = c.Int("nodelay")
|
||||
config.Interval = c.Int("interval")
|
||||
config.Resend = c.Int("resend")
|
||||
config.NoCongestion = c.Int("nc")
|
||||
config.SockBuf = c.Int("sockbuf")
|
||||
config.KeepAlive = c.Int("keepalive")
|
||||
config.Log = c.String("log")
|
||||
config.SnmpLog = c.String("snmplog")
|
||||
config.SnmpPeriod = c.Int("snmpperiod")
|
||||
config.Pprof = c.Bool("pprof")
|
||||
|
||||
if c.String("c") != "" {
|
||||
//Now only support json config file
|
||||
err := parseJSONConfig(&config, c.String("c"))
|
||||
checkError(err)
|
||||
}
|
||||
|
||||
// log redirect
|
||||
if config.Log != "" {
|
||||
f, err := os.OpenFile(config.Log, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
checkError(err)
|
||||
defer f.Close()
|
||||
log.SetOutput(f)
|
||||
}
|
||||
|
||||
switch config.Mode {
|
||||
case "normal":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 50, 2, 1
|
||||
case "fast":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 40, 2, 1
|
||||
case "fast2":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 30, 2, 1
|
||||
case "fast3":
|
||||
config.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1
|
||||
}
|
||||
|
||||
myApp.Action = func(c *cli.Context) {
|
||||
log.Println("version:", VERSION)
|
||||
pass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)
|
||||
nodelay, interval, resend, nc := c.Int("nodelay"), c.Int("interval"), c.Int("resend"), c.Int("nc")
|
||||
switch c.String("mode") {
|
||||
case "normal":
|
||||
nodelay, interval, resend, nc = 0, 30, 2, 1
|
||||
case "fast":
|
||||
nodelay, interval, resend, nc = 0, 20, 2, 1
|
||||
case "fast2":
|
||||
nodelay, interval, resend, nc = 1, 20, 2, 1
|
||||
case "fast3":
|
||||
nodelay, interval, resend, nc = 1, 10, 2, 1
|
||||
}
|
||||
|
||||
pass := pbkdf2.Key([]byte(c.String("key")), []byte(SALT), 4096, 32, sha1.New)
|
||||
var block kcp.BlockCrypt
|
||||
switch config.Crypt {
|
||||
switch c.String("crypt") {
|
||||
case "tea":
|
||||
block, _ = kcp.NewTEABlockCrypt(pass[:16])
|
||||
case "xor":
|
||||
block, _ = kcp.NewSimpleXORBlockCrypt(pass)
|
||||
case "none":
|
||||
block, _ = kcp.NewNoneBlockCrypt(pass)
|
||||
case "aes-128":
|
||||
block, _ = kcp.NewAESBlockCrypt(pass[:16])
|
||||
case "aes-192":
|
||||
block, _ = kcp.NewAESBlockCrypt(pass[:24])
|
||||
case "blowfish":
|
||||
block, _ = kcp.NewBlowfishBlockCrypt(pass)
|
||||
case "twofish":
|
||||
block, _ = kcp.NewTwofishBlockCrypt(pass)
|
||||
case "cast5":
|
||||
block, _ = kcp.NewCast5BlockCrypt(pass[:16])
|
||||
case "3des":
|
||||
block, _ = kcp.NewTripleDESBlockCrypt(pass[:24])
|
||||
case "xtea":
|
||||
block, _ = kcp.NewXTEABlockCrypt(pass[:16])
|
||||
case "salsa20":
|
||||
block, _ = kcp.NewSalsa20BlockCrypt(pass)
|
||||
default:
|
||||
config.Crypt = "aes"
|
||||
block, _ = kcp.NewAESBlockCrypt(pass)
|
||||
}
|
||||
|
||||
lis, err := kcp.ListenWithOptions(config.Listen, block, config.DataShard, config.ParityShard)
|
||||
checkError(err)
|
||||
log.Println("listening on:", lis.Addr())
|
||||
log.Println("target:", config.Target)
|
||||
log.Println("encryption:", config.Crypt)
|
||||
log.Println("nodelay parameters:", config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
log.Println("sndwnd:", config.SndWnd, "rcvwnd:", config.RcvWnd)
|
||||
log.Println("compression:", !config.NoComp)
|
||||
log.Println("mtu:", config.MTU)
|
||||
log.Println("datashard:", config.DataShard, "parityshard:", config.ParityShard)
|
||||
log.Println("acknodelay:", config.AckNodelay)
|
||||
log.Println("dscp:", config.DSCP)
|
||||
log.Println("sockbuf:", config.SockBuf)
|
||||
log.Println("keepalive:", config.KeepAlive)
|
||||
log.Println("snmplog:", config.SnmpLog)
|
||||
log.Println("snmpperiod:", config.SnmpPeriod)
|
||||
log.Println("pprof:", config.Pprof)
|
||||
|
||||
if err := lis.SetDSCP(config.DSCP); err != nil {
|
||||
log.Println("SetDSCP:", err)
|
||||
lis, err := kcp.ListenWithOptions(c.String("listen"), block, c.Int("datashard"), c.Int("parityshard"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := lis.SetReadBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetReadBuffer:", err)
|
||||
}
|
||||
if err := lis.SetWriteBuffer(config.SockBuf); err != nil {
|
||||
log.Println("SetWriteBuffer:", err)
|
||||
}
|
||||
|
||||
go snmpLogger(config.SnmpLog, config.SnmpPeriod)
|
||||
if config.Pprof {
|
||||
go http.ListenAndServe(":6060", nil)
|
||||
}
|
||||
|
||||
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("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.AcceptKCP(); err == nil {
|
||||
if conn, err := lis.Accept(); err == nil {
|
||||
log.Println("remote address:", conn.RemoteAddr())
|
||||
conn.SetStreamMode(true)
|
||||
conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
|
||||
conn.SetMtu(config.MTU)
|
||||
conn.SetWindowSize(config.SndWnd, config.RcvWnd)
|
||||
conn.SetACKNoDelay(config.AckNodelay)
|
||||
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"))
|
||||
|
||||
if config.NoComp {
|
||||
go handleMux(conn, &config)
|
||||
if c.Bool("nocomp") {
|
||||
go handleMux(conn, c.String("target"))
|
||||
} else {
|
||||
go handleMux(newCompStream(conn), &config)
|
||||
go handleMux(newCompStream(conn), c.String("target"))
|
||||
}
|
||||
} else {
|
||||
log.Printf("%+v", err)
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
myApp.Run(os.Args)
|
||||
}
|
||||
|
||||
func snmpLogger(path string, interval int) {
|
||||
if path == "" || interval == 0 {
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(time.Duration(interval) * time.Second)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
f, err := os.OpenFile(time.Now().Format(path), os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
w := csv.NewWriter(f)
|
||||
// write header in empty file
|
||||
if stat, err := f.Stat(); err == nil && stat.Size() == 0 {
|
||||
if err := w.Write(append([]string{"Unix"}, kcp.DefaultSnmp.Header()...)); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
if err := w.Write(append([]string{fmt.Sprint(time.Now().Unix())}, kcp.DefaultSnmp.ToSlice()...)); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
kcp.DefaultSnmp.Reset()
|
||||
w.Flush()
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
kcp "github.com/xtaci/kcp-go"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
||||
Reference in New Issue
Block a user