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:
parent
87e1d66ff0
commit
25ef62434a
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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() {
|
||||||
|
53
transport/internet/kcp/receiving.go
Normal file
53
transport/internet/kcp/receiving.go
Normal 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
|
||||||
|
}
|
||||||
|
}
|
32
transport/internet/kcp/receiving_test.go
Normal file
32
transport/internet/kcp/receiving_test.go
Normal 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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user