improve terminating performance

pull/215/head
v2ray 9 years ago
parent 9a44ff3145
commit c0a1198514
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169

@ -176,6 +176,7 @@ func (this *Connection) Read(b []byte) (int, error) {
if nBytes > 0 {
return nBytes, nil
}
var timer *time.Timer
if !this.rd.IsZero() {
duration := this.rd.Sub(time.Now())
@ -229,7 +230,7 @@ func (this *Connection) SetState(state State) {
current := this.Elapsed()
atomic.StoreInt32((*int32)(&this.state), int32(state))
atomic.StoreUint32(&this.stateBeginTime, current)
log.Info("KCP|Connection: Entering state ", state, " at ", current)
log.Info("KCP|Connection: #", this.conv, " entering state ", state, " at ", current)
switch state {
case StateReadyToClose:
@ -429,12 +430,18 @@ func (this *Connection) flush() {
if this.State() == StateActive && current-atomic.LoadUint32(&this.lastIncomingTime) >= 30000 {
this.Close()
}
if this.State() == StateReadyToClose && this.sendingWorker.IsEmpty() {
this.SetState(StateTerminating)
}
if this.State() == StateTerminating {
this.output.Write(&CmdOnlySegment{
Conv: this.conv,
Cmd: SegmentCommandTerminated,
})
log.Debug("KCP|Connection: #", this.conv, " sending terminating cmd.")
seg := NewCmdOnlySegment()
defer seg.Release()
seg.Conv = this.conv
seg.Cmd = SegmentCommandTerminated
this.output.Write(seg)
this.output.Flush()
if current-atomic.LoadUint32(&this.stateBeginTime) > 8000 {

@ -0,0 +1,77 @@
package kcp_test
import (
"crypto/rand"
"io"
"sync"
"testing"
"time"
v2net "github.com/v2ray/v2ray-core/common/net"
v2nettesting "github.com/v2ray/v2ray-core/common/net/testing"
"github.com/v2ray/v2ray-core/testing/assert"
. "github.com/v2ray/v2ray-core/transport/internet/kcp"
)
func TestDialAndListen(t *testing.T) {
assert := assert.On(t)
port := v2nettesting.PickPort()
listerner, err := NewListener(v2net.LocalHostIP, port)
assert.Error(err).IsNil()
go func() {
for {
conn, err := listerner.Accept()
if err != nil {
break
}
go func() {
payload := make([]byte, 1024)
for {
nBytes, err := conn.Read(payload)
if err != nil {
break
}
for idx, b := range payload[:nBytes] {
payload[idx] = b ^ 'c'
}
conn.Write(payload[:nBytes])
}
conn.Close()
}()
}
}()
wg := new(sync.WaitGroup)
for i := 0; i < 10; i++ {
clientConn, err := DialKCP(v2net.LocalHostIP, v2net.UDPDestination(v2net.LocalHostIP, port))
assert.Error(err).IsNil()
wg.Add(1)
go func() {
clientSend := make([]byte, 1024*1024)
rand.Read(clientSend)
clientConn.Write(clientSend)
clientReceived := make([]byte, 1024*1024)
nBytes, _ := io.ReadFull(clientConn, clientReceived)
assert.Int(nBytes).Equals(len(clientReceived))
clientConn.Close()
clientExpected := make([]byte, 1024*1024)
for idx, b := range clientSend {
clientExpected[idx] = b ^ 'c'
}
assert.Bytes(clientReceived).Equals(clientExpected)
wg.Done()
}()
}
wg.Wait()
time.Sleep(15 * time.Second)
assert.Int(listerner.ActiveConnections()).Equals(0)
listerner.Close()
}

@ -63,6 +63,7 @@ func (this *Listener) OnReceive(payload *alloc.Buffer, src v2net.Destination) {
sourceId := src.NetAddr() + "|" + serial.Uint16ToString(conv)
conn, found := this.sessions[sourceId]
if !found {
log.Debug("KCP|Listener: Creating session with id(", sourceId, ") from ", src)
writer := &Writer{
id: sourceId,
hub: this.hub,
@ -94,6 +95,7 @@ func (this *Listener) Remove(dest string) {
if !this.running {
return
}
log.Debug("KCP|Listener: Removing session ", dest)
delete(this.sessions, dest)
}
@ -130,6 +132,13 @@ func (this *Listener) Close() error {
return nil
}
func (this *Listener) ActiveConnections() int {
this.Lock()
defer this.Unlock()
return len(this.sessions)
}
// Addr returns the listener's network address, The Addr returned is shared by all invocations of Addr, so do not modify it.
func (this *Listener) Addr() net.Addr {
return this.localAddr

@ -40,6 +40,10 @@ func (this *SendingWindow) Len() int {
return int(this.len)
}
func (this *SendingWindow) IsEmpty() bool {
return this.len == 0
}
func (this *SendingWindow) Size() uint32 {
return this.cap
}
@ -64,7 +68,7 @@ func (this *SendingWindow) First() *DataSegment {
}
func (this *SendingWindow) Clear(una uint32) {
for this.Len() > 0 && this.data[this.start].Number < una {
for !this.IsEmpty() && this.data[this.start].Number < una {
this.Remove(0)
}
}
@ -121,7 +125,7 @@ func (this *SendingWindow) HandleFastAck(number uint32) {
}
func (this *SendingWindow) Flush(current uint32, resend uint32, rto uint32, maxInFlightSize uint32) {
if this.Len() == 0 {
if this.IsEmpty() {
return
}
@ -266,7 +270,7 @@ func (this *SendingWorker) ProcessReceivingNextWithoutLock(nextNumber uint32) {
// @Private
func (this *SendingWorker) FindFirstUnacknowledged() {
prevUna := this.firstUnacknowledged
if this.window.Len() > 0 {
if !this.window.IsEmpty() {
this.firstUnacknowledged = this.window.First().Number
} else {
this.firstUnacknowledged = this.nextNumber
@ -410,3 +414,10 @@ func (this *SendingWorker) CloseWrite() {
this.window.Clear(0xFFFFFFFF)
this.queue.Clear()
}
func (this *SendingWorker) IsEmpty() bool {
this.RLock()
defer this.RUnlock()
return this.window.IsEmpty() && this.queue.IsEmpty()
}

Loading…
Cancel
Save