mirror of
				https://github.com/ehang-io/nps
				synced 2025-10-30 13:00:12 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			303 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			303 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package kcp
 | ||
| 
 | ||
| import (
 | ||
| 	"bytes"
 | ||
| 	"container/list"
 | ||
| 	"encoding/binary"
 | ||
| 	"fmt"
 | ||
| 	"math/rand"
 | ||
| 	"sync"
 | ||
| 	"testing"
 | ||
| 	"time"
 | ||
| )
 | ||
| 
 | ||
| func iclock() int32 {
 | ||
| 	return int32(currentMs())
 | ||
| }
 | ||
| 
 | ||
| type DelayPacket struct {
 | ||
| 	_ptr  []byte
 | ||
| 	_size int
 | ||
| 	_ts   int32
 | ||
| }
 | ||
| 
 | ||
| func (p *DelayPacket) Init(size int, src []byte) {
 | ||
| 	p._ptr = make([]byte, size)
 | ||
| 	p._size = size
 | ||
| 	copy(p._ptr, src[:size])
 | ||
| }
 | ||
| 
 | ||
| func (p *DelayPacket) ptr() []byte    { return p._ptr }
 | ||
| func (p *DelayPacket) size() int      { return p._size }
 | ||
| func (p *DelayPacket) ts() int32      { return p._ts }
 | ||
| func (p *DelayPacket) setts(ts int32) { p._ts = ts }
 | ||
| 
 | ||
| type DelayTunnel struct{ *list.List }
 | ||
| type LatencySimulator struct {
 | ||
| 	current                        int32
 | ||
| 	lostrate, rttmin, rttmax, nmax int
 | ||
| 	p12                            DelayTunnel
 | ||
| 	p21                            DelayTunnel
 | ||
| 	r12                            *rand.Rand
 | ||
| 	r21                            *rand.Rand
 | ||
| }
 | ||
| 
 | ||
| // lostrate: 往返一周丢包率的百分比,默认 10%
 | ||
| // rttmin:rtt最小值,默认 60
 | ||
| // rttmax:rtt最大值,默认 125
 | ||
| //func (p *LatencySimulator)Init(int lostrate = 10, int rttmin = 60, int rttmax = 125, int nmax = 1000):
 | ||
| func (p *LatencySimulator) Init(lostrate, rttmin, rttmax, nmax int) {
 | ||
| 	p.r12 = rand.New(rand.NewSource(9))
 | ||
| 	p.r21 = rand.New(rand.NewSource(99))
 | ||
| 	p.p12 = DelayTunnel{list.New()}
 | ||
| 	p.p21 = DelayTunnel{list.New()}
 | ||
| 	p.current = iclock()
 | ||
| 	p.lostrate = lostrate / 2 // 上面数据是往返丢包率,单程除以2
 | ||
| 	p.rttmin = rttmin / 2
 | ||
| 	p.rttmax = rttmax / 2
 | ||
| 	p.nmax = nmax
 | ||
| }
 | ||
| 
 | ||
| // 发送数据
 | ||
| // peer - 端点0/1,从0发送,从1接收;从1发送从0接收
 | ||
| func (p *LatencySimulator) send(peer int, data []byte, size int) int {
 | ||
| 	rnd := 0
 | ||
| 	if peer == 0 {
 | ||
| 		rnd = p.r12.Intn(100)
 | ||
| 	} else {
 | ||
| 		rnd = p.r21.Intn(100)
 | ||
| 	}
 | ||
| 	//println("!!!!!!!!!!!!!!!!!!!!", rnd, p.lostrate, peer)
 | ||
| 	if rnd < p.lostrate {
 | ||
| 		return 0
 | ||
| 	}
 | ||
| 	pkt := &DelayPacket{}
 | ||
| 	pkt.Init(size, data)
 | ||
| 	p.current = iclock()
 | ||
| 	delay := p.rttmin
 | ||
| 	if p.rttmax > p.rttmin {
 | ||
| 		delay += rand.Int() % (p.rttmax - p.rttmin)
 | ||
| 	}
 | ||
| 	pkt.setts(p.current + int32(delay))
 | ||
| 	if peer == 0 {
 | ||
| 		p.p12.PushBack(pkt)
 | ||
| 	} else {
 | ||
| 		p.p21.PushBack(pkt)
 | ||
| 	}
 | ||
| 	return 1
 | ||
| }
 | ||
| 
 | ||
| // 接收数据
 | ||
