A Crypto-Secure, Production-Grade Reliable-UDP Library for golang with FEC

Overview

kcp-go

GoDoc Powered MIT licensed Build Status Go Report Card Coverage Statusd Sourcegraph

Introduction

kcp-go is a Production-Grade Reliable-UDP library for golang.

This library intents to provide a smooth, resilient, ordered, error-checked and anonymous delivery of streams over UDP packets, it has been battle-tested with opensource project kcptun. Millions of devices(from low-end MIPS routers to high-end servers) have deployed kcp-go powered program in a variety of forms like online games, live broadcasting, file synchronization and network acceleration.

Lastest Release

Features

  1. Designed for Latency-sensitive scenarios.
  2. Cache friendly and Memory optimized design, offers extremely High Performance core.
  3. Handles >5K concurrent connections on a single commodity server.
  4. Compatible with net.Conn and net.Listener, a drop-in replacement for net.TCPConn.
  5. FEC(Forward Error Correction) Support with Reed-Solomon Codes
  6. Packet level encryption support with AES, TEA, 3DES, Blowfish, Cast5, Salsa20, etc. in CFB mode, which generates completely anonymous packet.
  7. Only A fixed number of goroutines will be created for the entire server application, costs in context switch between goroutines have been taken into consideration.
  8. Compatible with skywind3000's C version with various improvements.
  9. Platform-dependent optimizations: sendmmsg and recvmmsg were expoloited for linux.

Documentation

For complete documentation, see the associated Godoc.

Specification

Frame Format

NONCE:
  16bytes cryptographically secure random number, nonce changes for every packet.
  
CRC32:
  CRC-32 checksum of data using the IEEE polynomial
 
FEC TYPE:
  typeData = 0xF1
  typeParity = 0xF2
  
FEC SEQID:
  monotonically increasing in range: [0, (0xffffffff/shardSize) * shardSize - 1]
  
SIZE:
  The size of KCP frame plus 2
+-----------------+
| SESSION         |
+-----------------+
| KCP(ARQ)        |
+-----------------+
| FEC(OPTIONAL)   |
+-----------------+
| CRYPTO(OPTIONAL)|
+-----------------+
| UDP(PACKET)     |
+-----------------+
| IP              |
+-----------------+
| LINK            |
+-----------------+
| PHY             |
+-----------------+
(LAYER MODEL OF KCP-GO)

Examples

  1. simple examples
  2. kcptun client
  3. kcptun server

Benchmark

===
Model Name:	MacBook Pro
Model Identifier:	MacBookPro14,1
Processor Name:	Intel Core i5
Processor Speed:	3.1 GHz
Number of Processors:	1
Total Number of Cores:	2
L2 Cache (per Core):	256 KB
L3 Cache:	4 MB
Memory:	8 GB
===

$ go test -v -run=^$ -bench .
beginning tests, encryption:salsa20, fec:10/3
goos: darwin
goarch: amd64
pkg: github.com/xtaci/kcp-go
BenchmarkSM4-4                 	   50000	     32180 ns/op	  93.23 MB/s	       0 B/op	       0 allocs/op
BenchmarkAES128-4              	  500000	      3285 ns/op	 913.21 MB/s	       0 B/op	       0 allocs/op
BenchmarkAES192-4              	  300000	      3623 ns/op	 827.85 MB/s	       0 B/op	       0 allocs/op
BenchmarkAES256-4              	  300000	      3874 ns/op	 774.20 MB/s	       0 B/op	       0 allocs/op
BenchmarkTEA-4                 	  100000	     15384 ns/op	 195.00 MB/s	       0 B/op	       0 allocs/op
BenchmarkXOR-4                 	20000000	        89.9 ns/op	33372.00 MB/s	       0 B/op	       0 allocs/op
BenchmarkBlowfish-4            	   50000	     26927 ns/op	 111.41 MB/s	       0 B/op	       0 allocs/op
BenchmarkNone-4                	30000000	        45.7 ns/op	65597.94 MB/s	       0 B/op	       0 allocs/op
BenchmarkCast5-4               	   50000	     34258 ns/op	  87.57 MB/s	       0 B/op	       0 allocs/op
Benchmark3DES-4                	   10000	    117149 ns/op	  25.61 MB/s	       0 B/op	       0 allocs/op
BenchmarkTwofish-4             	   50000	     33538 ns/op	  89.45 MB/s	       0 B/op	       0 allocs/op
BenchmarkXTEA-4                	   30000	     45666 ns/op	  65.69 MB/s	       0 B/op	       0 allocs/op
BenchmarkSalsa20-4             	  500000	      3308 ns/op	 906.76 MB/s	       0 B/op	       0 allocs/op
BenchmarkCRC32-4               	20000000	        65.2 ns/op	15712.43 MB/s
BenchmarkCsprngSystem-4        	 1000000	      1150 ns/op	  13.91 MB/s
BenchmarkCsprngMD5-4           	10000000	       145 ns/op	 110.26 MB/s
BenchmarkCsprngSHA1-4          	10000000	       158 ns/op	 126.54 MB/s
BenchmarkCsprngNonceMD5-4      	10000000	       153 ns/op	 104.22 MB/s
BenchmarkCsprngNonceAES128-4   	100000000	        19.1 ns/op	 837.81 MB/s
BenchmarkFECDecode-4           	 1000000	      1119 ns/op	1339.61 MB/s	    1606 B/op	       2 allocs/op
BenchmarkFECEncode-4           	 2000000	       832 ns/op	1801.83 MB/s	      17 B/op	       0 allocs/op
BenchmarkFlush-4               	 5000000	       272 ns/op	       0 B/op	       0 allocs/op
BenchmarkEchoSpeed4K-4         	    5000	    259617 ns/op	  15.78 MB/s	    5451 B/op	     149 allocs/op
BenchmarkEchoSpeed64K-4        	    1000	   1706084 ns/op	  38.41 MB/s	   56002 B/op	    1604 allocs/op
BenchmarkEchoSpeed512K-4       	     100	  14345505 ns/op	  36.55 MB/s	  482597 B/op	   13045 allocs/op
BenchmarkEchoSpeed1M-4         	      30	  34859104 ns/op	  30.08 MB/s	 1143773 B/op	   27186 allocs/op
BenchmarkSinkSpeed4K-4         	   50000	     31369 ns/op	 130.57 MB/s	    1566 B/op	      30 allocs/op
BenchmarkSinkSpeed64K-4        	    5000	    329065 ns/op	 199.16 MB/s	   21529 B/op	     453 allocs/op
BenchmarkSinkSpeed256K-4       	     500	   2373354 ns/op	 220.91 MB/s	  166332 B/op	    3554 allocs/op
BenchmarkSinkSpeed1M-4         	     300	   5117927 ns/op	 204.88 MB/s	  310378 B/op	    6988 allocs/op
PASS
ok  	github.com/xtaci/kcp-go	50.349s
=== Raspberry Pi 4 ===

