|
|
@@ -9,6 +9,7 @@ import (
|
|
|
|
|
|
"github.com/v2ray/v2ray-core/common/alloc"
|
|
|
"github.com/v2ray/v2ray-core/common/log"
|
|
|
+ "github.com/v2ray/v2ray-core/common/signal"
|
|
|
)
|
|
|
|
|
|
var (
|
|
|
@@ -63,6 +64,7 @@ type Connection struct {
|
|
|
chReadEvent chan struct{}
|
|
|
writer io.WriteCloser
|
|
|
since int64
|
|
|
+ terminateOnce signal.Once
|
|
|
}
|
|
|
|
|
|
// NewConnection create a new KCP connection between local and remote.
|
|
|
@@ -76,21 +78,7 @@ func NewConnection(conv uint32, writerCloser io.WriteCloser, local *net.UDPAddr,
|
|
|
conn.since = nowMillisec()
|
|
|
|
|
|
mtu := uint32(effectiveConfig.Mtu - block.HeaderSize() - headerSize)
|
|
|
- conn.kcp = NewKCP(conv, mtu, func(buf []byte, size int) {
|
|
|
- if size >= IKCP_OVERHEAD {
|
|
|
- ext := alloc.NewBuffer().Clear().Append(buf[:size])
|
|
|
- cmd := CommandData
|
|
|
- opt := Option(0)
|
|
|
- if conn.state == ConnStateReadyToClose {
|
|
|
- opt = OptionClose
|
|
|
- }
|
|
|
- ext.Prepend([]byte{byte(cmd), byte(opt)})
|
|
|
- go conn.output(ext)
|
|
|
- }
|
|
|
- if conn.state == ConnStateReadyToClose && conn.kcp.WaitSnd() == 0 {
|
|
|
- go conn.NotifyTermination()
|
|
|
- }
|
|
|
- })
|
|
|
+ conn.kcp = NewKCP(conv, mtu, conn.output)
|
|
|
conn.kcp.WndSize(effectiveConfig.Sndwnd, effectiveConfig.Rcvwnd)
|
|
|
conn.kcp.NoDelay(1, 20, 2, 1)
|
|
|
conn.kcp.current = conn.Elapsed()
|
|
|
@@ -199,7 +187,7 @@ func (this *Connection) NotifyTermination() {
|
|
|
this.RUnlock()
|
|
|
buffer := alloc.NewSmallBuffer().Clear()
|
|
|
buffer.AppendBytes(byte(CommandTerminate), byte(OptionClose), byte(0), byte(0), byte(0), byte(0))
|
|
|
- this.output(buffer)
|
|
|
+ this.outputBuffer(buffer)
|
|
|
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
|
@@ -207,6 +195,19 @@ func (this *Connection) NotifyTermination() {
|
|
|
this.Terminate()
|
|
|
}
|
|
|
|
|
|
+func (this *Connection) ForceTimeout() {
|
|
|
+ if this == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ for i := 0; i < 5; i++ {
|
|
|
+ if this.state == ConnStateClosed {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ time.Sleep(time.Minute)
|
|
|
+ }
|
|
|
+ go this.terminateOnce.Do(this.NotifyTermination)
|
|
|
+}
|
|
|
+
|
|
|
// Close closes the connection.
|
|
|
func (this *Connection) Close() error {
|
|
|
if this == nil || this.state == ConnStateClosed || this.state == ConnStateReadyToClose {
|
|
|
@@ -219,7 +220,9 @@ func (this *Connection) Close() error {
|
|
|
if this.state == ConnStateActive {
|
|
|
this.state = ConnStateReadyToClose
|
|
|
if this.kcp.WaitSnd() == 0 {
|
|
|
- go this.NotifyTermination()
|
|
|
+ go this.terminateOnce.Do(this.NotifyTermination)
|
|
|
+ } else {
|
|
|
+ go this.ForceTimeout()
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -280,7 +283,7 @@ func (this *Connection) SetWriteDeadline(t time.Time) error {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
-func (this *Connection) output(payload *alloc.Buffer) {
|
|
|
+func (this *Connection) outputBuffer(payload *alloc.Buffer) {
|
|
|
defer payload.Release()
|
|
|
if this == nil {
|
|
|
return
|
|
|
@@ -296,6 +299,29 @@ func (this *Connection) output(payload *alloc.Buffer) {
|
|
|
this.writer.Write(payload.Value)
|
|
|
}
|
|
|
|
|
|
+func (this *Connection) output(payload []byte) {
|
|
|
+ if this == nil || this.state == ConnStateClosed {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ if this.state == ConnStateReadyToClose && this.kcp.WaitSnd() == 0 {
|
|
|
+ go this.terminateOnce.Do(this.NotifyTermination)
|
|
|
+ }
|
|
|
+
|
|
|
+ if len(payload) < IKCP_OVERHEAD {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ buffer := alloc.NewBuffer().Clear().Append(payload)
|
|
|
+ cmd := CommandData
|
|
|
+ opt := Option(0)
|
|
|
+ if this.state == ConnStateReadyToClose {
|
|
|
+ opt = OptionClose
|
|
|
+ }
|
|
|
+ buffer.Prepend([]byte{byte(cmd), byte(opt)})
|
|
|
+ this.outputBuffer(buffer)
|
|
|
+}
|
|
|
+
|
|
|
// kcp update, input loop
|
|
|
func (this *Connection) updateTask() {
|
|
|
for this.state != ConnStateClosed {
|