| func (p *LatencySimulator) recv(peer int, data []byte, maxsize int) int32 {
 | ||
| 	var it *list.Element
 | ||
| 	if peer == 0 {
 | ||
| 		it = p.p21.Front()
 | ||
| 		if p.p21.Len() == 0 {
 | ||
| 			return -1
 | ||
| 		}
 | ||
| 	} else {
 | ||
| 		it = p.p12.Front()
 | ||
| 		if p.p12.Len() == 0 {
 | ||
| 			return -1
 | ||
| 		}
 | ||
| 	}
 | ||
| 	pkt := it.Value.(*DelayPacket)
 | ||
| 	p.current = iclock()
 | ||
| 	if p.current < pkt.ts() {
 | ||
| 		return -2
 | ||
| 	}
 | ||
| 	if maxsize < pkt.size() {
 | ||
| 		return -3
 | ||
| 	}
 | ||
| 	if peer == 0 {
 | ||
| 		p.p21.Remove(it)
 | ||
| 	} else {
 | ||
| 		p.p12.Remove(it)
 | ||
| 	}
 | ||
| 	maxsize = pkt.size()
 | ||
| 	copy(data, pkt.ptr()[:maxsize])
 | ||
| 	return int32(maxsize)
 | ||
| }
 | ||
| 
 | ||
| //=====================================================================
 | ||
| //=====================================================================
 | ||
| 
 | ||
| // 模拟网络
 | ||
| var vnet *LatencySimulator
 | ||
| 
 | ||
| // 测试用例
 | ||