➜  kcp-go git:(master) cat /proc/cpuinfo
processor	: 0
model name	: ARMv7 Processor rev 3 (v7l)
BogoMIPS	: 108.00
Features	: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
CPU implementer	: 0x41
CPU architecture: 7
CPU variant	: 0x0
CPU part	: 0xd08
CPU revision	: 3

➜  kcp-go git:(master)  go test -run=^$ -bench .
2020/01/05 19:25:13 beginning tests, encryption:salsa20, fec:10/3
goos: linux
goarch: arm
pkg: github.com/xtaci/kcp-go/v5
BenchmarkSM4-4                     20000             86475 ns/op          34.69 MB/s           0 B/op          0 allocs/op
BenchmarkAES128-4                  20000             62254 ns/op          48.19 MB/s           0 B/op          0 allocs/op
BenchmarkAES192-4                  20000             71802 ns/op          41.78 MB/s           0 B/op          0 allocs/op
BenchmarkAES256-4                  20000             80570 ns/op          37.23 MB/s           0 B/op          0 allocs/op
BenchmarkTEA-4                     50000             37343 ns/op          80.34 MB/s           0 B/op          0 allocs/op
BenchmarkXOR-4                    100000             22266 ns/op         134.73 MB/s           0 B/op          0 allocs/op
BenchmarkBlowfish-4                20000             66123 ns/op          45.37 MB/s           0 B/op          0 allocs/op
BenchmarkNone-4                  3000000               518 ns/op        5786.77 MB/s           0 B/op          0 allocs/op
BenchmarkCast5-4                   20000             76705 ns/op          39.11 MB/s           0 B/op          0 allocs/op
Benchmark3DES-4                     5000            418868 ns/op           7.16 MB/s           0 B/op          0 allocs/op
BenchmarkTwofish-4                  5000            326896 ns/op           9.18 MB/s           0 B/op          0 allocs/op
BenchmarkXTEA-4                    10000            114418 ns/op          26.22 MB/s           0 B/op          0 allocs/op
BenchmarkSalsa20-4                 50000             36736 ns/op          81.66 MB/s           0 B/op          0 allocs/op
BenchmarkCRC32-4                 1000000              1735 ns/op         589.98 MB/s
BenchmarkCsprngSystem-4          1000000              2179 ns/op           7.34 MB/s
BenchmarkCsprngMD5-4             2000000               811 ns/op          19.71 MB/s
BenchmarkCsprngSHA1-4            2000000               862 ns/op          23.19 MB/s
BenchmarkCsprngNonceMD5-4        2000000               878 ns/op          18.22 MB/s
BenchmarkCsprngNonceAES128-4     5000000               326 ns/op          48.97 MB/s
BenchmarkFECDecode-4              200000              9081 ns/op         165.16 MB/s         140 B/op          1 allocs/op
BenchmarkFECEncode-4              100000             12039 ns/op         124.59 MB/s          11 B/op          0 allocs/op
BenchmarkFlush-4                  100000             21704 ns/op               0 B/op          0 allocs/op
BenchmarkEchoSpeed4K-4              2000            981182 ns/op           4.17 MB/s       12384 B/op        424 allocs/op
BenchmarkEchoSpeed64K-4              100          10503324 ns/op           6.24 MB/s      123616 B/op       3779 allocs/op
BenchmarkEchoSpeed512K-4              20         138633802 ns/op           3.78 MB/s     1606584 B/op      29233 allocs/op
BenchmarkEchoSpeed1M-4                 5         372903568 ns/op           2.81 MB/s     4080504 B/op      63600 allocs/op
BenchmarkSinkSpeed4K-4             10000            121239 ns/op          33.78 MB/s        4647 B/op        104 allocs/op
BenchmarkSinkSpeed64K-4             1000           1587906 ns/op          41.27 MB/s       50914 B/op       1115 allocs/op
BenchmarkSinkSpeed256K-4             100          16277830 ns/op          32.21 MB/s      453027 B/op       9296 allocs/op
BenchmarkSinkSpeed1M-4               100          31040703 ns/op          33.78 MB/s      898097 B/op      18932 allocs/op
PASS
ok      github.com/xtaci/kcp-go/v5      64.151s

