1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2025-01-03 07:56:42 -05:00

high performance receiving window

This commit is contained in:
v2ray 2016-06-25 21:35:18 +02:00
parent 87e1d66ff0
commit 25ef62434a
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
6 changed files with 132 additions and 93 deletions

View File

@ -1,10 +1,10 @@
package kcp package kcp
type Config struct { type Config struct {
Mtu int // Maximum transmission unit Mtu uint32 // Maximum transmission unit
Tti int Tti uint32
UplinkCapacity int UplinkCapacity uint32
DownlinkCapacity int DownlinkCapacity uint32
Congestion bool Congestion bool
WriteBuffer int WriteBuffer int
} }
@ -13,11 +13,11 @@ func (this *Config) Apply() {
effectiveConfig = *this effectiveConfig = *this
} }
func (this *Config) GetSendingWindowSize() int { func (this *Config) GetSendingWindowSize() uint32 {
return this.UplinkCapacity * 1024 * 1024 / this.Mtu / (1000 / this.Tti) return this.UplinkCapacity * 1024 * 1024 / this.Mtu / (1000 / this.Tti)
} }
func (this *Config) GetReceivingWindowSize() int { func (this *Config) GetReceivingWindowSize() uint32 {
return this.DownlinkCapacity * 1024 * 1024 / this.Mtu / (1000 / this.Tti) return this.DownlinkCapacity * 1024 * 1024 / this.Mtu / (1000 / this.Tti)
} }

View File