| func test(mode int) {
 | ||
| 	// 创建模拟网络:丢包率10%,Rtt 60ms~125ms
 | ||
| 	vnet = &LatencySimulator{}
 | ||
| 	vnet.Init(10, 60, 125, 1000)
 | ||
| 
 | ||
| 	// 创建两个端点的 kcp对象,第一个参数 conv是会话编号,同一个会话需要相同
 | ||
| 	// 最后一个是 user参数,用来传递标识
 | ||
| 	output1 := func(buf []byte, size int) {
 | ||
| 		if vnet.send(0, buf, size) != 1 {
 | ||
| 		}
 | ||
| 	}
 | ||
| 	output2 := func(buf []byte, size int) {
 | ||
| 		if vnet.send(1, buf, size) != 1 {
 | ||
| 		}
 | ||
| 	}
 | ||
| 	kcp1 := NewKCP(0x11223344, output1)
 | ||
| 	kcp2 := NewKCP(0x11223344, output2)
 | ||
| 
 | ||
| 	current := uint32(iclock())
 | ||
| 	slap := current + 20
 | ||
| 	index := 0
 | ||
| 	next := 0
 | ||
| 	var sumrtt uint32
 | ||
| 	count := 0
 | ||
| 	maxrtt := 0
 | ||
| 
 | ||
| 	// 配置窗口大小:平均延迟200ms,每20ms发送一个包,
 | ||
| 	// 而考虑到丢包重发,设置最大收发窗口为128
 | ||
| 	kcp1.WndSize(128, 128)
 | ||
| 	kcp2.WndSize(128, 128)
 | ||
| 
 | ||
| 	// 判断测试用例的模式
 | ||
| 	if mode == 0 {
 | ||
| 		// 默认模式
 | ||
| 		kcp1.NoDelay(0, 10, 0, 0)
 | ||
| 		kcp2.NoDelay(0, 10, 0, 0)
 | ||
| 	} else if mode == 1 {
 | ||
| 		// 普通模式,关闭流控等
 | ||
| 		kcp1.NoDelay(0, 10, 0, 1)
 | ||
| 		kcp2.NoDelay(0, 10, 0, 1)
 | ||
| 	} else {
 | ||
| 		// 启动快速模式
 | ||
| 		// 第二个参数 nodelay-启用以后若干常规加速将启动
 | ||
| 		// 第三个参数 interval为内部处理时钟,默认设置为 10ms
 | ||
| 		// 第四个参数 resend为快速重传指标,设置为2
 | ||
| 		// 第五个参数 为是否禁用常规流控,这里禁止
 | ||
| 		kcp1.NoDelay(1, 10, 2, 1)
 | ||
| 		kcp2.NoDelay(1, 10, 2, 1)
 | ||
| 	}
 | ||
| 
 | ||
| 	buffer := make([]byte, 2000)
 | ||
| 	var hr int32
 | ||
| 
 | ||
| 	ts1 := iclock()
 | ||
| 
 | ||
| 	for {
 | ||
| 		time.Sleep(1 * time.Millisecond)
 | ||
| 		current = uint32(iclock())
 | ||
| 		kcp1.Update()
 | ||
| 		kcp2.Update()
 | ||
| 
 | ||
| 		// 每隔 20ms,kcp1发送数据
 | ||
| 		for ; current >= slap; slap += 20 {
 | ||
| 			buf := new(bytes.Buffer)
 | ||
| 			binary.Write(buf, binary.LittleEndian, uint32(index))
 | ||
| 			index++
 | ||
| 			binary.Write(buf, binary.LittleEndian, uint32(current))
 | ||
| 			// 发送上层协议包
 | ||
| 			kcp1.Send(buf.Bytes())
 | ||
| 			//println("now", iclock())
 | ||
| 		}
 | ||
| 
 | ||
| 		// 处理虚拟网络:检测是否有udp包从p1->p2
 | ||
| 		for {
 | ||
| 			hr = vnet.recv(1, buffer, 2000)
 | ||
| 			if hr < 0 {
 | ||
| 				break
 | ||
| 			}
 | ||
| 			// 如果 p2收到udp,则作为下层协议输入到kcp2
 | ||
| 			kcp2.Input(buffer[:hr], true, false)
 | ||
| 		}
 | ||
| 
 | ||
| 		// 处理虚拟网络:检测是否有udp包从p2->p1
 | ||
| 		for {
 | ||
| 			hr = vnet.recv(0, buffer, 2000)
 | ||
| 			if hr < 0 {
 | ||
| 				break
 | ||
| 			}
 | ||
| 			// 如果 p1收到udp,则作为下层协议输入到kcp1
 | ||
| 			kcp1.Input(buffer[:hr], true, false)
 | ||
| 			//println("@@@@", hr, r)
 | ||
| 		}
 | ||
| 
 | ||
| 		// kcp2接收到任何包都返回回去
 | ||
| 		for {
 | ||
| 			hr = int32(kcp2.Recv(buffer[:10]))
 | ||
| 			// 没有收到包就退出
 | ||
| 			if hr < 0 {
 | ||
| 				break
 | ||
| 			}
 | ||
| 			// 如果收到包就回射
 | ||
| 			buf := bytes.NewReader(buffer)
 | ||
| 			var sn uint32
 | ||
| 			binary.Read(buf, binary.LittleEndian, &sn)
 | ||
| 			kcp2.Send(buffer[:hr])
 | ||
| 		}
 | ||
| 
 | ||
| 		// kcp1收到kcp2的回射数据
 | ||
| 		for {
 | ||
| 			hr = int32(kcp1.Recv(buffer[:10]))
 | ||
| 			buf := bytes.NewReader(buffer)
 | ||
| 			// 没有收到包就退出
 | ||
| 			if hr < 0 {
 | ||
| 				break
 | ||
| 			}
 | ||
| 			var sn uint32
 | ||
| 			var ts, rtt uint32
 | ||
| 			binary.Read(buf, binary.LittleEndian, &sn)
 | ||
| 			binary.Read(buf, binary.LittleEndian, &ts)
 | ||
| 			rtt = uint32(current) - ts
 | ||
| 
 | ||
| 			if sn != uint32(next) {
 | ||
| 				// 如果收到的包不连续
 | ||
| 				//for i:=0;i<8 ;i++ {
 | ||
| 				//println("---", i, buffer[i])
 | ||
| 				//}
 | ||
| 				println("ERROR sn ", count, "<->", next, sn)
 | ||
| 				return
 | ||
| 			}
 | ||
| 
 | ||
| 			next++
 | ||
| 			sumrtt += rtt
 | ||
| 			count++
 | ||
| 			if rtt > uint32(maxrtt) {
 | ||
| 				maxrtt = int(rtt)
 | ||
| 			}
 | ||
| 
 | ||
| 			//println("[RECV] mode=", mode, " sn=", sn, " rtt=", rtt)
 | ||
| 		}
 | ||
| 
 | ||
| 		if next > 100 {
 | ||
| 			break
 | ||
| 		}
 | ||
| 	}
 | ||
| 
 | ||
| 	ts1 = iclock() - ts1
 | ||
| 
 | ||
| 	names := []string{"default", "normal", "fast"}
 | ||
| 	fmt.Printf("%s mode result (%dms):\n", names[mode], ts1)
 | ||
| 	fmt.Printf("avgrtt=%d maxrtt=%d\n", int(sumrtt/uint32(count)), maxrtt)
 | ||
| }
 | ||
| 
 | ||
| func TestNetwork(t *testing.T) {
 | ||
| 	test(0) // 默认模式,类似 TCP:正常模式,无快速重传,常规流控
 | ||
| 	test(1) // 普通模式,关闭流控等
 | ||
| 	test(2) // 快速模式,所有开关都打开,且关闭流控
 | ||
| }
 | ||
| 
 | ||
| func BenchmarkFlush(b *testing.B) {
 | ||
| 	kcp := NewKCP(1, func(buf []byte, size int) {})
 | ||
| 	kcp.snd_buf = make([]segment, 1024)
 | ||
| 	for k := range kcp.snd_buf {
 | ||
| 		kcp.snd_buf[k].xmit = 1
 | ||
| 		kcp.snd_buf[k].resendts = currentMs() + 10000
 | ||
| 	}
 | ||
| 	b.ResetTimer()
 | ||
| 	b.ReportAllocs()
 | ||
| 	var mu sync.Mutex
 | ||
| 	for i := 0; i < b.N; i++ {
 | ||
| 		mu.Lock()
 | ||
| 		kcp.flush(false)
 | ||
| 		mu.Unlock()
 | ||
| 	}
 | ||
| }
 | 