Typical Flame Graph

Flame Graph in kcptun

Key Design Considerations

  1. slice vs. container/list

kcp.flush() loops through the send queue for retransmission checking for every 20ms(interval).

I've wrote a benchmark for comparing sequential loop through slice and container/list here:

https://github.com/xtaci/notes/blob/master/golang/benchmark2/cachemiss_test.go

BenchmarkLoopSlice-4   	2000000000	         0.39 ns/op
BenchmarkLoopList-4    	100000000	        54.6 ns/op

List structure introduces heavy cache misses compared to slice which owns better locality, 5000 connections with 32 window size and 20ms interval will cost 6us/0.03%(cpu) using slice, and 8.7ms/43.5%(cpu) for list for each kcp.flush().

  1. Timing accuracy vs. syscall clock_gettime

Timing is critical to RTT estimator, inaccurate timing leads to false retransmissions in KCP, but calling time.Now() costs 42 cycles(10.5ns on 4GHz CPU, 15.6ns on my MacBook Pro 2.7GHz).

The benchmark for time.Now() lies here:

https://github.com/xtaci/notes/blob/master/golang/benchmark2/syscall_test.go

BenchmarkNow-4         	100000000	        15.6 ns/op

In kcp-go, after each kcp.output() function call, current clock time will be updated upon return, and for a single kcp.flush() operation, current time will be queried from system once. For most of the time, 5000 connections costs 5000 * 15.6ns = 78us(a fixed cost while no packet needs to be sent), as for 10MB/s data transfering with 1400 MTU, kcp.output() will be called around 7500 times and costs 117us for time.Now() in every second.

  1. Memory management

Primary memory allocation are done from a global buffer pool xmit.Buf, in kcp-go, when we need to allocate some bytes, we can get from that pool, and a fixed-capacity 1500 bytes(mtuLimit) will be returned, the rx queue, tx queue and fec queue all receive bytes from there, and they will return the bytes to the pool after using to prevent unnecessary zer0ing of bytes. The pool mechanism maintained a high watermark for slice objects, these in-flight objects from the pool will survive from the perodical garbage collection, meanwhile the pool kept the ability to return the memory to runtime if in idle.

  1. Information security

kcp-go is shipped with builtin packet encryption powered by various block encryption algorithms and works in Cipher Feedback Mode, for each packet to be sent, the encryption process will start from encrypting a nonce from the system entropy, so encryption to same plaintexts never leads to a same ciphertexts thereafter.

The contents of the packets are completely anonymous with encryption, including the headers(FEC,KCP), checksums and contents. Note that, no matter which encryption method you choose on you upper layer, if you disable encryption, the transmit will be insecure somehow, since the header is PLAINTEXT to everyone it would be susceptible to header tampering, such as jamming the sliding window size, round-trip time, FEC property and checksums. AES-128 is suggested for minimal encryption since modern CPUs are shipped with AES-NI instructions and performs even better than salsa20(check the table above).

Other possible attacks to kcp-go includes: a) traffic analysis, dataflow on specific websites may have pattern while interchanging data, but this type of eavesdropping has been mitigated by adapting smux to mix data streams so as to introduce noises, perfect solution to this has not appeared yet, theroretically by shuffling/mixing messages on larger scale network may mitigate this problem. b) replay attack, since the asymmetrical encryption has not been introduced into kcp-go for some reason, capturing the packets and replay them on a different machine is possible, (notice: hijacking the session and decrypting the contents is still impossible), so upper layers should contain a asymmetrical encryption system to guarantee the authenticity of each message(to process message exactly once), such as HTTPS/OpenSSL/LibreSSL, only by signing the requests with private keys can eliminate this type of attack.

Connection Termination