@ -11,11 +11,11 @@ import (
func (this *Config) UnmarshalJSON(data []byte) error { func (this *Config) UnmarshalJSON(data []byte) error {
type JSONConfig struct { type JSONConfig struct {
Mtu *int `json:"mtu"` Mtu *uint32 `json:"mtu"`
Tti *int `json:"tti"` Tti *uint32 `json:"tti"`
UpCap *int `json:"uplinkCapacity"` UpCap *uint32 `json:"uplinkCapacity"`
DownCap *int `json:"downlinkCapacity"` DownCap *uint32 `json:"downlinkCapacity"`
Congestion *bool `json:"congestion"` Congestion *bool `json:"congestion"`
} }
jsonConfig := new(JSONConfig) jsonConfig := new(JSONConfig)
if err := json.Unmarshal(data, &jsonConfig); err != nil { if err := json.Unmarshal(data, &jsonConfig); err != nil {
@ -39,7 +39,7 @@ func (this *Config) UnmarshalJSON(data []byte) error {
} }
if jsonConfig.UpCap != nil { if jsonConfig.UpCap != nil {
upCap := *jsonConfig.UpCap upCap := *jsonConfig.UpCap
if upCap < 0 { if upCap == 0 {
log.Error("KCP|Config: Invalid uplink capacity: ", upCap) log.Error("KCP|Config: Invalid uplink capacity: ", upCap)
return common.ErrBadConfiguration return common.ErrBadConfiguration
} }
@ -47,7 +47,7 @@ func (this *Config) UnmarshalJSON(data []byte) error {
} }
if jsonConfig.DownCap != nil { if jsonConfig.DownCap != nil {
downCap := *jsonConfig.DownCap downCap := *jsonConfig.DownCap
if downCap < 0 { if downCap == 0 {
log.Error("KCP|Config: Invalid downlink capacity: ", downCap) log.Error("KCP|Config: Invalid downlink capacity: ", downCap)
return common.ErrBadConfiguration return common.ErrBadConfiguration
} }

View File

@ -20,7 +20,7 @@ var (
) )
const ( const (
headerSize = 2 headerSize uint32 = 2
) )
type Command byte type Command byte
@ -65,7 +65,7 @@ type Connection struct {
writer io.WriteCloser writer io.WriteCloser
since int64 since int64
terminateOnce signal.Once terminateOnce signal.Once
writeBufferSize int writeBufferSize uint32
} }
// NewConnection create a new KCP connection between local and remote. // NewConnection create a new KCP connection between local and remote.
@ -78,12 +78,11 @@ func NewConnection(conv uint32, writerCloser io.WriteCloser, local *net.UDPAddr,
conn.writer = writerCloser conn.writer = writerCloser
conn.since = nowMillisec() conn.since = nowMillisec()
mtu := uint32(effectiveConfig.Mtu - block.HeaderSize() - headerSize) mtu := effectiveConfig.Mtu - uint32(block.HeaderSize()) - headerSize
conn.kcp = NewKCP(conv, mtu, conn.output) conn.kcp = NewKCP(conv, mtu, effectiveConfig.GetSendingWindowSize(), effectiveConfig.GetReceivingWindowSize(), conn.output)
conn.kcp.WndSize(effectiveConfig.GetSendingWindowSize(), effectiveConfig.GetReceivingWindowSize())
conn.kcp.NoDelay(effectiveConfig.Tti, 2, effectiveConfig.Congestion) conn.kcp.NoDelay(effectiveConfig.Tti, 2, effectiveConfig.Congestion)
conn.kcp.current = conn.Elapsed() conn.kcp.current = conn.Elapsed()
conn.writeBufferSize = effectiveConfig.WriteBuffer / effectiveConfig.Mtu conn.writeBufferSize = uint32(effectiveConfig.WriteBuffer) / effectiveConfig.Mtu
go conn.updateTask() go conn.updateTask()

View File

@ -149,7 +149,7 @@ type KCP struct {
snd_queue []*Segment snd_queue []*Segment
rcv_queue []*Segment rcv_queue []*Segment
snd_buf []*Segment snd_buf []*Segment
rcv_buf []*Segment rcv_buf *ReceivingWindow
acklist []uint32 acklist []uint32
@ -161,11 +161,11 @@ type KCP struct {
// NewKCP create a new kcp control object, 'conv' must equal in two endpoint // NewKCP create a new kcp control object, 'conv' must equal in two endpoint
// from the same connection. // from the same connection.
func NewKCP(conv uint32, mtu uint32, output Output) *KCP { func NewKCP(conv uint32, mtu uint32, sendingWindowSize uint32, receivingWindowSize uint32, output Output) *KCP {
kcp := new(KCP) kcp := new(KCP)
kcp.conv = conv kcp.conv = conv
kcp.snd_wnd = IKCP_WND_SND kcp.snd_wnd = sendingWindowSize
kcp.rcv_wnd = IKCP_WND_RCV kcp.rcv_wnd = receivingWindowSize
kcp.rmt_wnd = IKCP_WND_RCV kcp.rmt_wnd = IKCP_WND_RCV
kcp.mtu = mtu kcp.mtu = mtu
kcp.mss = kcp.mtu - IKCP_OVERHEAD kcp.mss = kcp.mtu - IKCP_OVERHEAD
@ -176,6 +176,7 @@ func NewKCP(conv uint32, mtu uint32, output Output) *KCP {
kcp.ssthresh = IKCP_THRESH_INIT kcp.ssthresh = IKCP_THRESH_INIT
kcp.dead_link = IKCP_DEADLINK kcp.dead_link = IKCP_DEADLINK
kcp.output = output kcp.output = output
kcp.rcv_buf = NewReceivingWindow(receivingWindowSize)
return kcp return kcp
} }
@ -205,19 +206,7 @@ func (kcp *KCP) Recv(buffer []byte) (n int) {
} }
kcp.rcv_queue = kcp.rcv_queue[count:] kcp.rcv_queue = kcp.rcv_queue[count:]
// move available data from rcv_buf -> rcv_queue kcp.DumpReceivingBuf()
count = 0
for _, seg := range kcp.rcv_buf {
if seg.sn == kcp.rcv_nxt && len(kcp.rcv_queue) < int(kcp.rcv_wnd) {
kcp.rcv_queue = append(kcp.rcv_queue, seg)
kcp.rcv_nxt++
count++
} else {
break
}
}
kcp.rcv_buf = kcp.rcv_buf[count:]
// fast recover // fast recover
if len(kcp.rcv_queue) < int(kcp.rcv_wnd) && fast_recover { if len(kcp.rcv_queue) < int(kcp.rcv_wnd) && fast_recover {
// ready to send back IKCP_CMD_WINS in ikcp_flush // ready to send back IKCP_CMD_WINS in ikcp_flush
@ -227,6 +216,20 @@ func (kcp *KCP) Recv(buffer []byte) (n int) {
return return
} }
// DumpReceivingBuf moves available data from rcv_buf -> rcv_queue
// @Private
func (kcp *KCP) DumpReceivingBuf() {
for {
seg := kcp.rcv_buf.RemoveFirst()
if seg == nil {
break
}
kcp.rcv_queue = append(kcp.rcv_queue, seg)
kcp.rcv_buf.Advance()
kcp.rcv_nxt++
}
}
// Send is user/upper level send, returns below zero for error // Send is user/upper level send, returns below zero for error
func (kcp *KCP) Send(buffer []byte) int { func (kcp *KCP) Send(buffer []byte) int {
var count int var count int
@ -357,43 +360,12 @@ func (kcp *KCP) parse_data(newseg *Segment) {
return return
} }
n := len(kcp.rcv_buf) - 1 idx := sn - kcp.rcv_nxt
insert_idx := 0 if !kcp.rcv_buf.Set(idx, newseg) {
repeat := false newseg.Release()
for i := n; i >= 0; i-- {
seg := kcp.rcv_buf[i]
if seg.sn == sn {
repeat = true
break
}
if _itimediff(sn, seg.sn) > 0 {
insert_idx = i + 1
break
}
} }
if !repeat { kcp.DumpReceivingBuf()
if insert_idx == n+1 {
kcp.rcv_buf = append(kcp.rcv_buf, newseg)
} else {
kcp.rcv_buf = append(kcp.rcv_buf, &Segment{})
copy(kcp.rcv_buf[insert_idx+1:], kcp.rcv_buf[insert_idx:])
kcp.rcv_buf[insert_idx] = newseg
}
}
// move available data from rcv_buf -> rcv_queue
count := 0
for k, seg := range kcp.rcv_buf {
if seg.sn == kcp.rcv_nxt && len(kcp.rcv_queue) < int(kcp.rcv_wnd) {
kcp.rcv_queue = append(kcp.rcv_queue, kcp.rcv_buf[k])
kcp.rcv_nxt++
count++
} else {
break
}
}
kcp.rcv_buf = kcp.rcv_buf[count:]
} }
// Input when you received a low level packet (eg. UDP packet), call it // Input when you received a low level packet (eg. UDP packet), call it
@ -790,15 +762,9 @@ func (kcp *KCP) Check(current uint32) uint32 {
// interval: internal update timer interval in millisec, default is 100ms // interval: internal update timer interval in millisec, default is 100ms
// resend: 0:disable fast resend(default), 1:enable fast resend // resend: 0:disable fast resend(default), 1:enable fast resend
// nc: 0:normal congestion control(default), 1:disable congestion control // nc: 0:normal congestion control(default), 1:disable congestion control
func (kcp *KCP) NoDelay(interval, resend int, congestionControl bool) int { func (kcp *KCP) NoDelay(interval uint32, resend int, congestionControl bool) int {
if interval >= 0 { kcp.interval = interval
if interval > 5000 {
interval = 5000
} else if interval < 10 {
interval = 10
}
kcp.interval = uint32(interval)
}
if resend >= 0 { if resend >= 0 {
kcp.fastresend = int32(resend) kcp.fastresend = int32(resend)
} }
@ -806,20 +772,9 @@ func (kcp *KCP) NoDelay(interval, resend int, congestionControl bool) int {
return 0 return 0
} }
// WndSize sets maximum window size: sndwnd=32, rcvwnd=32 by default
func (kcp *KCP) WndSize(sndwnd, rcvwnd int) int {
if sndwnd > 0 {
kcp.snd_wnd = uint32(sndwnd)
}
if rcvwnd > 0 {
kcp.rcv_wnd = uint32(rcvwnd)
}
return 0
}
// WaitSnd gets how many packet is waiting to be sent // WaitSnd gets how many packet is waiting to be sent
func (kcp *KCP) WaitSnd() int { func (kcp *KCP) WaitSnd() uint32 {
return len(kcp.snd_buf) + len(kcp.snd_queue) return uint32(len(kcp.snd_buf) + len(kcp.snd_queue))
} }
func (this *KCP) ClearSendQueue() { func (this *KCP) ClearSendQueue() {

View File

@ -0,0 +1,53 @@
package kcp
type ReceivingWindow struct {
start uint32
size uint32
list []*Segment
}
func NewReceivingWindow(size uint32) *ReceivingWindow {
return &ReceivingWindow{
start: 0,
size: size,
list: make([]*Segment, size),
}
}
func (this *ReceivingWindow) Size() uint32 {
return this.size
}
func (this *ReceivingWindow) Position(idx uint32) uint32 {
return (idx + this.start) % this.size
}
func (this *ReceivingWindow) Set(idx uint32, value *Segment) bool {
pos := this.Position(idx)
if this.list[pos] != nil {
return false
}
this.list[pos] = value
return true
}
func (this *ReceivingWindow) Remove(idx uint32) *Segment {
pos := this.Position(idx)
if this.list[pos] == nil {
return nil
}
e := this.list[pos]
this.list[pos] = nil
return e
}
func (this *ReceivingWindow) RemoveFirst() *Segment {
return this.Remove(0)
}
func (this *ReceivingWindow) Advance() {
this.start++
if this.start == this.size {
this.start = 0
}
}

View File

@ -0,0 +1,32 @@
package kcp_test
import (
"testing"
"github.com/v2ray/v2ray-core/testing/assert"
. "github.com/v2ray/v2ray-core/transport/internet/kcp"
)
func TestRecivingWindow(t *testing.T) {
assert := assert.On(t)
window := NewReceivingWindow(3)
seg0 := &Segment{}
seg1 := &Segment{}
seg2 := &Segment{}
seg3 := &Segment{}
assert.Bool(window.Set(0, seg0)).IsTrue()
assert.Pointer(window.RemoveFirst()).Equals(seg0)
assert.Bool(window.Set(1, seg1)).IsTrue()
assert.Bool(window.Set(2, seg2)).IsTrue()
window.Advance()
assert.Bool(window.Set(2, seg3)).IsTrue()
assert.Pointer(window.RemoveFirst()).Equals(seg1)
assert.Pointer(window.Remove(1)).Equals(seg2)
assert.Pointer(window.Remove(2)).Equals(seg3)
}