Control messages like SYN/FIN/RST in TCP are not defined in KCP, you need some keepalive/heartbeat mechanism in the application-level. A real world example is to use some multiplexing protocol over session, such as smux(with embedded keepalive mechanism), see kcptun for example.

FAQ

Q: I'm handling >5K connections on my server, the CPU utilization is so high.

A: A standalone agent or gate server for running kcp-go is suggested, not only for CPU utilization, but also important to the precision of RTT measurements(timing) which indirectly affects retransmission. By increasing update interval with SetNoDelay like conn.SetNoDelay(1, 40, 1, 1) will dramatically reduce system load, but lower the performance.

Q: When should I enable FEC?

A: Forward error correction is critical to long-distance transmission, because a packet loss will lead to a huge penalty in time. And for the complicated packet routing network in modern world, round-trip time based loss check will not always be efficient, the big deviation of RTT samples in the long way usually leads to a larger RTO value in typical rtt estimator, which in other words, slows down the transmission.

Q: Should I enable encryption?

A: Yes, for the safety of protocol, even if the upper layer has encrypted.

Who is using this?

  1. https://github.com/xtaci/kcptun -- A Secure Tunnel Based On KCP over UDP.
  2. https://github.com/getlantern/lantern -- Lantern delivers fast access to the open Internet.
  3. https://github.com/smallnest/rpcx -- A RPC service framework based on net/rpc like alibaba Dubbo and weibo Motan.
  4. https://github.com/gonet2/agent -- A gateway for games with stream multiplexing.
  5. https://github.com/syncthing/syncthing -- Open Source Continuous File Synchronization.

Links

  1. https://github.com/xtaci/smux/ -- A Stream Multiplexing Library for golang with least memory
  2. https://github.com/xtaci/libkcp -- FEC enhanced KCP session library for iOS/Android in C++
  3. https://github.com/skywind3000/kcp -- A Fast and Reliable ARQ Protocol
  4. https://github.com/klauspost/reedsolomon -- Reed-Solomon Erasure Coding in Go

Consulting

WeChat(付费技术咨询)

kcptun

Issues
  • sync.Pool leaks

    sync.Pool leaks

    We've noticed memory leaks with KCP-Go. We have traced this down to sync.Pool leaks by instrumenting with counters for each of the calls to Get() and Put(), ie:

    					// recycle the recovers
    					xmitBuf.Put(r)
    					// Temporary counter
    					atomic.AddUint64(&Framesput, 1)
    

    Over time, about 5-8% of byte buffers are not being returned to the sync.Pool and at 1500 bytes each, this adds up.

    Are you seeing the same thing? Any thoughts on how to fix this?

    opened by WinstonPrivacy 70
  • fec问题

    fec问题

    关于fec decode, 感觉跑了一段时间会有问题 假设一开始seqid是一直增长的, data_shards是2, parity_shards是1,

    1. 假设先收到0, 1, 然后解析成功, 0, 1从queue里面去掉了
    2. 收到2, 那么2就会留在queue里面
    3. ... 假如1,2两步一直重复, 那么queue就会被填满
    4. seqid一直增, 直到超过最大值,然后从0重新开始, 从这个时间开始, 因为queue满了, 而新的seqid一直小于queue里面的数值, 这样最小的seqid就会被淘汰, 那样的话,从这个点开始,fec就相当于一直不工作了.
    opened by dista 37
  • 数据有可能错掉

    数据有可能错掉

    类似kcptun,在kcp-go上再套上yamux,两台服务器之间传输数据,服务端用一个简单的echoserver,写了一个测试程序,每隔50ms发送1024个字节的数据,然后计算RTT延迟,延迟是比较小(0-15ms),但是运行到一定的数量,yamux会输出invalid protocol version错误,然后连接断掉,使用还没加SetACKNodelay的版本,就不会这样但是延迟会稍微高些(50ms左右,具体哪个版本现在无法找回了),测试程序是这个: https://github.com/zx9597446/echoping 现在不能确定是kcp-go的问题,还是yamux的问题,怀疑是kcp-go发送的数据会有一定几率错掉?

    opened by zx9597446 32
  • close old session if conv conflict and sn = 0 && only create new kcp …

    close old session if conv conflict and sn = 0 && only create new kcp …

    两个修改:

    1. 如果同一个ip:port收到冲突的conv,且sn=0,说明该端口在kcp超时时间内被客户端重用了。此时关闭旧的session,创建新session。
    2. 仅当收到sn=0的packet时,才创建新连接。避免服务端主动关闭连接后,客户端重发之前的包导致连接重开。这个修改可能引起当创建新连接时,客户端同时发送多个包,在sn=0之前到达的包被丢弃,需要重传,个人认为这个是可以接收的。

    udp端口在短时间内很容易重用,见该测试:https://gist.github.com/wudeng/f227bdd4becc786d8e3a9d213b0475c3

    跟issue #150 有相关性。

    opened by xjdrew 25
  • About dependency library reedsolomon

    About dependency library reedsolomon

    I'm debian pkg maintainer of a few kcp related projects. It's got my attention that golang-github-xtaci-kcp v3.19 changes dependency library reedsolomon from original upstream to a fork, which claims big performance boost:

    • https://github.com/templexxx/reedsolomon

    So for debian pkg maintainer, before I can release debian pkg of golang-github-xtaci-kcp to v3.19, I have to package the new fork of reedsolomon, which I want to avoid. So I created ticket to the fork and upstream, to seek the possibility of merging 2 version.

    • https://github.com/templexxx/reedsolomon/issues/9
    • https://github.com/klauspost/reedsolomon/issues/69

    Now it seems the author of the fork isn't interested to submit the improvement patch upstream. However the original author replied in detail and proposed his improvement:

    • https://github.com/klauspost/reedsolomon/pull/70

    So I want to ask whether it's possible for you, golang-github-xtaci-kcp, to change back to the original upstream of reedsolomon, of course with the patch in pull-request above.

    Thanks for your understanding!

    Cheers

    help wanted 
    opened by rogers0 19
  • Multiple changes

    Multiple changes

    1. Use interfaces instead of concrete types
    2. Allow creating connections from the provided connection
    3. Add deadlines to Listener (only read is used for accepting for now)
    4. Allow disabling logging
    5. Return a proper timeout error (net.Error)

    Another thing which would be nice to add is DialWithTimeout, but as far as I understand the protocol, there are no handshake as such until data is actually sent, hence it wouldn't make any sense?

    opened by AudriusButkevicius 19
  • Add ahead encryption support

    Add ahead encryption support

    1. Add ahead encryption support to UDP level to avoid major rework for BlockCrypt
    2. Since using ahead encryption, so there is no need for CRC check and original encryptions.
    3. Ahead encryption and original encryption is mutual exclusive.
    4. Add new crypt test and bencmark
    5. Add ahead for sess_test.go, using USING_AHEAD to control which encryption to use
    6. All test passed
    7. I noticed you were using single gorouting for encryption and decryption all traffic, so I think its better using pre-defined numbers of go routing to parallel these tasks for better performance, since its kind of bottleneck for now.
    opened by weishi258 16
  • kcp-go的客户端,为什么不能设置convid?

    kcp-go的客户端,为什么不能设置convid?

    func NewConn(raddr string, block BlockCrypt, dataShards, parityShards int, conn net.PacketConn) (*UDPSession, error) {
    	udpaddr, err := net.ResolveUDPAddr("udp", raddr)
    	if err != nil {
    		return nil, errors.Wrap(err, "net.ResolveUDPAddr")
    	}
    
    	var convid uint32
    	binary.Read(rand.Reader, binary.LittleEndian, &convid)
    	return newUDPSession(convid, dataShards, parityShards, nil, conn, udpaddr, block), nil
    }
    

    我发现convid是随机的,能否提供个让我能自己传入convid的方法

    opened by spiderpoison 15
  • ping包完全随机?

    ping包完全随机?

    如下代码是发送ping包的outputTask()的片段,客户端和服务端都会发送,发送的是完全随机的字节:

            case <-ticker.C: // NAT keep-alive
                if len(s.chUDPOutput) == 0 {
                    s.mu.Lock()
                    interval := s.keepAliveInterval
                    s.mu.Unlock()
                    if interval > 0 && time.Now().After(lastPing.Add(interval)) {
                        sz := s.rng.Intn(IKCP_MTU_DEF - s.headerSize - IKCP_OVERHEAD)
                        sz += s.headerSize + IKCP_OVERHEAD
                        ping := make([]byte, sz)
                        io.ReadFull(crand.Reader, ping)
                        n, err := s.writeTo(ping, s.remote)
                        if err != nil {
                            log.Println(err, n)
                        }
                        lastPing = time.Now()
                    }
                }
    
    

    那么有疑问,就是ping包发送的是完全随机的字节: 假如开启了加密,好么接收端收到这些完全随机的ping包,会校验失败而忽略掉,但是ping包导致的校验错误会记录到DefaultSnmp.InCsumErrors中,这不是所期望的吧?:

        for {
            select {
            case p := <-chPacket:
                raw := p.data
                data := p.data
                from := p.from
                dataValid := false
                if l.block != nil {
                    l.block.Decrypt(data, data)
                    data = data[nonceSize:]
                    checksum := crc32.ChecksumIEEE(data[crcSize:])
                    if checksum == binary.LittleEndian.Uint32(data) {
                        data = data[crcSize:]
                        dataValid = true
                    } else {
                        atomic.AddUint64(&DefaultSnmp.InCsumErrors, 1)
                    }
                } else if l.block == nil {
                    dataValid = true
                }
                s.kcpInput(data)
    

    即便是开启了加密和CRC,也有极小的概率出现随机产生的数据包恰巧验证通过而被当成了正常的数据包,这样情况也是异常的。不知我的理解正不正确?

    opened by FTwOoO 14
  • 包模式下接收数据并没有还原原来的完整包

    包模式下接收数据并没有还原原来的完整包

    测试代码下,当服务器发送的包大于1326,则接收端收到的每个包最大为1326. 在消息模式是不是应该发送端发送了一个包,则接收端只收到一个包?

    package main
    
    import (
    	"log"
    	"time"
    
    	"github.com/xtaci/kcp-go"
    )
    
    type kcpConfig struct {
    	Listen       string
    	MTU          int
    	AckNodelay   bool
    	NoDelay      int
    	Interval     int
    	Resend       int
    	NoCongestion int
    }
    
    var config = &kcpConfig{
    	MTU:          1350,
    	Listen:       ":15000",
    	NoDelay:      1,
    	Interval:     60,
    	Resend:       2,
    	NoCongestion: 1,
    }
    
    func kcpServer() {
    
    	kcpLis, err := kcp.ListenWithOptions(config.Listen, nil, 0, 0)
    	if err != nil {
    		log.Fatal("kcp listen err:", err)
    	}
    
    	for {
    		conn, err := kcpLis.AcceptKCP()
    		if err == nil {
    			log.Println("new kcp,remote address:", conn.RemoteAddr())
    			conn.SetStreamMode(false)
    			conn.SetWriteDelay(false)
    			conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
    			conn.SetMtu(config.MTU)
    			conn.SetACKNoDelay(config.AckNodelay)
    
    			go SendOneKcpConnection(conn)
    
    		} else {
    			log.Println("kcp accept err:", err)
    		}
    	}
    }
    
    func SendOneKcpConnection(conn *kcp.UDPSession) {
    	var buf []byte = make([]byte, 10*1024)
    	sendsize := 1
    
    	n, _ := conn.Read(buf)
    	log.Println("kcp got:", n)
    	for {
    
    		log.Println("kcp send:", sendsize)
    		conn.Write(buf[:sendsize])
    
    		sendsize += 100
    
    		if sendsize >= 4096 {
    			time.Sleep(time.Second)
    			conn.Close()
    			return
    		}
    
    		time.Sleep(60 * time.Millisecond)
    
    	}
    }
    
    func kcpClient() {
    	time.Sleep(3 * time.Second)
    	conn, err := kcp.DialWithOptions("127.0.0.1:15000", nil, 0, 0)
    	if err != nil {
    		log.Fatal("kcp dial error", err.Error())
    	}
    
    	conn.SetStreamMode(false)
    	conn.SetWriteDelay(false)
    	conn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)
    	conn.SetMtu(config.MTU)
    	conn.SetACKNoDelay(config.AckNodelay)
    
    	var buf []byte = make([]byte, 10*1024)
    
    	conn.Write([]byte("123"))
    	for {
    		n, err := conn.Read(buf)
    		log.Println("kcp read:", n, err)
    	}
    }
    
    func main() {
    	go kcpClient()
    
    	kcpServer()
    
    }
    

    下面是一次运行记录

    2018/07/23 13:07:46 new kcp,remote address: 127.0.0.1:45621
    2018/07/23 13:07:46 kcp got: 3
    2018/07/23 13:07:46 kcp send: 1
    2018/07/23 13:07:46 kcp read: 1 <nil>
    2018/07/23 13:07:46 kcp send: 101
    2018/07/23 13:07:46 kcp read: 101 <nil>
    2018/07/23 13:07:46 kcp send: 201
    2018/07/23 13:07:46 kcp read: 201 <nil>
    2018/07/23 13:07:46 kcp send: 301
    2018/07/23 13:07:46 kcp read: 301 <nil>
    2018/07/23 13:07:46 kcp send: 401
    2018/07/23 13:07:46 kcp read: 401 <nil>
    2018/07/23 13:07:46 kcp send: 501
    2018/07/23 13:07:46 kcp read: 501 <nil>
    2018/07/23 13:07:46 kcp send: 601
    2018/07/23 13:07:46 kcp read: 601 <nil>
    2018/07/23 13:07:47 kcp send: 701
    2018/07/23 13:07:47 kcp read: 701 <nil>
    2018/07/23 13:07:47 kcp send: 801
    2018/07/23 13:07:47 kcp read: 801 <nil>
    2018/07/23 13:07:47 kcp send: 901
    2018/07/23 13:07:47 kcp read: 901 <nil>
    2018/07/23 13:07:47 kcp send: 1001
    2018/07/23 13:07:47 kcp read: 1001 <nil>
    2018/07/23 13:07:47 kcp send: 1101
    2018/07/23 13:07:47 kcp read: 1101 <nil>
    2018/07/23 13:07:47 kcp send: 1201
    2018/07/23 13:07:47 kcp read: 1201 <nil>
    2018/07/23 13:07:47 kcp send: 1301
    2018/07/23 13:07:47 kcp read: 1301 <nil>
    2018/07/23 13:07:47 kcp send: 1401
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 75 <nil>
    2018/07/23 13:07:47 kcp send: 1501
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 175 <nil>
    2018/07/23 13:07:47 kcp send: 1601
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 275 <nil>
    2018/07/23 13:07:47 kcp send: 1701
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 375 <nil>
    2018/07/23 13:07:47 kcp send: 1801
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 475 <nil>
    2018/07/23 13:07:47 kcp send: 1901
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 575 <nil>
    2018/07/23 13:07:47 kcp send: 2001
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 675 <nil>
    2018/07/23 13:07:47 kcp send: 2101
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 775 <nil>
    2018/07/23 13:07:47 kcp send: 2201
    2018/07/23 13:07:47 kcp read: 1326 <nil>
    2018/07/23 13:07:47 kcp read: 875 <nil>
    2018/07/23 13:07:48 kcp send: 2301
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 975 <nil>
    2018/07/23 13:07:48 kcp send: 2401
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1075 <nil>
    2018/07/23 13:07:48 kcp send: 2501
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1175 <nil>
    2018/07/23 13:07:48 kcp send: 2601
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1275 <nil>
    2018/07/23 13:07:48 kcp send: 2701
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 49 <nil>
    2018/07/23 13:07:48 kcp send: 2801
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 149 <nil>
    2018/07/23 13:07:48 kcp send: 2901
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 249 <nil>
    2018/07/23 13:07:48 kcp send: 3001
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 349 <nil>
    2018/07/23 13:07:48 kcp send: 3101
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 449 <nil>
    2018/07/23 13:07:48 kcp send: 3201
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 549 <nil>
    2018/07/23 13:07:48 kcp send: 3301
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 649 <nil>
    2018/07/23 13:07:48 kcp send: 3401
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 749 <nil>
    2018/07/23 13:07:48 kcp send: 3501
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 849 <nil>
    2018/07/23 13:07:48 kcp send: 3601
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 949 <nil>
    2018/07/23 13:07:48 kcp send: 3701
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1049 <nil>
    2018/07/23 13:07:48 kcp send: 3801
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1149 <nil>
    2018/07/23 13:07:48 kcp send: 3901
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1326 <nil>
    2018/07/23 13:07:48 kcp read: 1249 <nil>
    2018/07/23 13:07:49 kcp send: 4001
    2018/07/23 13:07:49 kcp read: 1326 <nil>
    2018/07/23 13:07:49 kcp read: 1326 <nil>
    2018/07/23 13:07:49 kcp read: 1326 <nil>
    2018/07/23 13:07:49 kcp read: 23 <nil>
    
    
    opened by dbskccc 13
  • Reconnect from same IP fails if there's only one active client.

    Reconnect from same IP fails if there's only one active client.

    lastAddr should be cleared when a session is closed.

    index fac3122..8725f40 100644
    --- a/sess.go
    +++ b/sess.go
    @@ -754,6 +754,7 @@ func (l *Listener) monitor() {
     
                            xmitBuf.Put(raw)
                    case deadlink := <-l.chSessionClosed:
    +                       lastAddr = ""
                            delete(l.sessions, deadlink.String())
                    case <-l.die:
                            return
    
    opened by univac490 13
  • Why is UDPSession.SetWriteBuffer no effect if accepted from Listener?

    Why is UDPSession.SetWriteBuffer no effect if accepted from Listener?

    // SetWriteBuffer sets the socket write buffer, no effect if it's accepted from Listener
    func (s *UDPSession) SetWriteBuffer(bytes int) error {
    	s.mu.Lock()
    	defer s.mu.Unlock()
    	if s.l == nil {
    		if nc, ok := s.conn.(setWriteBuffer); ok {
    			return nc.SetWriteBuffer(bytes)
    		}
    	}
    	return errInvalidOperation
    }
    

    If UDPSession said to be a drop-in replacement for net.TCPConn, why is UDPSession.SetWriteBuffer no effect if accepted from Listener?

    opened by zhiqiangxu 2
  • listener.Accept() runs only after a byte is received

    listener.Accept() runs only after a byte is received

    listener.Accept() runs only after a byte is received: but expected to run it after kcp.Dial.

    The test code:

    package main
    
    import (
    	"log"
    	"time"
    	kcp "github.com/xtaci/kcp-go/v5"
    )
    
    func main() {
    
    	// start the server
    
    	ln, err := kcp.Listen("127.0.0.1:48000")
    	log.Println("Listening KCP...")
    	if err != nil {
    		panic(err)
    	}
    	// run the client
    	go client()
    	/**
    	Listening for a connection
    	**/
    	_, err = ln.Accept()
    	if err != nil {
    		panic(err)
    	} else {
    		log.Println("OK.")
    	}
    }
    
    func client() {
    
    	// wait for server to become ready
    	time.Sleep(time.Second)
    
    	// dial to the echo server
    	if sess, err := kcp.Dial("127.0.0.1:48000"); err == nil {
    		// wait for data write
    		time.Sleep(time.Second)
    		data := "."
    		log.Println("sent:", data[:1])
    		if _, err := sess.Write([]byte(data[:1])); err != nil {
    			log.Fatal(err)				
    		}
    	} else {
    		log.Fatal(err)
    	}
    }
    

    Output:

    $ go run examples/main.go
    2022/06/04 12:36:33 Listening KCP...
    2022/06/04 12:36:35 sent: .
    2022/06/04 12:36:35 OK.
    

    Expected "OK." to follow right after "Listening KCP..."

    opened by vikulin 1
  • bad package divided

    bad package divided

    when send a big packet by Kcp.Send, packet will be divided with frg flag. but in UDPSession.WriteBuffers function, packet is divided before call Kcp.Send. in this case, receiver can not get integral packet in normal mode. I think divide packet in WriteBuffers is not needed.

    opened by w-zz 1
Releases(v5.6.1)
Owner
xtaci
自心取自心,非幻成幻法。
xtaci
Reliable, thin UDP library for Golang targetting games.

⚠️ Do not use this. There's a lot to be worked on here and it's still not comple

sailormoon 2 Dec 27, 2021
UDP Transport: compress, encrypt and send any data reliably over unreliable UDP connections

udpt UDP Transport Compresses, encrypts and transfers data between a sender and receiver using UDP protocol. Features and Design Aims: Avoid the overh

Ali Bala 41 Jul 30, 2022
UDP output for beats to send events over UDP.

beats-udp-output How To Use Clone this project to elastic/beats/libbeat/output/ Modify elastic/beats/libbeat/publisher/includes/includes.go : // add i

ichx 0 Dec 11, 2021
A simple UDP server to make a virtual secure channel with the clients

udpsocket I made this package to make a virtual stateful connection between the client & server using the UDP protocol for a golang game server (as yo

TheRedRad 5 Jun 18, 2022
BitTorrent client and library in Go. Running in production at put.io.

rain BitTorrent client and library in Go. Running in production at put.io. Integration of embedded gRPC framework (All APIs translated to gRPC Server-

Suhas Aggarwal 2 Apr 11, 2022
Pure-Go library for cross-platform local peer discovery using UDP multicast :woman: :repeat: :woman:

peerdiscovery Pure-go library for cross-platform thread-safe local peer discovery using UDP multicast. I needed to use peer discovery for croc and eve

Zack 552 Aug 4, 2022
SOCKS Protocol Version 5 Library in Go. Full TCP/UDP and IPv4/IPv6 support

socks5 中文 SOCKS Protocol Version 5 Library. Full TCP/UDP and IPv4/IPv6 support. Goals: KISS, less is more, small API, code is like the original protoc

TxThinking 476 Jul 30, 2022
Forked Version of Miekg's DNS library that recycles UDP sockets

Alternative (more granular) approach to a DNS library Less is more. Complete and usable DNS library. All Resource Records are supported, including the

null 0 Jan 20, 2022
A socks5 server(tcp/udp) written in golang.

socks5-server A socks5 server(tcp/udp) written in golang. Usage Usage of /main: -l string local address (default "127.0.0.1:1080") -p stri

null 26 Aug 3, 2022
Golang pow implementation client <-> server over UDP and TCP protocols

Client <-> server over UDP and TCP pow protocol Denial-of-Service-attacks are a typical situation when providing services over a network. A method for

Nikita Kryuchkov 0 Jan 13, 2022
Go-weatherflow - WeatherFlow Tempest UDP broadcast receiver in golang

go-weatherflow $ go build $ ./go-weatherflow {"serial_number":"ST-00055227","typ

boB Rudis 1 Jan 27, 2022
Udp forward - Forwarding UPD requests with golang

udp_forward About tool I want to check how GOlang work with network... It tool t

null 0 Feb 3, 2022
SFTP support for the go.crypto/ssh package

sftp The sftp package provides support for file system operations on remote ssh servers using the SFTP subsystem. It also implements an SFTP server fo

null 1.2k Aug 1, 2022
Chisel is a fast TCP/UDP tunnel, transported over HTTP, secured via SSH.

Chisel is a fast TCP/UDP tunnel, transported over HTTP, secured via SSH. Single executable including both client and server. Written in Go (golang). Chisel is mainly useful for passing through firewalls, though it can also be used to provide a secure endpoint into your network.

Jaime Pillora 7.6k Aug 8, 2022
Send network packets over a TCP or UDP connection.

Packet is the main class representing a single network message. It has a byte code indicating the type of the message and a []byte type payload.

Aero 71 Jul 15, 2022
netscanner - TCP/UDP scanner to find open or closed ports

netscanner netscanner - TCP/UDP scanner to find open or closed ports installation you have to run this command to install the program $ go get github.

R4yan 199 Jul 27, 2022
A tiny command line DNS client with support for UDP, DoT, DoH, and DoQ.

q A tiny command line DNS client with support for UDP, DoT, DoH, and DoQ. Usage q command line DNS client (https://github.com/natesales/q) Usage: q

Nate Sales 729 Aug 11, 2022