Chris Lu
4 years ago
16 changed files with 2711 additions and 24 deletions
-
2go.mod
-
4go.sum
-
37weed/command/volume.go
-
715weed/udptransfer/conn.go
-
86weed/udptransfer/conn_test.go
-
95weed/udptransfer/debug.go
-
339weed/udptransfer/endpoint.go
-
42weed/udptransfer/endpoint_test.go
-
333weed/udptransfer/linked.go
-
236weed/udptransfer/linked_test.go
-
142weed/udptransfer/packet.go
-
516weed/udptransfer/state.go
-
36weed/udptransfer/stopwatch.go
-
18weed/udptransfer/timer.go
-
32weed/udptransfer/timing_test.go
-
102weed/wdclient/volume_udp_client.go
@ -0,0 +1,715 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"encoding/binary" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"log" |
||||
|
"net" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_MAX_RETRIES = 6 |
||||
|
_MIN_RTT = 8 |
||||
|
_MIN_RTO = 30 |
||||
|
_MIN_ATO = 2 |
||||
|
_MAX_ATO = 10 |
||||
|
_MIN_SWND = 10 |
||||
|
_MAX_SWND = 960 |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_VACK_SCHED = iota + 1 |
||||
|
_VACK_QUICK |
||||
|
_VACK_MUST |
||||
|
_VSWND_ACTIVE |
||||
|
_VRETR_IMMED |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_RETR_REST = -1 |
||||
|
_CLOSE = 0xff |
||||
|
) |
||||
|
|
||||
|
var debug int |
||||
|
|
||||
|
func nodeOf(pk *packet) *qNode { |
||||
|
return &qNode{packet: pk} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) internalRecvLoop() { |
||||
|
defer func() { |
||||
|
// avoid send to closed channel while some replaying
|
||||
|
// data packets were received in shutting down.
|
||||
|
_ = recover() |
||||
|
}() |
||||
|
var buf, body []byte |
||||
|
for { |
||||
|
select { |
||||
|
case buf = <-c.evRecv: |
||||
|
if buf != nil { |
||||
|
body = buf[_TH_SIZE:] |
||||
|
} else { // shutdown
|
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
pk := new(packet) |
||||
|
// keep the original buffer, so we could recycle it in future
|
||||
|
pk.buffer = buf |
||||
|
unmarshall(pk, body) |
||||
|
if pk.flag&_F_SACK != 0 { |
||||
|
c.processSAck(pk) |
||||
|
continue |
||||
|
} |
||||
|
if pk.flag&_F_ACK != 0 { |
||||
|
c.processAck(pk) |
||||
|
} |
||||
|
if pk.flag&_F_DATA != 0 { |
||||
|
c.insertData(pk) |
||||
|
} else if pk.flag&_F_FIN != 0 { |
||||
|
if pk.flag&_F_RESET != 0 { |
||||
|
go c.forceShutdownWithLock() |
||||
|
} else { |
||||
|
go c.closeR(pk) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) internalSendLoop() { |
||||
|
var timer = time.NewTimer(time.Duration(c.rtt) * time.Millisecond) |
||||
|
for { |
||||
|
select { |
||||
|
case v := <-c.evSWnd: |
||||
|
switch v { |
||||
|
case _VRETR_IMMED: |
||||
|
c.outlock.Lock() |
||||
|
c.retransmit2() |
||||
|
c.outlock.Unlock() |
||||
|
case _VSWND_ACTIVE: |
||||
|
timer.Reset(time.Duration(c.rtt) * time.Millisecond) |
||||
|
case _CLOSE: |
||||
|
return |
||||
|
} |
||||
|
case <-timer.C: // timeout yet
|
||||
|
var notifySender bool |
||||
|
c.outlock.Lock() |
||||
|
rest, _ := c.retransmit() |
||||
|
switch rest { |
||||
|
case _RETR_REST, 0: // nothing to send
|
||||
|
if c.outQ.size() > 0 { |
||||
|
timer.Reset(time.Duration(c.rtt) * time.Millisecond) |
||||
|
} else { |
||||
|
timer.Stop() |
||||
|
// avoid sender blocking
|
||||
|
notifySender = true |
||||
|
} |
||||
|
default: // recent rto point
|
||||
|
timer.Reset(time.Duration(minI64(rest, c.rtt)) * time.Millisecond) |
||||
|
} |
||||
|
c.outlock.Unlock() |
||||
|
if notifySender { |
||||
|
select { |
||||
|
case c.evSend <- 1: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) internalAckLoop() { |
||||
|
// var ackTimer = time.NewTicker(time.Duration(c.ato))
|
||||
|
var ackTimer = time.NewTimer(time.Duration(c.ato) * time.Millisecond) |
||||
|
var lastAckState byte |
||||
|
for { |
||||
|
var v byte |
||||
|
select { |
||||
|
case <-ackTimer.C: |
||||
|
// may cause sending duplicated ack if ato>rtt
|
||||
|
v = _VACK_QUICK |
||||
|
case v = <-c.evAck: |
||||
|
ackTimer.Reset(time.Duration(c.ato) * time.Millisecond) |
||||
|
state := lastAckState |
||||
|
lastAckState = v |
||||
|
if state != v { |
||||
|
if v == _CLOSE { |
||||
|
return |
||||
|
} |
||||
|
v = _VACK_MUST |
||||
|
} |
||||
|
} |
||||
|
c.inlock.Lock() |
||||
|
if pkAck := c.makeAck(v); pkAck != nil { |
||||
|
c.internalWrite(nodeOf(pkAck)) |
||||
|
} |
||||
|
c.inlock.Unlock() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) retransmit() (rest int64, count int32) { |
||||
|
var now, rto = Now(), c.rto |
||||
|
var limit = c.cwnd |
||||
|
for item := c.outQ.head; item != nil && limit > 0; item = item.next { |
||||
|
if item.scnt != _SENT_OK { // ACKed has scnt==-1
|
||||
|
diff := now - item.sent |
||||
|
if diff > rto { // already rto
|
||||
|
c.internalWrite(item) |
||||
|
count++ |
||||
|
} else { |
||||
|
// continue search next min rto duration
|
||||
|
if rest > 0 { |
||||
|
rest = minI64(rest, rto-diff+1) |
||||
|
} else { |
||||
|
rest = rto - diff + 1 |
||||
|
} |
||||
|
limit-- |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
c.outDupCnt += int(count) |
||||
|
if count > 0 { |
||||
|
shrcond := (c.fastRetransmit && count > maxI32(c.cwnd>>5, 4)) || (!c.fastRetransmit && count > c.cwnd>>3) |
||||
|
if shrcond && now-c.lastShrink > c.rto { |
||||
|
log.Printf("shrink cwnd from=%d to=%d s/4=%d", c.cwnd, c.cwnd>>1, c.swnd>>2) |
||||
|
c.lastShrink = now |
||||
|
// shrink cwnd and ensure cwnd >= swnd/4
|
||||
|
if c.cwnd > c.swnd>>1 { |
||||
|
c.cwnd >>= 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if c.outQ.size() > 0 { |
||||
|
return |
||||
|
} |
||||
|
return _RETR_REST, 0 |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) retransmit2() (count int32) { |
||||
|
var limit, now = minI32(c.outPending>>4, 8), Now() |
||||
|
var fRtt = c.rtt |
||||
|
if now-c.lastShrink > c.rto { |
||||
|
fRtt += maxI64(c.rtt>>4, 1) |
||||
|
} else { |
||||
|
fRtt += maxI64(c.rtt>>1, 2) |
||||
|
} |
||||
|
for item := c.outQ.head; item != nil && count < limit; item = item.next { |
||||
|
if item.scnt != _SENT_OK { // ACKed has scnt==-1
|
||||
|
if item.miss >= 3 && now-item.sent >= fRtt { |
||||
|
item.miss = 0 |
||||
|
c.internalWrite(item) |
||||
|
count++ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
c.fRCnt += int(count) |
||||
|
c.outDupCnt += int(count) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) inputAndSend(pk *packet) error { |
||||
|
item := &qNode{packet: pk} |
||||
|
if c.mySeq&3 == 1 { |
||||
|
c.tSlotT0 = NowNS() |
||||
|
} |
||||
|
c.outlock.Lock() |
||||
|
// inflight packets exceeds cwnd
|
||||
|
// inflight includes: 1, unacked; 2, missed
|
||||
|
for c.outPending >= c.cwnd+c.missed { |
||||
|
c.outlock.Unlock() |
||||
|
if c.wtmo > 0 { |
||||
|
var tmo int64 |
||||
|
tmo, c.wtmo = c.wtmo, 0 |
||||
|
select { |
||||
|
case v := <-c.evSend: |
||||
|
if v == _CLOSE { |
||||
|
return io.EOF |
||||
|
} |
||||
|
case <-NewTimerChan(tmo): |
||||
|
return ErrIOTimeout |
||||
|
} |
||||
|
} else { |
||||
|
if v := <-c.evSend; v == _CLOSE { |
||||
|
return io.EOF |
||||
|
} |
||||
|
} |
||||
|
c.outlock.Lock() |
||||
|
} |
||||
|
c.outPending++ |
||||
|
c.outPkCnt++ |
||||
|
c.mySeq++ |
||||
|
pk.seq = c.mySeq |
||||
|
c.outQ.appendTail(item) |
||||
|
c.internalWrite(item) |
||||
|
c.outlock.Unlock() |
||||
|
// active resending timer, must blocking
|
||||
|
c.evSWnd <- _VSWND_ACTIVE |
||||
|
if c.mySeq&3 == 0 && c.flatTraffic { |
||||
|
// calculate time error bewteen tslot with actual usage.
|
||||
|
// consider last sleep time error
|
||||
|
t1 := NowNS() |
||||
|
terr := c.tSlot<<2 - c.lastSErr - (t1 - c.tSlotT0) |
||||
|
// rest terr/2 if current time usage less than tslot of 100us.
|
||||
|
if terr > 1e5 { // 100us
|
||||
|
time.Sleep(time.Duration(terr >> 1)) |
||||
|
c.lastSErr = maxI64(NowNS()-t1-terr, 0) |
||||
|
} else { |
||||
|
c.lastSErr >>= 1 |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) internalWrite(item *qNode) { |
||||
|
if item.scnt >= 20 { |
||||
|
// no exception of sending fin
|
||||
|
if item.flag&_F_FIN != 0 { |
||||
|
c.fakeShutdown() |
||||
|
c.dest = nil |
||||
|
return |
||||
|
} else { |
||||
|
log.Println("Warn: too many retries", item) |
||||
|
if c.urgent > 0 { // abort
|
||||
|
c.forceShutdown() |
||||
|
return |
||||
|
} else { // continue to retry 10
|
||||
|
c.urgent++ |
||||
|
item.scnt = 10 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// update current sent time and prev sent time
|
||||
|
item.sent, item.sent_1 = Now(), item.sent |
||||
|
item.scnt++ |
||||
|
buf := item.marshall(c.connID) |
||||
|
if debug >= 3 { |
||||
|
var pkType = packetTypeNames[item.flag] |
||||
|
if item.flag&_F_SACK != 0 { |
||||
|
log.Printf("send %s trp=%d on=%d %x", pkType, item.seq, item.ack, buf[_AH_SIZE+4:]) |
||||
|
} else { |
||||
|
log.Printf("send %s seq=%d ack=%d scnt=%d len=%d", pkType, item.seq, item.ack, item.scnt, len(buf)-_TH_SIZE) |
||||
|
} |
||||
|
} |
||||
|
c.sock.WriteToUDP(buf, c.dest) |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) logAck(ack uint32) { |
||||
|
c.lastAck = ack |
||||
|
c.lastAckTime = Now() |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) makeLastAck() (pk *packet) { |
||||
|
c.inlock.Lock() |
||||
|
defer c.inlock.Unlock() |
||||
|
if Now()-c.lastAckTime < c.rtt { |
||||
|
return nil |
||||
|
} |
||||
|
pk = &packet{ |
||||
|
ack: maxU32(c.lastAck, c.inQ.maxCtnSeq), |
||||
|
flag: _F_ACK, |
||||
|
} |
||||
|
c.logAck(pk.ack) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) makeAck(level byte) (pk *packet) { |
||||
|
now := Now() |
||||
|
if level < _VACK_MUST && now-c.lastAckTime < c.ato { |
||||
|
if level < _VACK_QUICK || now-c.lastAckTime < minI64(c.ato>>2, 1) { |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
// ready Q <-|
|
||||
|
// |-> outQ start (or more right)
|
||||
|
// |-> bitmap start
|
||||
|
// [predecessor] [predecessor+1] [predecessor+2] .....
|
||||
|
var fakeSAck bool |
||||
|
var predecessor = c.inQ.maxCtnSeq |
||||
|
bmap, tbl := c.inQ.makeHolesBitmap(predecessor) |
||||
|
if len(bmap) <= 0 { // fake sack
|
||||
|
bmap = make([]uint64, 1) |
||||
|
bmap[0], tbl = 1, 1 |
||||
|
fakeSAck = true |
||||
|
} |
||||
|
// head 4-byte: TBL:1 | SCNT:1 | DELAY:2
|
||||
|
buf := make([]byte, len(bmap)*8+4) |
||||
|
pk = &packet{ |
||||
|
ack: predecessor + 1, |
||||
|
flag: _F_SACK, |
||||
|
payload: buf, |
||||
|
} |
||||
|
if fakeSAck { |
||||
|
pk.ack-- |
||||
|
} |
||||
|
buf[0] = byte(tbl) |
||||
|
// mark delayed time according to the time reference point
|
||||
|
if trp := c.inQ.lastIns; trp != nil { |
||||
|
delayed := now - trp.sent |
||||
|
if delayed < c.rtt { |
||||
|
pk.seq = trp.seq |
||||
|
pk.flag |= _F_TIME |
||||
|
buf[1] = trp.scnt |
||||
|
if delayed <= 0 { |
||||
|
delayed = 1 |
||||
|
} |
||||
|
binary.BigEndian.PutUint16(buf[2:], uint16(delayed)) |
||||
|
} |
||||
|
} |
||||
|
buf1 := buf[4:] |
||||
|
for i, b := range bmap { |
||||
|
binary.BigEndian.PutUint64(buf1[i*8:], b) |
||||
|
} |
||||
|
c.logAck(predecessor) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func unmarshallSAck(data []byte) (bmap []uint64, tbl uint32, delayed uint16, scnt uint8) { |
||||
|
if len(data) > 0 { |
||||
|
bmap = make([]uint64, len(data)>>3) |
||||
|
} else { |
||||
|
return |
||||
|
} |
||||
|
tbl = uint32(data[0]) |
||||
|
scnt = data[1] |
||||
|
delayed = binary.BigEndian.Uint16(data[2:]) |
||||
|
data = data[4:] |
||||
|
for i := 0; i < len(bmap); i++ { |
||||
|
bmap[i] = binary.BigEndian.Uint64(data[i*8:]) |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func calSwnd(bandwidth, rtt int64) int32 { |
||||
|
w := int32(bandwidth * rtt / (8000 * _MSS)) |
||||
|
if w <= _MAX_SWND { |
||||
|
if w >= _MIN_SWND { |
||||
|
return w |
||||
|
} else { |
||||
|
return _MIN_SWND |
||||
|
} |
||||
|
} else { |
||||
|
return _MAX_SWND |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) measure(seq uint32, delayed int64, scnt uint8) { |
||||
|
target := c.outQ.get(seq) |
||||
|
if target != nil { |
||||
|
var lastSent int64 |
||||
|
switch target.scnt - scnt { |
||||
|
case 0: |
||||
|
// not sent again since this ack was sent out
|
||||
|
lastSent = target.sent |
||||
|
case 1: |
||||
|
// sent again once since this ack was sent out
|
||||
|
// then use prev sent time
|
||||
|
lastSent = target.sent_1 |
||||
|
default: |
||||
|
// can't measure here because the packet was sent too many times
|
||||
|
return |
||||
|
} |
||||
|
// real-time rtt
|
||||
|
rtt := Now() - lastSent - delayed |
||||
|
// reject these abnormal measures:
|
||||
|
// 1. rtt too small -> rtt/8
|
||||
|
// 2. backlogging too long
|
||||
|
if rtt < maxI64(c.rtt>>3, 1) || delayed > c.rtt>>1 { |
||||
|
return |
||||
|
} |
||||
|
// srtt: update 1/8
|
||||
|
err := rtt - (c.srtt >> 3) |
||||
|
c.srtt += err |
||||
|
c.rtt = c.srtt >> 3 |
||||
|
if c.rtt < _MIN_RTT { |
||||
|
c.rtt = _MIN_RTT |
||||
|
} |
||||
|
// s-swnd: update 1/4
|
||||
|
swnd := c.swnd<<3 - c.swnd + calSwnd(c.bandwidth, c.rtt) |
||||
|
c.swnd = swnd >> 3 |
||||
|
c.tSlot = c.rtt * 1e6 / int64(c.swnd) |
||||
|
c.ato = c.rtt >> 4 |
||||
|
if c.ato < _MIN_ATO { |
||||
|
c.ato = _MIN_ATO |
||||
|
} else if c.ato > _MAX_ATO { |
||||
|
c.ato = _MAX_ATO |
||||
|
} |
||||
|
if err < 0 { |
||||
|
err = -err |
||||
|
err -= c.mdev >> 2 |
||||
|
if err > 0 { |
||||
|
err >>= 3 |
||||
|
} |
||||
|
} else { |
||||
|
err -= c.mdev >> 2 |
||||
|
} |
||||
|
// mdev: update 1/4
|
||||
|
c.mdev += err |
||||
|
rto := c.rtt + maxI64(c.rtt<<1, c.mdev) |
||||
|
if rto >= c.rto { |
||||
|
c.rto = rto |
||||
|
} else { |
||||
|
c.rto = (c.rto + rto) >> 1 |
||||
|
} |
||||
|
if c.rto < _MIN_RTO { |
||||
|
c.rto = _MIN_RTO |
||||
|
} |
||||
|
if debug >= 1 { |
||||
|
log.Printf("--- rtt=%d srtt=%d rto=%d swnd=%d", c.rtt, c.srtt, c.rto, c.swnd) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) processSAck(pk *packet) { |
||||
|
c.outlock.Lock() |
||||
|
bmap, tbl, delayed, scnt := unmarshallSAck(pk.payload) |
||||
|
if bmap == nil { // bad packet
|
||||
|
c.outlock.Unlock() |
||||
|
return |
||||
|
} |
||||
|
if pk.flag&_F_TIME != 0 { |
||||
|
c.measure(pk.seq, int64(delayed), scnt) |
||||
|
} |
||||
|
deleted, missed, continuous := c.outQ.deleteByBitmap(bmap, pk.ack, tbl) |
||||
|
if deleted > 0 { |
||||
|
c.ackHit(deleted, missed) |
||||
|
// lock is released
|
||||
|
} else { |
||||
|
c.outlock.Unlock() |
||||
|
} |
||||
|
if c.fastRetransmit && !continuous { |
||||
|
// peer Q is uncontinuous, then trigger FR
|
||||
|
if deleted == 0 { |
||||
|
c.evSWnd <- _VRETR_IMMED |
||||
|
} else { |
||||
|
select { |
||||
|
case c.evSWnd <- _VRETR_IMMED: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
if debug >= 2 { |
||||
|
log.Printf("SACK qhead=%d deleted=%d outPending=%d on=%d %016x", |
||||
|
c.outQ.distanceOfHead(0), deleted, c.outPending, pk.ack, bmap) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) processAck(pk *packet) { |
||||
|
c.outlock.Lock() |
||||
|
if end := c.outQ.get(pk.ack); end != nil { // ack hit
|
||||
|
_, deleted := c.outQ.deleteBefore(end) |
||||
|
c.ackHit(deleted, 0) // lock is released
|
||||
|
if debug >= 2 { |
||||
|
log.Printf("ACK hit on=%d", pk.ack) |
||||
|
} |
||||
|
// special case: ack the FIN
|
||||
|
if pk.seq == _FIN_ACK_SEQ { |
||||
|
select { |
||||
|
case c.evClose <- _S_FIN0: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
} else { // duplicated ack
|
||||
|
if debug >= 2 { |
||||
|
log.Printf("ACK miss on=%d", pk.ack) |
||||
|
} |
||||
|
if pk.flag&_F_SYN != 0 { // No.3 Ack lost
|
||||
|
if pkAck := c.makeLastAck(); pkAck != nil { |
||||
|
c.internalWrite(nodeOf(pkAck)) |
||||
|
} |
||||
|
} |
||||
|
c.outlock.Unlock() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) ackHit(deleted, missed int32) { |
||||
|
// must in outlock
|
||||
|
c.outPending -= deleted |
||||
|
now := Now() |
||||
|
if c.cwnd < c.swnd && now-c.lastShrink > c.rto { |
||||
|
if c.cwnd < c.swnd>>1 { |
||||
|
c.cwnd <<= 1 |
||||
|
} else { |
||||
|
c.cwnd += deleted << 1 |
||||
|
} |
||||
|
} |
||||
|
if c.cwnd > c.swnd { |
||||
|
c.cwnd = c.swnd |
||||
|
} |
||||
|
if now-c.lastRstMis > c.ato { |
||||
|
c.lastRstMis = now |
||||
|
c.missed = missed |
||||
|
} else { |
||||
|
c.missed = c.missed>>1 + missed |
||||
|
} |
||||
|
if qswnd := c.swnd >> 4; c.missed > qswnd { |
||||
|
c.missed = qswnd |
||||
|
} |
||||
|
c.outlock.Unlock() |
||||
|
select { |
||||
|
case c.evSend <- 1: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) insertData(pk *packet) { |
||||
|
c.inlock.Lock() |
||||
|
defer c.inlock.Unlock() |
||||
|
exists := c.inQ.contains(pk.seq) |
||||
|
// duplicated with already queued or history
|
||||
|
// means: last ACK were lost
|
||||
|
if exists || pk.seq <= c.inQ.maxCtnSeq { |
||||
|
// then send ACK for dups
|
||||
|
select { |
||||
|
case c.evAck <- _VACK_MUST: |
||||
|
default: |
||||
|
} |
||||
|
if debug >= 2 { |
||||
|
dumpQ(fmt.Sprint("duplicated ", pk.seq), c.inQ) |
||||
|
} |
||||
|
c.inDupCnt++ |
||||
|
return |
||||
|
} |
||||
|
// record current time in sent and regard as received time
|
||||
|
item := &qNode{packet: pk, sent: Now()} |
||||
|
dis := c.inQ.searchInsert(item, c.lastReadSeq) |
||||
|
if debug >= 3 { |
||||
|
log.Printf("\t\t\trecv DATA seq=%d dis=%d maxCtn=%d lastReadSeq=%d", item.seq, dis, c.inQ.maxCtnSeq, c.lastReadSeq) |
||||
|
} |
||||
|
|
||||
|
var ackState byte = _VACK_MUST |
||||
|
var available bool |
||||
|
switch dis { |
||||
|
case 0: // impossible
|
||||
|
return |
||||
|
case 1: |
||||
|
if c.inQDirty { |
||||
|
available = c.inQ.updateContinuous(item) |
||||
|
if c.inQ.isWholeContinuous() { // whole Q is ordered
|
||||
|
c.inQDirty = false |
||||
|
} else { //those holes still exists.
|
||||
|
ackState = _VACK_QUICK |
||||
|
} |
||||
|
} else { |
||||
|
// here is an ideal situation
|
||||
|
c.inQ.maxCtnSeq = pk.seq |
||||
|
available = true |
||||
|
ackState = _VACK_SCHED |
||||
|
} |
||||
|
|
||||
|
default: // there is an unordered packet, hole occurred here.
|
||||
|
if !c.inQDirty { |
||||
|
c.inQDirty = true |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// write valid received count
|
||||
|
c.inPkCnt++ |
||||
|
c.inQ.lastIns = item |
||||
|
// try notify ack
|
||||
|
select { |
||||
|
case c.evAck <- ackState: |
||||
|
default: |
||||
|
} |
||||
|
if available { // try notify reader
|
||||
|
select { |
||||
|
case c.evRead <- 1: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) readInQ() bool { |
||||
|
c.inlock.Lock() |
||||
|
defer c.inlock.Unlock() |
||||
|
// read already <-|-> expected Q
|
||||
|
// [lastReadSeq] | [lastReadSeq+1] [lastReadSeq+2] ......
|
||||
|
if c.inQ.isEqualsHead(c.lastReadSeq+1) && c.lastReadSeq < c.inQ.maxCtnSeq { |
||||
|
c.lastReadSeq = c.inQ.maxCtnSeq |
||||
|
availabled := c.inQ.get(c.inQ.maxCtnSeq) |
||||
|
availabled, _ = c.inQ.deleteBefore(availabled) |
||||
|
for i := availabled; i != nil; i = i.next { |
||||
|
c.inQReady = append(c.inQReady, i.payload...) |
||||
|
// data was copied, then could recycle memory
|
||||
|
bpool.Put(i.buffer) |
||||
|
i.payload = nil |
||||
|
i.buffer = nil |
||||
|
} |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
// should not call this function concurrently.
|
||||
|
func (c *Conn) Read(buf []byte) (nr int, err error) { |
||||
|
for { |
||||
|
if len(c.inQReady) > 0 { |
||||
|
n := copy(buf, c.inQReady) |
||||
|
c.inQReady = c.inQReady[n:] |
||||
|
return n, nil |
||||
|
} |
||||
|
if !c.readInQ() { |
||||
|
if c.rtmo > 0 { |
||||
|
var tmo int64 |
||||
|
tmo, c.rtmo = c.rtmo, 0 |
||||
|
select { |
||||
|
case _, y := <-c.evRead: |
||||
|
if !y && len(c.inQReady) == 0 { |
||||
|
return 0, io.EOF |
||||
|
} |
||||
|
case <-NewTimerChan(tmo): |
||||
|
return 0, ErrIOTimeout |
||||
|
} |
||||
|
} else { |
||||
|
// only when evRead is closed and inQReady is empty
|
||||
|
// then could reply eof
|
||||
|
if _, y := <-c.evRead; !y && len(c.inQReady) == 0 { |
||||
|
return 0, io.EOF |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// should not call this function concurrently.
|
||||
|
func (c *Conn) Write(data []byte) (nr int, err error) { |
||||
|
for len(data) > 0 && err == nil { |
||||
|
//buf := make([]byte, _MSS+_AH_SIZE)
|
||||
|
buf := bpool.Get(c.mss + _AH_SIZE) |
||||
|
body := buf[_TH_SIZE+_CH_SIZE:] |
||||
|
n := copy(body, data) |
||||
|
nr += n |
||||
|
data = data[n:] |
||||
|
pk := &packet{flag: _F_DATA, payload: body[:n], buffer: buf[:_AH_SIZE+n]} |
||||
|
err = c.inputAndSend(pk) |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) LocalAddr() net.Addr { |
||||
|
return c.sock.LocalAddr() |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) RemoteAddr() net.Addr { |
||||
|
return c.dest |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) SetDeadline(t time.Time) error { |
||||
|
c.SetReadDeadline(t) |
||||
|
c.SetWriteDeadline(t) |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) SetReadDeadline(t time.Time) error { |
||||
|
if d := t.UnixNano()/Millisecond - Now(); d > 0 { |
||||
|
c.rtmo = d |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) SetWriteDeadline(t time.Time) error { |
||||
|
if d := t.UnixNano()/Millisecond - Now(); d > 0 { |
||||
|
c.wtmo = d |
||||
|
} |
||||
|
return nil |
||||
|
} |
@ -0,0 +1,86 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"math/rand" |
||||
|
"sort" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
var conn *Conn |
||||
|
|
||||
|
func init() { |
||||
|
conn = &Conn{ |
||||
|
outQ: newLinkedMap(_QModeOut), |
||||
|
inQ: newLinkedMap(_QModeIn), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func assert(cond bool, t testing.TB, format string, args ...interface{}) { |
||||
|
if !cond { |
||||
|
t.Errorf(format, args...) |
||||
|
panic("last error") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func Test_ordered_insert(t *testing.T) { |
||||
|
data := []byte{1} |
||||
|
var pk *packet |
||||
|
for i := int32(1); i < 33; i++ { |
||||
|
pk = &packet{ |
||||
|
seq: uint32(i), |
||||
|
payload: data, |
||||
|
} |
||||
|
conn.insertData(pk) |
||||
|
assert(conn.inQ.size() == i, t, "len inQ=%d", conn.inQ.size()) |
||||
|
assert(conn.inQ.maxCtnSeq == pk.seq, t, "lastCtnIn") |
||||
|
assert(!conn.inQDirty, t, "dirty") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func Test_unordered_insert(t *testing.T) { |
||||
|
conn.inQ.reset() |
||||
|
|
||||
|
data := []byte{1} |
||||
|
var pk *packet |
||||
|
var seqs = make([]int, 0xfff) |
||||
|
// unordered insert, and assert size
|
||||
|
for i := 1; i < len(seqs); i++ { |
||||
|
var seq uint32 |
||||
|
for conn.inQ.contains(seq) || seq == 0 { |
||||
|
seq = uint32(rand.Int31n(0xFFffff)) |
||||
|
} |
||||
|
seqs[i] = int(seq) |
||||
|
pk = &packet{ |
||||
|
seq: seq, |
||||
|
payload: data, |
||||
|
} |
||||
|
conn.insertData(pk) |
||||
|
assert(conn.inQ.size() == int32(i), t, "i=%d inQ.len=%d", i, conn.inQ.size()) |
||||
|
} |
||||
|
// assert lastCtnSeq
|
||||
|
sort.Ints(seqs) |
||||
|
var zero = 0 |
||||
|
var last *int |
||||
|
for i := 0; i < len(seqs); i++ { |
||||
|
if i == 0 && seqs[0] != 0 { |
||||
|
last = &zero |
||||
|
break |
||||
|
} |
||||
|
if last != nil && seqs[i]-*last > 1 { |
||||
|
if i == 1 { |
||||
|
last = &zero |
||||
|
} |
||||
|
break |
||||
|
} |
||||
|
last = &seqs[i] |
||||
|
} |
||||
|
if *last != int(conn.inQ.maxCtnSeq) { |
||||
|
for i, j := range seqs { |
||||
|
if i < 10 { |
||||
|
t.Logf("seq %d", j) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
assert(*last == int(conn.inQ.maxCtnSeq), t, "lastCtnSeq=%d but expected=%d", conn.inQ.maxCtnSeq, *last) |
||||
|
t.Logf("lastCtnSeq=%d dirty=%v", conn.inQ.maxCtnSeq, conn.inQDirty) |
||||
|
} |
@ -0,0 +1,95 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"encoding/hex" |
||||
|
"fmt" |
||||
|
"log" |
||||
|
"os" |
||||
|
"os/signal" |
||||
|
"runtime" |
||||
|
"runtime/pprof" |
||||
|
"sync/atomic" |
||||
|
"syscall" |
||||
|
) |
||||
|
|
||||
|
var enable_pprof bool |
||||
|
var enable_stacktrace bool |
||||
|
var debug_inited int32 |
||||
|
|
||||
|
func set_debug_params(p *Params) { |
||||
|
if atomic.CompareAndSwapInt32(&debug_inited, 0, 1) { |
||||
|
debug = p.Debug |
||||
|
enable_pprof = p.EnablePprof |
||||
|
enable_stacktrace = p.Stacktrace |
||||
|
if enable_pprof { |
||||
|
f, err := os.Create("udptransfer.pprof") |
||||
|
if err != nil { |
||||
|
log.Fatalln(err) |
||||
|
} |
||||
|
pprof.StartCPUProfile(f) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) PrintState() { |
||||
|
log.Printf("inQ=%d inQReady=%d outQ=%d", c.inQ.size(), len(c.inQReady), c.outQ.size()) |
||||
|
log.Printf("inMaxCtnSeq=%d lastAck=%d lastReadSeq=%d", c.inQ.maxCtnSeq, c.lastAck, c.lastReadSeq) |
||||
|
if c.inPkCnt > 0 { |
||||
|
log.Printf("Rx pcnt=%d dups=%d %%d=%f%%", c.inPkCnt, c.inDupCnt, 100*float32(c.inDupCnt)/float32(c.inPkCnt)) |
||||
|
} |
||||
|
if c.outPkCnt > 0 { |
||||
|
log.Printf("Tx pcnt=%d dups=%d %%d=%f%%", c.outPkCnt, c.outDupCnt, 100*float32(c.outDupCnt)/float32(c.outPkCnt)) |
||||
|
} |
||||
|
log.Printf("current-rtt=%d FastRetransmit=%d", c.rtt, c.fRCnt) |
||||
|
if enable_stacktrace { |
||||
|
var buf = make([]byte, 6400) |
||||
|
for i := 0; i < 3; i++ { |
||||
|
n := runtime.Stack(buf, true) |
||||
|
if n >= len(buf) { |
||||
|
buf = make([]byte, len(buf)<<1) |
||||
|
} else { |
||||
|
buf = buf[:n] |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
fmt.Println(string(buf)) |
||||
|
} |
||||
|
if enable_pprof { |
||||
|
pprof.StopCPUProfile() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) internal_state() { |
||||
|
ev := make(chan os.Signal, 10) |
||||
|
signal.Notify(ev, syscall.Signal(12), syscall.SIGINT) |
||||
|
for v := range ev { |
||||
|
c.PrintState() |
||||
|
if v == syscall.SIGINT { |
||||
|
os.Exit(1) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func printBits(b uint64, j, s, d uint32) { |
||||
|
fmt.Printf("bits=%064b j=%d seq=%d dis=%d\n", b, j, s, d) |
||||
|
} |
||||
|
|
||||
|
func dumpb(label string, buf []byte) { |
||||
|
log.Println(label, "\n", hex.Dump(buf)) |
||||
|
} |
||||
|
|
||||
|
func dumpQ(s string, q *linkedMap) { |
||||
|
var seqs = make([]uint32, 0, 20) |
||||
|
n := q.head |
||||
|
for i, m := int32(0), q.size(); i < m && n != nil; i++ { |
||||
|
seqs = append(seqs, n.seq) |
||||
|
n = n.next |
||||
|
if len(seqs) == 20 { |
||||
|
log.Printf("%s: Q=%d", s, seqs) |
||||
|
seqs = seqs[:0] |
||||
|
} |
||||
|
} |
||||
|
if len(seqs) > 0 { |
||||
|
log.Printf("%s: Q=%d", s, seqs) |
||||
|
} |
||||
|
} |
@ -0,0 +1,339 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"encoding/binary" |
||||
|
"fmt" |
||||
|
"io" |
||||
|
"log" |
||||
|
"math/rand" |
||||
|
"net" |
||||
|
"sort" |
||||
|
"sync" |
||||
|
"sync/atomic" |
||||
|
"time" |
||||
|
|
||||
|
"github.com/cloudflare/golibs/bytepool" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_SO_BUF_SIZE = 8 << 20 |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
bpool bytepool.BytePool |
||||
|
) |
||||
|
|
||||
|
type Params struct { |
||||
|
LocalAddr string |
||||
|
Bandwidth int64 |
||||
|
Mtu int |
||||
|
IsServ bool |
||||
|
FastRetransmit bool |
||||
|
FlatTraffic bool |
||||
|
EnablePprof bool |
||||
|
Stacktrace bool |
||||
|
Debug int |
||||
|
} |
||||
|
|
||||
|
type connID struct { |
||||
|
lid uint32 |
||||
|
rid uint32 |
||||
|
} |
||||
|
|
||||
|
type Endpoint struct { |
||||
|
udpconn *net.UDPConn |
||||
|
state int32 |
||||
|
idSeq uint32 |
||||
|
isServ bool |
||||
|
listenChan chan *Conn |
||||
|
lRegistry map[uint32]*Conn |
||||
|
rRegistry map[string][]uint32 |
||||
|
mlock sync.RWMutex |
||||
|
timeout *time.Timer |
||||
|
params Params |
||||
|
} |
||||
|
|
||||
|
func (c *connID) setRid(b []byte) { |
||||
|
c.rid = binary.BigEndian.Uint32(b[_MAGIC_SIZE+6:]) |
||||
|
} |
||||
|
|
||||
|
func init() { |
||||
|
bpool.Init(0, 2000) |
||||
|
rand.Seed(NowNS()) |
||||
|
} |
||||
|
|
||||
|
func NewEndpoint(p *Params) (*Endpoint, error) { |
||||
|
set_debug_params(p) |
||||
|
if p.Bandwidth <= 0 || p.Bandwidth > 100 { |
||||
|
return nil, fmt.Errorf("bw->(0,100]") |
||||
|
} |
||||
|
conn, err := net.ListenPacket("udp", p.LocalAddr) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
e := &Endpoint{ |
||||
|
udpconn: conn.(*net.UDPConn), |
||||
|
idSeq: 1, |
||||
|
isServ: p.IsServ, |
||||
|
listenChan: make(chan *Conn, 1), |
||||
|
lRegistry: make(map[uint32]*Conn), |
||||
|
rRegistry: make(map[string][]uint32), |
||||
|
timeout: time.NewTimer(0), |
||||
|
params: *p, |
||||
|
} |
||||
|
if e.isServ { |
||||
|
e.state = _S_EST0 |
||||
|
} else { // client
|
||||
|
e.state = _S_EST1 |
||||
|
e.idSeq = uint32(rand.Int31()) |
||||
|
} |
||||
|
e.params.Bandwidth = p.Bandwidth << 20 // mbps to bps
|
||||
|
e.udpconn.SetReadBuffer(_SO_BUF_SIZE) |
||||
|
go e.internal_listen() |
||||
|
return e, nil |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) internal_listen() { |
||||
|
const rtmo = time.Duration(30*time.Second) |
||||
|
var id connID |
||||
|
for { |
||||
|
//var buf = make([]byte, 1600)
|
||||
|
var buf = bpool.Get(1600) |
||||
|
e.udpconn.SetReadDeadline(time.Now().Add(rtmo)) |
||||
|
n, addr, err := e.udpconn.ReadFromUDP(buf) |
||||
|
if err == nil && n >= _AH_SIZE { |
||||
|
buf = buf[:n] |
||||
|
e.getConnID(&id, buf) |
||||
|
|
||||
|
switch id.lid { |
||||
|
case 0: // new connection
|
||||
|
if e.isServ { |
||||
|
go e.acceptNewConn(id, addr, buf) |
||||
|
} else { |
||||
|
dumpb("drop", buf) |
||||
|
} |
||||
|
|
||||
|
case _INVALID_SEQ: |
||||
|
dumpb("drop invalid", buf) |
||||
|
|
||||
|
default: // old connection
|
||||
|
e.mlock.RLock() |
||||
|
conn := e.lRegistry[id.lid] |
||||
|
e.mlock.RUnlock() |
||||
|
if conn != nil { |
||||
|
e.dispatch(conn, buf) |
||||
|
} else { |
||||
|
e.resetPeer(addr, id) |
||||
|
dumpb("drop null", buf) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} else if err != nil { |
||||
|
// idle process
|
||||
|
if nerr, y := err.(net.Error); y && nerr.Timeout() { |
||||
|
e.idleProcess() |
||||
|
continue |
||||
|
} |
||||
|
// other errors
|
||||
|
if atomic.LoadInt32(&e.state) == _S_FIN { |
||||
|
return |
||||
|
} else { |
||||
|
log.Println("Error: read sock", err) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) idleProcess() { |
||||
|
// recycle/shrink memory
|
||||
|
bpool.Drain() |
||||
|
e.mlock.Lock() |
||||
|
defer e.mlock.Unlock() |
||||
|
// reset urgent
|
||||
|
for _, c := range e.lRegistry { |
||||
|
c.outlock.Lock() |
||||
|
if c.outQ.size() == 0 && c.urgent != 0 { |
||||
|
c.urgent = 0 |
||||
|
} |
||||
|
c.outlock.Unlock() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) Dial(addr string) (*Conn, error) { |
||||
|
rAddr, err := net.ResolveUDPAddr("udp", addr) |
||||
|
if err != nil { |
||||
|
return nil, err |
||||
|
} |
||||
|
e.mlock.Lock() |
||||
|
e.idSeq++ |
||||
|
id := connID{e.idSeq, 0} |
||||
|
conn := NewConn(e, rAddr, id) |
||||
|
e.lRegistry[id.lid] = conn |
||||
|
e.mlock.Unlock() |
||||
|
if atomic.LoadInt32(&e.state) != _S_FIN { |
||||
|
err = conn.initConnection(nil) |
||||
|
return conn, err |
||||
|
} |
||||
|
return nil, io.EOF |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) acceptNewConn(id connID, addr *net.UDPAddr, buf []byte) { |
||||
|
rKey := addr.String() |
||||
|
e.mlock.Lock() |
||||
|
// map: remoteAddr => remoteConnID
|
||||
|
// filter duplicated syn packets
|
||||
|
if newArr := insertRid(e.rRegistry[rKey], id.rid); newArr != nil { |
||||
|
e.rRegistry[rKey] = newArr |
||||
|
} else { |
||||
|
e.mlock.Unlock() |
||||
|
log.Println("Warn: duplicated connection", addr) |
||||
|
return |
||||
|
} |
||||
|
e.idSeq++ |
||||
|
id.lid = e.idSeq |
||||
|
conn := NewConn(e, addr, id) |
||||
|
e.lRegistry[id.lid] = conn |
||||
|
e.mlock.Unlock() |
||||
|
err := conn.initConnection(buf) |
||||
|
if err == nil { |
||||
|
select { |
||||
|
case e.listenChan <- conn: |
||||
|
case <-time.After(_10ms): |
||||
|
log.Println("Warn: no listener") |
||||
|
} |
||||
|
} else { |
||||
|
e.removeConn(id, addr) |
||||
|
log.Println("Error: init_connection", addr, err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) removeConn(id connID, addr *net.UDPAddr) { |
||||
|
e.mlock.Lock() |
||||
|
delete(e.lRegistry, id.lid) |
||||
|
rKey := addr.String() |
||||
|
if newArr := deleteRid(e.rRegistry[rKey], id.rid); newArr != nil { |
||||
|
if len(newArr) > 0 { |
||||
|
e.rRegistry[rKey] = newArr |
||||
|
} else { |
||||
|
delete(e.rRegistry, rKey) |
||||
|
} |
||||
|
} |
||||
|
e.mlock.Unlock() |
||||
|
} |
||||
|
|
||||
|
// net.Listener
|
||||
|
func (e *Endpoint) Close() error { |
||||
|
state := atomic.LoadInt32(&e.state) |
||||
|
if state > 0 && atomic.CompareAndSwapInt32(&e.state, state, _S_FIN) { |
||||
|
err := e.udpconn.Close() |
||||
|
e.lRegistry = nil |
||||
|
e.rRegistry = nil |
||||
|
select { // release listeners
|
||||
|
case e.listenChan <- nil: |
||||
|
default: |
||||
|
} |
||||
|
return err |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// net.Listener
|
||||
|
func (e *Endpoint) Addr() net.Addr { |
||||
|
return e.udpconn.LocalAddr() |
||||
|
} |
||||
|
|
||||
|
// net.Listener
|
||||
|
func (e *Endpoint) Accept() (net.Conn, error) { |
||||
|
if atomic.LoadInt32(&e.state) == _S_EST0 { |
||||
|
return <-e.listenChan, nil |
||||
|
} else { |
||||
|
return nil, io.EOF |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) Listen() *Conn { |
||||
|
if atomic.LoadInt32(&e.state) == _S_EST0 { |
||||
|
return <-e.listenChan |
||||
|
} else { |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// tmo in MS
|
||||
|
func (e *Endpoint) ListenTimeout(tmo int64) *Conn { |
||||
|
if tmo <= 0 { |
||||
|
return e.Listen() |
||||
|
} |
||||
|
if atomic.LoadInt32(&e.state) == _S_EST0 { |
||||
|
select { |
||||
|
case c := <-e.listenChan: |
||||
|
return c |
||||
|
case <-NewTimerChan(tmo): |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) getConnID(idPtr *connID, buf []byte) { |
||||
|
// TODO determine magic header
|
||||
|
magicAndLen := binary.BigEndian.Uint64(buf) |
||||
|
if int(magicAndLen&0xFFff) == len(buf) { |
||||
|
id := binary.BigEndian.Uint64(buf[_MAGIC_SIZE+2:]) |
||||
|
idPtr.lid = uint32(id >> 32) |
||||
|
idPtr.rid = uint32(id) |
||||
|
} else { |
||||
|
idPtr.lid = _INVALID_SEQ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) dispatch(c *Conn, buf []byte) { |
||||
|
e.timeout.Reset(30*time.Millisecond) |
||||
|
select { |
||||
|
case c.evRecv <- buf: |
||||
|
case <-e.timeout.C: |
||||
|
log.Println("Warn: dispatch packet failed") |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (e *Endpoint) resetPeer(addr *net.UDPAddr, id connID) { |
||||
|
pk := &packet{flag: _F_FIN | _F_RESET} |
||||
|
buf := nodeOf(pk).marshall(id) |
||||
|
e.udpconn.WriteToUDP(buf, addr) |
||||
|
} |
||||
|
|
||||
|
type u32Slice []uint32 |
||||
|
|
||||
|
func (p u32Slice) Len() int { return len(p) } |
||||
|
func (p u32Slice) Less(i, j int) bool { return p[i] < p[j] } |
||||
|
func (p u32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } |
||||
|
|
||||
|
// if the rid is not existed in array then insert it return new array
|
||||
|
func insertRid(array []uint32, rid uint32) []uint32 { |
||||
|
if len(array) > 0 { |
||||
|
pos := sort.Search(len(array), func(n int) bool { |
||||
|
return array[n] >= rid |
||||
|
}) |
||||
|
if pos < len(array) && array[pos] == rid { |
||||
|
return nil |
||||
|
} |
||||
|
} |
||||
|
array = append(array, rid) |
||||
|
sort.Sort(u32Slice(array)) |
||||
|
return array |
||||
|
} |
||||
|
|
||||
|
// if rid was existed in array then delete it return new array
|
||||
|
func deleteRid(array []uint32, rid uint32) []uint32 { |
||||
|
if len(array) > 0 { |
||||
|
pos := sort.Search(len(array), func(n int) bool { |
||||
|
return array[n] >= rid |
||||
|
}) |
||||
|
if pos < len(array) && array[pos] == rid { |
||||
|
newArray := make([]uint32, len(array)-1) |
||||
|
n := copy(newArray, array[:pos]) |
||||
|
copy(newArray[n:], array[pos+1:]) |
||||
|
return newArray |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"math/rand" |
||||
|
"sort" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
func Test_insert_delete_rid(t *testing.T) { |
||||
|
var a []uint32 |
||||
|
var b = make([]uint32, 0, 1e3) |
||||
|
var uniq = make(map[uint32]int) |
||||
|
// insert into a with random
|
||||
|
for i := 0; i < cap(b); i++ { |
||||
|
n := uint32(rand.Int31()) |
||||
|
if _, y := uniq[n]; !y { |
||||
|
b = append(b, n) |
||||
|
uniq[n] = 1 |
||||
|
} |
||||
|
dups := 1 |
||||
|
if i&0xf == 0xf { |
||||
|
dups = 3 |
||||
|
} |
||||
|
for j := 0; j < dups; j++ { |
||||
|
if aa := insertRid(a, n); aa != nil { |
||||
|
a = aa |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
sort.Sort(u32Slice(b)) |
||||
|
bStr := fmt.Sprintf("%d", b) |
||||
|
aStr := fmt.Sprintf("%d", a) |
||||
|
assert(aStr == bStr, t, "a!=b") |
||||
|
|
||||
|
for i := 0; i < len(b); i++ { |
||||
|
if aa := deleteRid(a, b[i]); aa != nil { |
||||
|
a = aa |
||||
|
} |
||||
|
} |
||||
|
assert(len(a) == 0, t, "a!=0") |
||||
|
} |
@ -0,0 +1,333 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
type qNode struct { |
||||
|
*packet |
||||
|
prev *qNode |
||||
|
next *qNode |
||||
|
sent int64 // last sent time
|
||||
|
sent_1 int64 // prev sent time
|
||||
|
miss int // sack miss count
|
||||
|
} |
||||
|
|
||||
|
type linkedMap struct { |
||||
|
head *qNode |
||||
|
tail *qNode |
||||
|
qmap map[uint32]*qNode |
||||
|
lastIns *qNode |
||||
|
maxCtnSeq uint32 |
||||
|
mode int |
||||
|
} |
||||
|
|
||||
|
const ( |
||||
|
_QModeIn = 1 |
||||
|
_QModeOut = 2 |
||||
|
) |
||||
|
|
||||
|
func newLinkedMap(qmode int) *linkedMap { |
||||
|
return &linkedMap{ |
||||
|
qmap: make(map[uint32]*qNode), |
||||
|
mode: qmode, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) get(seq uint32) (i *qNode) { |
||||
|
i = l.qmap[seq] |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) contains(seq uint32) bool { |
||||
|
_, y := l.qmap[seq] |
||||
|
return y |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) size() int32 { |
||||
|
return int32(len(l.qmap)) |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) reset() { |
||||
|
l.head = nil |
||||
|
l.tail = nil |
||||
|
l.lastIns = nil |
||||
|
l.maxCtnSeq = 0 |
||||
|
l.qmap = make(map[uint32]*qNode) |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) isEqualsHead(seq uint32) bool { |
||||
|
return l.head != nil && seq == l.head.seq |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) distanceOfHead(seq uint32) int32 { |
||||
|
if l.head != nil { |
||||
|
return int32(seq - l.head.seq) |
||||
|
} else { |
||||
|
return -1 |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) appendTail(one *qNode) { |
||||
|
if l.tail != nil { |
||||
|
l.tail.next = one |
||||
|
one.prev = l.tail |
||||
|
l.tail = one |
||||
|
} else { |
||||
|
l.head = one |
||||
|
l.tail = one |
||||
|
} |
||||
|
l.qmap[one.seq] = one |
||||
|
} |
||||
|
|
||||
|
// xxx - n - yyy
|
||||
|
// xxx - yyy
|
||||
|
func (l *linkedMap) deleteAt(n *qNode) { |
||||
|
x, y := n.prev, n.next |
||||
|
if x != nil { |
||||
|
x.next = y |
||||
|
} else { |
||||
|
l.head = y |
||||
|
} |
||||
|
if y != nil { |
||||
|
y.prev = x |
||||
|
} else { |
||||
|
l.tail = x |
||||
|
} |
||||
|
n.prev, n.next = nil, nil |
||||
|
delete(l.qmap, n.seq) |
||||
|
} |
||||
|
|
||||
|
// delete with n <- ...n |
|
||||
|
func (l *linkedMap) deleteBefore(n *qNode) (left *qNode, deleted int32) { |
||||
|
for i := n; i != nil; i = i.prev { |
||||
|
delete(l.qmap, i.seq) |
||||
|
if i.scnt != _SENT_OK { |
||||
|
deleted++ |
||||
|
// only outQ could delete at here
|
||||
|
if l.mode == _QModeOut { |
||||
|
bpool.Put(i.buffer) |
||||
|
i.buffer = nil |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
left = l.head |
||||
|
l.head = n.next |
||||
|
n.next = nil |
||||
|
if l.head != nil { |
||||
|
l.head.prev = nil |
||||
|
} else { // n.next is the tail and is nil
|
||||
|
l.tail = nil |
||||
|
} |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// xxx - ref
|
||||
|
// xxx - one - ref
|
||||
|
func (l *linkedMap) insertBefore(ref, one *qNode) { |
||||
|
x := ref.prev |
||||
|
if x != nil { |
||||
|
x.next = one |
||||
|
one.prev = x |
||||
|
} else { |
||||
|
l.head = one |
||||
|
} |
||||
|
ref.prev = one |
||||
|
one.next = ref |
||||
|
l.qmap[one.seq] = one |
||||
|
} |
||||
|
|
||||
|
// ref - zzz
|
||||
|
// ref - one - zzz
|
||||
|
func (l *linkedMap) insertAfter(ref, one *qNode) { |
||||
|
z := ref.next |
||||
|
if z == nil { // append
|
||||
|
ref.next = one |
||||
|
l.tail = one |
||||
|
} else { // insert mid
|
||||
|
z.prev = one |
||||
|
ref.next = one |
||||
|
} |
||||
|
one.prev = ref |
||||
|
one.next = z |
||||
|
l.qmap[one.seq] = one |
||||
|
} |
||||
|
|
||||
|
// baseHead: the left outside boundary
|
||||
|
// if inserted, return the distance between newNode with baseHead
|
||||
|
func (l *linkedMap) searchInsert(one *qNode, baseHead uint32) (dis int64) { |
||||
|
for i := l.tail; i != nil; i = i.prev { |
||||
|
dis = int64(one.seq) - int64(i.seq) |
||||
|
if dis > 0 { |
||||
|
l.insertAfter(i, one) |
||||
|
return |
||||
|
} else if dis == 0 { |
||||
|
// duplicated
|
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
if one.seq <= baseHead { |
||||
|
return 0 |
||||
|
} |
||||
|
if l.head != nil { |
||||
|
l.insertBefore(l.head, one) |
||||
|
} else { |
||||
|
l.head = one |
||||
|
l.tail = one |
||||
|
l.qmap[one.seq] = one |
||||
|
} |
||||
|
dis = int64(one.seq) - int64(baseHead) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) updateContinuous(i *qNode) bool { |
||||
|
var lastCtnSeq = l.maxCtnSeq |
||||
|
for ; i != nil; i = i.next { |
||||
|
if i.seq-lastCtnSeq == 1 { |
||||
|
lastCtnSeq = i.seq |
||||
|
} else { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
if lastCtnSeq != l.maxCtnSeq { |
||||
|
l.maxCtnSeq = lastCtnSeq |
||||
|
return true |
||||
|
} |
||||
|
return false |
||||
|
} |
||||
|
|
||||
|
func (l *linkedMap) isWholeContinuous() bool { |
||||
|
return l.tail != nil && l.maxCtnSeq == l.tail.seq |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
func (l *linkedMap) searchMaxContinued(baseHead uint32) (*qNode, bool) { |
||||
|
var last *qNode |
||||
|
for i := l.head; i != nil; i = i.next { |
||||
|
if last != nil { |
||||
|
if i.seq-last.seq > 1 { |
||||
|
return last, true |
||||
|
} |
||||
|
} else { |
||||
|
if i.seq != baseHead { |
||||
|
return nil, false |
||||
|
} |
||||
|
} |
||||
|
last = i |
||||
|
} |
||||
|
if last != nil { |
||||
|
return last, true |
||||
|
} else { |
||||
|
return nil, false |
||||
|
} |
||||
|
} |
||||
|
*/ |
||||
|
|
||||
|
func (q *qNode) forward(n int) *qNode { |
||||
|
for ; n > 0 && q != nil; n-- { |
||||
|
q = q.next |
||||
|
} |
||||
|
return q |
||||
|
} |
||||
|
|
||||
|
// prev of bitmap start point
|
||||
|
func (l *linkedMap) makeHolesBitmap(prev uint32) ([]uint64, uint32) { |
||||
|
var start *qNode |
||||
|
var bits uint64 |
||||
|
var j uint32 |
||||
|
for i := l.head; i != nil; i = i.next { |
||||
|
if i.seq >= prev+1 { |
||||
|
start = i |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
var bitmap []uint64 |
||||
|
// search start which is the recent successor of [prev]
|
||||
|
Iterator: |
||||
|
for i := start; i != nil; i = i.next { |
||||
|
dis := i.seq - prev |
||||
|
prev = i.seq |
||||
|
j += dis // j is next bit index
|
||||
|
for j >= 65 { |
||||
|
if len(bitmap) >= 20 { // bitmap too long
|
||||
|
break Iterator |
||||
|
} |
||||
|
bitmap = append(bitmap, bits) |
||||
|
bits = 0 |
||||
|
j -= 64 |
||||
|
} |
||||
|
bits |= 1 << (j - 1) |
||||
|
} |
||||
|
if j > 0 { |
||||
|
// j -> (0, 64]
|
||||
|
bitmap = append(bitmap, bits) |
||||
|
} |
||||
|
return bitmap, j |
||||
|
} |
||||
|
|
||||
|
// from= the bitmap start point
|
||||
|
func (l *linkedMap) deleteByBitmap(bmap []uint64, from uint32, tailBitsLen uint32) (deleted, missed int32, lastContinued bool) { |
||||
|
var start = l.qmap[from] |
||||
|
if start != nil { |
||||
|
// delete predecessors
|
||||
|
if pred := start.prev; pred != nil { |
||||
|
_, n := l.deleteBefore(pred) |
||||
|
deleted += n |
||||
|
} |
||||
|
} else { |
||||
|
// [from] is out of bounds
|
||||
|
return |
||||
|
} |
||||
|
var j, bitsLen uint32 |
||||
|
var bits uint64 |
||||
|
bits, bmap = bmap[0], bmap[1:] |
||||
|
if len(bmap) > 0 { |
||||
|
bitsLen = 64 |
||||
|
} else { |
||||
|
// bmap.len==1, tail is here
|
||||
|
bitsLen = tailBitsLen |
||||
|
} |
||||
|
// maxContinued will save the max continued node (from [start]) which could be deleted safely.
|
||||
|
// keep the queue smallest
|
||||
|
var maxContinued *qNode |
||||
|
lastContinued = true |
||||
|
|
||||
|
for i := start; i != nil; j++ { |
||||
|
if j >= bitsLen { |
||||
|
if len(bmap) > 0 { |
||||
|
j = 0 |
||||
|
bits, bmap = bmap[0], bmap[1:] |
||||
|
if len(bmap) > 0 { |
||||
|
bitsLen = 64 |
||||
|
} else { |
||||
|
bitsLen = tailBitsLen |
||||
|
} |
||||
|
} else { |
||||
|
// no more pages
|
||||
|
goto finished |
||||
|
} |
||||
|
} |
||||
|
if bits&1 == 1 { |
||||
|
if lastContinued { |
||||
|
maxContinued = i |
||||
|
} |
||||
|
if i.scnt != _SENT_OK { |
||||
|
// no mark means first deleting
|
||||
|
deleted++ |
||||
|
} |
||||
|
// don't delete, just mark it
|
||||
|
i.scnt = _SENT_OK |
||||
|
} else { |
||||
|
// known it may be lost
|
||||
|
if i.miss == 0 { |
||||
|
missed++ |
||||
|
} |
||||
|
i.miss++ |
||||
|
lastContinued = false |
||||
|
} |
||||
|
bits >>= 1 |
||||
|
i = i.next |
||||
|
} |
||||
|
|
||||
|
finished: |
||||
|
if maxContinued != nil { |
||||
|
l.deleteBefore(maxContinued) |
||||
|
} |
||||
|
return |
||||
|
} |
@ -0,0 +1,236 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"math/rand" |
||||
|
"testing" |
||||
|
) |
||||
|
|
||||
|
var lmap *linkedMap |
||||
|
|
||||
|
func init() { |
||||
|
lmap = newLinkedMap(_QModeIn) |
||||
|
} |
||||
|
|
||||
|
func node(seq int) *qNode { |
||||
|
return &qNode{packet: &packet{seq: uint32(seq)}} |
||||
|
} |
||||
|
|
||||
|
func Test_get(t *testing.T) { |
||||
|
var i, n *qNode |
||||
|
assert(!lmap.contains(0), t, "0 nil") |
||||
|
n = node(0) |
||||
|
lmap.head = n |
||||
|
lmap.tail = n |
||||
|
lmap.qmap[0] = n |
||||
|
i = lmap.get(0) |
||||
|
assert(i == n, t, "0=n") |
||||
|
} |
||||
|
|
||||
|
func Test_insert(t *testing.T) { |
||||
|
lmap.reset() |
||||
|
n := node(1) |
||||
|
// appendTail
|
||||
|
lmap.appendTail(n) |
||||
|
assert(lmap.head == n, t, "head n") |
||||
|
assert(lmap.tail == n, t, "head n") |
||||
|
n = node(2) |
||||
|
lmap.appendTail(n) |
||||
|
assert(lmap.head != n, t, "head n") |
||||
|
assert(lmap.tail == n, t, "head n") |
||||
|
assert(lmap.size() == 2, t, "size") |
||||
|
} |
||||
|
|
||||
|
func Test_insertAfter(t *testing.T) { |
||||
|
n1 := lmap.get(1) |
||||
|
n2 := n1.next |
||||
|
n3 := node(3) |
||||
|
lmap.insertAfter(n1, n3) |
||||
|
assert(n1.next == n3, t, "n3") |
||||
|
assert(n1 == n3.prev, t, "left n3") |
||||
|
assert(n2 == n3.next, t, "n3 right") |
||||
|
} |
||||
|
|
||||
|
func Test_insertBefore(t *testing.T) { |
||||
|
n3 := lmap.get(3) |
||||
|
n2 := n3.next |
||||
|
n4 := node(4) |
||||
|
lmap.insertAfter(n3, n4) |
||||
|
assert(n3.next == n4, t, "n4") |
||||
|
assert(n3 == n4.prev, t, "left n4") |
||||
|
assert(n2 == n4.next, t, "n4 right") |
||||
|
} |
||||
|
|
||||
|
func Test_deleteBefore(t *testing.T) { |
||||
|
lmap.reset() |
||||
|
for i := 1; i < 10; i++ { |
||||
|
n := node(i) |
||||
|
lmap.appendTail(n) |
||||
|
} |
||||
|
|
||||
|
var assertRangeEquals = func(n *qNode, start, wantCount int) { |
||||
|
var last *qNode |
||||
|
var count int |
||||
|
for ; n != nil; n = n.next { |
||||
|
assert(int(n.seq) == start, t, "nseq=%d start=%d", n.seq, start) |
||||
|
last = n |
||||
|
start++ |
||||
|
count++ |
||||
|
} |
||||
|
assert(last.next == nil, t, "tail nil") |
||||
|
assert(count == wantCount, t, "count") |
||||
|
} |
||||
|
assertRangeEquals(lmap.head, 1, 9) |
||||
|
var n *qNode |
||||
|
n = lmap.get(3) |
||||
|
n, _ = lmap.deleteBefore(n) |
||||
|
assertRangeEquals(n, 1, 3) |
||||
|
assert(lmap.head.seq == 4, t, "head") |
||||
|
|
||||
|
n = lmap.get(8) |
||||
|
n, _ = lmap.deleteBefore(n) |
||||
|
assertRangeEquals(n, 4, 5) |
||||
|
assert(lmap.head.seq == 9, t, "head") |
||||
|
|
||||
|
n = lmap.get(9) |
||||
|
n, _ = lmap.deleteBefore(n) |
||||
|
assertRangeEquals(n, 9, 1) |
||||
|
|
||||
|
assert(lmap.size() == 0, t, "size 0") |
||||
|
assert(lmap.head == nil, t, "head nil") |
||||
|
assert(lmap.tail == nil, t, "tail nil") |
||||
|
} |
||||
|
|
||||
|
func testBitmap(t *testing.T, bmap []uint64, prev uint32) { |
||||
|
var j uint |
||||
|
var k int |
||||
|
bits := bmap[k] |
||||
|
t.Logf("test-%d %016x", k, bits) |
||||
|
var checkNextPage = func() { |
||||
|
if j >= 64 { |
||||
|
j = 0 |
||||
|
k++ |
||||
|
bits = bmap[k] |
||||
|
t.Logf("test-%d %016x", k, bits) |
||||
|
} |
||||
|
} |
||||
|
for i := lmap.head; i != nil && k < len(bmap); i = i.next { |
||||
|
checkNextPage() |
||||
|
dis := i.seq - prev |
||||
|
prev = i.seq |
||||
|
if dis == 1 { |
||||
|
bit := (bits >> j) & 1 |
||||
|
assert(bit == 1, t, "1 bit=%d j=%d", bit, j) |
||||
|
j++ |
||||
|
} else { |
||||
|
for ; dis > 0; dis-- { |
||||
|
checkNextPage() |
||||
|
bit := (bits >> j) & 1 |
||||
|
want := uint64(0) |
||||
|
if dis == 1 { |
||||
|
want = 1 |
||||
|
} |
||||
|
assert(bit == want, t, "?=%d bit=%d j=%d", want, bit, j) |
||||
|
j++ |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
// remains bits should be 0
|
||||
|
for i := j & 63; i > 0; i-- { |
||||
|
bit := (bits >> j) & 1 |
||||
|
assert(bit == 0, t, "00 bit=%d j=%d", bit, j) |
||||
|
j++ |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func Test_bitmap(t *testing.T) { |
||||
|
var prev uint32 |
||||
|
var head uint32 = prev + 1 |
||||
|
|
||||
|
lmap.reset() |
||||
|
// test 66-%3 and record holes
|
||||
|
var holes = make([]uint32, 0, 50) |
||||
|
for i := head; i < 366; i++ { |
||||
|
if i%3 == 0 { |
||||
|
holes = append(holes, i) |
||||
|
continue |
||||
|
} |
||||
|
n := node(int(i)) |
||||
|
lmap.appendTail(n) |
||||
|
} |
||||
|
bmap, tbl := lmap.makeHolesBitmap(prev) |
||||
|
testBitmap(t, bmap, prev) |
||||
|
|
||||
|
lmap.reset() |
||||
|
// full 66, do deleteByBitmap then compare
|
||||
|
for i := head; i < 366; i++ { |
||||
|
n := node(int(i)) |
||||
|
lmap.appendTail(n) |
||||
|
} |
||||
|
|
||||
|
lmap.deleteByBitmap(bmap, head, tbl) |
||||
|
var holesResult = make([]uint32, 0, 50) |
||||
|
for i := lmap.head; i != nil; i = i.next { |
||||
|
if i.scnt != _SENT_OK { |
||||
|
holesResult = append(holesResult, i.seq) |
||||
|
} |
||||
|
} |
||||
|
a := fmt.Sprintf("%x", holes) |
||||
|
b := fmt.Sprintf("%x", holesResult) |
||||
|
assert(a == b, t, "deleteByBitmap \na=%s \nb=%s", a, b) |
||||
|
|
||||
|
lmap.reset() |
||||
|
// test stride across page 1
|
||||
|
for i := head; i < 69; i++ { |
||||
|
if i >= 63 && i <= 65 { |
||||
|
continue |
||||
|
} |
||||
|
n := node(int(i)) |
||||
|
lmap.appendTail(n) |
||||
|
} |
||||
|
bmap, _ = lmap.makeHolesBitmap(prev) |
||||
|
testBitmap(t, bmap, prev) |
||||
|
|
||||
|
lmap.reset() |
||||
|
prev = 65 |
||||
|
head = prev + 1 |
||||
|
// test stride across page 0
|
||||
|
for i := head; i < 68; i++ { |
||||
|
n := node(int(i)) |
||||
|
lmap.appendTail(n) |
||||
|
} |
||||
|
bmap, _ = lmap.makeHolesBitmap(prev) |
||||
|
testBitmap(t, bmap, prev) |
||||
|
} |
||||
|
|
||||
|
var ackbitmap []uint64 |
||||
|
|
||||
|
func init_benchmark_map() { |
||||
|
if lmap.size() != 640 { |
||||
|
lmap.reset() |
||||
|
for i := 1; i <= 640; i++ { |
||||
|
lmap.appendTail(node(i)) |
||||
|
} |
||||
|
ackbitmap = make([]uint64, 10) |
||||
|
for i := 0; i < len(ackbitmap); i++ { |
||||
|
n := rand.Int63() |
||||
|
ackbitmap[i] = uint64(n) << 1 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func Benchmark_make_bitmap(b *testing.B) { |
||||
|
init_benchmark_map() |
||||
|
|
||||
|
for i := 0; i < b.N; i++ { |
||||
|
lmap.makeHolesBitmap(0) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func Benchmark_apply_bitmap(b *testing.B) { |
||||
|
init_benchmark_map() |
||||
|
|
||||
|
for i := 0; i < b.N; i++ { |
||||
|
lmap.deleteByBitmap(ackbitmap, 1, 64) |
||||
|
} |
||||
|
} |
@ -0,0 +1,142 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"encoding/binary" |
||||
|
"fmt" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_F_NIL = 0 |
||||
|
_F_SYN = 1 |
||||
|
_F_ACK = 1 << 1 |
||||
|
_F_SACK = 1 << 2 |
||||
|
_F_TIME = 1 << 3 |
||||
|
_F_DATA = 1 << 4 |
||||
|
// reserved = 1 << 5
|
||||
|
_F_RESET = 1 << 6 |
||||
|
_F_FIN = 1 << 7 |
||||
|
) |
||||
|
|
||||
|
var packetTypeNames = map[byte]string{ |
||||
|
0: "NOOP", |
||||
|
1: "SYN", |
||||
|
2: "ACK", |
||||
|
3: "SYN+ACK", |
||||
|
4: "SACK", |
||||
|
8: "TIME", |
||||
|
12: "SACK+TIME", |
||||
|
16: "DATA", |
||||
|
64: "RESET", |
||||
|
128: "FIN", |
||||
|
192: "FIN+RESET", |
||||
|
} |
||||
|
|
||||
|
const ( |
||||
|
_S_FIN = iota |
||||
|
_S_FIN0 |
||||
|
_S_FIN1 |
||||
|
_S_SYN0 |
||||
|
_S_SYN1 |
||||
|
_S_EST0 |
||||
|
_S_EST1 |
||||
|
) |
||||
|
|
||||
|
// Magic-6 | TH-10 | CH-10 | payload
|
||||
|
const ( |
||||
|
_MAGIC_SIZE = 6 |
||||
|
_TH_SIZE = 10 + _MAGIC_SIZE |
||||
|
_CH_SIZE = 10 |
||||
|
_AH_SIZE = _TH_SIZE + _CH_SIZE |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
// Max UDP payload: 1500 MTU - 20 IP hdr - 8 UDP hdr = 1472 bytes
|
||||
|
// Then: MSS = 1472-26 = 1446
|
||||
|
// And For ADSL: 1446-8 = 1438
|
||||
|
_MSS = 1438 |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_SENT_OK = 0xff |
||||
|
) |
||||
|
|
||||
|
type packet struct { |
||||
|
seq uint32 |
||||
|
ack uint32 |
||||
|
flag uint8 |
||||
|
scnt uint8 |
||||
|
payload []byte |
||||
|
buffer []byte |
||||
|
} |
||||
|
|
||||
|
func (p *packet) marshall(id connID) []byte { |
||||
|
buf := p.buffer |
||||
|
if buf == nil { |
||||
|
buf = make([]byte, _AH_SIZE+len(p.payload)) |
||||
|
copy(buf[_TH_SIZE+10:], p.payload) |
||||
|
} |
||||
|
binary.BigEndian.PutUint16(buf[_MAGIC_SIZE:], uint16(len(buf))) |
||||
|
binary.BigEndian.PutUint32(buf[_MAGIC_SIZE+2:], id.rid) |
||||
|
binary.BigEndian.PutUint32(buf[_MAGIC_SIZE+6:], id.lid) |
||||
|
binary.BigEndian.PutUint32(buf[_TH_SIZE:], p.seq) |
||||
|
binary.BigEndian.PutUint32(buf[_TH_SIZE+4:], p.ack) |
||||
|
buf[_TH_SIZE+8] = p.flag |
||||
|
buf[_TH_SIZE+9] = p.scnt |
||||
|
return buf |
||||
|
} |
||||
|
|
||||
|
func unmarshall(pk *packet, buf []byte) { |
||||
|
if len(buf) >= _CH_SIZE { |
||||
|
pk.seq = binary.BigEndian.Uint32(buf) |
||||
|
pk.ack = binary.BigEndian.Uint32(buf[4:]) |
||||
|
pk.flag = buf[8] |
||||
|
pk.scnt = buf[9] |
||||
|
pk.payload = buf[10:] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (n *qNode) String() string { |
||||
|
now := Now() |
||||
|
return fmt.Sprintf("type=%s seq=%d scnt=%d sndtime~=%d,%d miss=%d", |
||||
|
packetTypeNames[n.flag], n.seq, n.scnt, n.sent-now, n.sent_1-now, n.miss) |
||||
|
} |
||||
|
|
||||
|
func maxI64(a, b int64) int64 { |
||||
|
if a >= b { |
||||
|
return a |
||||
|
} else { |
||||
|
return b |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func maxU32(a, b uint32) uint32 { |
||||
|
if a >= b { |
||||
|
return a |
||||
|
} else { |
||||
|
return b |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func minI64(a, b int64) int64 { |
||||
|
if a <= b { |
||||
|
return a |
||||
|
} else { |
||||
|
return b |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func maxI32(a, b int32) int32 { |
||||
|
if a >= b { |
||||
|
return a |
||||
|
} else { |
||||
|
return b |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func minI32(a, b int32) int32 { |
||||
|
if a <= b { |
||||
|
return a |
||||
|
} else { |
||||
|
return b |
||||
|
} |
||||
|
} |
@ -0,0 +1,516 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"errors" |
||||
|
"log" |
||||
|
"net" |
||||
|
"sync" |
||||
|
"sync/atomic" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_10ms = time.Millisecond * 10 |
||||
|
_100ms = time.Millisecond * 100 |
||||
|
) |
||||
|
|
||||
|
const ( |
||||
|
_FIN_ACK_SEQ uint32 = 0xffFF0000 |
||||
|
_INVALID_SEQ uint32 = 0xffFFffFF |
||||
|
) |
||||
|
|
||||
|
var ( |
||||
|
ErrIOTimeout error = &TimeoutError{} |
||||
|
ErrUnknown = errors.New("Unknown error") |
||||
|
ErrInexplicableData = errors.New("Inexplicable data") |
||||
|
ErrTooManyAttempts = errors.New("Too many attempts to connect") |
||||
|
) |
||||
|
|
||||
|
type TimeoutError struct{} |
||||
|
|
||||
|
func (e *TimeoutError) Error() string { return "i/o timeout" } |
||||
|
func (e *TimeoutError) Timeout() bool { return true } |
||||
|
func (e *TimeoutError) Temporary() bool { return true } |
||||
|
|
||||
|
type Conn struct { |
||||
|
sock *net.UDPConn |
||||
|
dest *net.UDPAddr |
||||
|
edp *Endpoint |
||||
|
connID connID // 8 bytes
|
||||
|
// events
|
||||
|
evRecv chan []byte |
||||
|
evRead chan byte |
||||
|
evSend chan byte |
||||
|
evSWnd chan byte |
||||
|
evAck chan byte |
||||
|
evClose chan byte |
||||
|
// protocol state
|
||||
|
inlock sync.Mutex |
||||
|
outlock sync.Mutex |
||||
|
state int32 |
||||
|
mySeq uint32 |
||||
|
swnd int32 |
||||
|
cwnd int32 |
||||
|
missed int32 |
||||
|
outPending int32 |
||||
|
lastAck uint32 |
||||
|
lastAckTime int64 |
||||
|
lastAckTime2 int64 |
||||
|
lastShrink int64 |
||||
|
lastRstMis int64 |
||||
|
ato int64 |
||||
|
rto int64 |
||||
|
rtt int64 |
||||
|
srtt int64 |
||||
|
mdev int64 |
||||
|
rtmo int64 |
||||
|
wtmo int64 |
||||
|
tSlot int64 |
||||
|
tSlotT0 int64 |
||||
|
lastSErr int64 |
||||
|
// queue
|
||||
|
outQ *linkedMap |
||||
|
inQ *linkedMap |
||||
|
inQReady []byte |
||||
|
inQDirty bool |
||||
|
lastReadSeq uint32 // last user read seq
|
||||
|
// params
|
||||
|
bandwidth int64 |
||||
|
fastRetransmit bool |
||||
|
flatTraffic bool |
||||
|
mss int |
||||
|
// statistics
|
||||
|
urgent int |
||||
|
inPkCnt int |
||||
|
inDupCnt int |
||||
|
outPkCnt int |
||||
|
outDupCnt int |
||||
|
fRCnt int |
||||
|
} |
||||
|
|
||||
|
func NewConn(e *Endpoint, dest *net.UDPAddr, id connID) *Conn { |
||||
|
c := &Conn{ |
||||
|
sock: e.udpconn, |
||||
|
dest: dest, |
||||
|
edp: e, |
||||
|
connID: id, |
||||
|
evRecv: make(chan []byte, 128), |
||||
|
evRead: make(chan byte, 1), |
||||
|
evSWnd: make(chan byte, 2), |
||||
|
evSend: make(chan byte, 4), |
||||
|
evAck: make(chan byte, 1), |
||||
|
evClose: make(chan byte, 2), |
||||
|
outQ: newLinkedMap(_QModeOut), |
||||
|
inQ: newLinkedMap(_QModeIn), |
||||
|
} |
||||
|
p := e.params |
||||
|
c.bandwidth = p.Bandwidth |
||||
|
c.fastRetransmit = p.FastRetransmit |
||||
|
c.flatTraffic = p.FlatTraffic |
||||
|
c.mss = _MSS |
||||
|
if dest.IP.To4() == nil { |
||||
|
// typical ipv6 header length=40
|
||||
|
c.mss -= 20 |
||||
|
} |
||||
|
return c |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) initConnection(buf []byte) (err error) { |
||||
|
if buf == nil { |
||||
|
err = c.initDialing() |
||||
|
} else { //server
|
||||
|
err = c.acceptConnection(buf[_TH_SIZE:]) |
||||
|
} |
||||
|
if err != nil { |
||||
|
return |
||||
|
} |
||||
|
if c.state == _S_EST1 { |
||||
|
c.lastReadSeq = c.lastAck |
||||
|
c.inQ.maxCtnSeq = c.lastAck |
||||
|
c.rtt = maxI64(c.rtt, _MIN_RTT) |
||||
|
c.mdev = c.rtt << 1 |
||||
|
c.srtt = c.rtt << 3 |
||||
|
c.rto = maxI64(c.rtt*2, _MIN_RTO) |
||||
|
c.ato = maxI64(c.rtt>>4, _MIN_ATO) |
||||
|
c.ato = minI64(c.ato, _MAX_ATO) |
||||
|
// initial cwnd
|
||||
|
c.swnd = calSwnd(c.bandwidth, c.rtt) >> 1 |
||||
|
c.cwnd = 8 |
||||
|
go c.internalRecvLoop() |
||||
|
go c.internalSendLoop() |
||||
|
go c.internalAckLoop() |
||||
|
if debug >= 0 { |
||||
|
go c.internal_state() |
||||
|
} |
||||
|
return nil |
||||
|
} else { |
||||
|
return ErrUnknown |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) initDialing() error { |
||||
|
// first syn
|
||||
|
pk := &packet{ |
||||
|
seq: c.mySeq, |
||||
|
flag: _F_SYN, |
||||
|
} |
||||
|
item := nodeOf(pk) |
||||
|
var buf []byte |
||||
|
c.state = _S_SYN0 |
||||
|
t0 := Now() |
||||
|
for i := 0; i < _MAX_RETRIES && c.state == _S_SYN0; i++ { |
||||
|
// send syn
|
||||
|
c.internalWrite(item) |
||||
|
select { |
||||
|
case buf = <-c.evRecv: |
||||
|
c.rtt = Now() - t0 |
||||
|
c.state = _S_SYN1 |
||||
|
c.connID.setRid(buf) |
||||
|
buf = buf[_TH_SIZE:] |
||||
|
case <-time.After(time.Second): |
||||
|
continue |
||||
|
} |
||||
|
} |
||||
|
if c.state == _S_SYN0 { |
||||
|
return ErrTooManyAttempts |
||||
|
} |
||||
|
|
||||
|
unmarshall(pk, buf) |
||||
|
// expected syn+ack
|
||||
|
if pk.flag == _F_SYN|_F_ACK && pk.ack == c.mySeq { |
||||
|
if scnt := pk.scnt - 1; scnt > 0 { |
||||
|
c.rtt -= int64(scnt) * 1e3 |
||||
|
} |
||||
|
log.Println("rtt", c.rtt) |
||||
|
c.state = _S_EST0 |
||||
|
// build ack3
|
||||
|
pk.scnt = 0 |
||||
|
pk.ack = pk.seq |
||||
|
pk.flag = _F_ACK |
||||
|
item := nodeOf(pk) |
||||
|
// send ack3
|
||||
|
c.internalWrite(item) |
||||
|
// update lastAck
|
||||
|
c.logAck(pk.ack) |
||||
|
c.state = _S_EST1 |
||||
|
return nil |
||||
|
} else { |
||||
|
return ErrInexplicableData |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) acceptConnection(buf []byte) error { |
||||
|
var pk = new(packet) |
||||
|
var item *qNode |
||||
|
unmarshall(pk, buf) |
||||
|
// expected syn
|
||||
|
if pk.flag == _F_SYN { |
||||
|
c.state = _S_SYN1 |
||||
|
// build syn+ack
|
||||
|
pk.ack = pk.seq |
||||
|
pk.seq = c.mySeq |
||||
|
pk.flag |= _F_ACK |
||||
|
// update lastAck
|
||||
|
c.logAck(pk.ack) |
||||
|
item = nodeOf(pk) |
||||
|
item.scnt = pk.scnt - 1 |
||||
|
} else { |
||||
|
dumpb("Syn1 ?", buf) |
||||
|
return ErrInexplicableData |
||||
|
} |
||||
|
for i := 0; i < 5 && c.state == _S_SYN1; i++ { |
||||
|
t0 := Now() |
||||
|
// reply syn+ack
|
||||
|
c.internalWrite(item) |
||||
|
// recv ack3
|
||||
|
select { |
||||
|
case buf = <-c.evRecv: |
||||
|
c.state = _S_EST0 |
||||
|
c.rtt = Now() - t0 |
||||
|
buf = buf[_TH_SIZE:] |
||||
|
log.Println("rtt", c.rtt) |
||||
|
case <-time.After(time.Second): |
||||
|
continue |
||||
|
} |
||||
|
} |
||||
|
if c.state == _S_SYN1 { |
||||
|
return ErrTooManyAttempts |
||||
|
} |
||||
|
|
||||
|
pk = new(packet) |
||||
|
unmarshall(pk, buf) |
||||
|
// expected ack3
|
||||
|
if pk.flag == _F_ACK && pk.ack == c.mySeq { |
||||
|
c.state = _S_EST1 |
||||
|
} else { |
||||
|
// if ack3 lost, resend syn+ack 3-times
|
||||
|
// and drop these coming data
|
||||
|
if pk.flag&_F_DATA != 0 && pk.seq > c.lastAck { |
||||
|
c.internalWrite(item) |
||||
|
c.state = _S_EST1 |
||||
|
} else { |
||||
|
dumpb("Ack3 ?", buf) |
||||
|
return ErrInexplicableData |
||||
|
} |
||||
|
} |
||||
|
return nil |
||||
|
} |
||||
|
|
||||
|
// 20,20,20,20, 100,100,100,100, 1s,1s,1s,1s
|
||||
|
func selfSpinWait(fn func() bool) error { |
||||
|
const _MAX_SPIN = 12 |
||||
|
for i := 0; i < _MAX_SPIN; i++ { |
||||
|
if fn() { |
||||
|
return nil |
||||
|
} else if i <= 3 { |
||||
|
time.Sleep(_10ms * 2) |
||||
|
} else if i <= 7 { |
||||
|
time.Sleep(_100ms) |
||||
|
} else { |
||||
|
time.Sleep(time.Second) |
||||
|
} |
||||
|
} |
||||
|
return ErrIOTimeout |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) IsClosed() bool { |
||||
|
return atomic.LoadInt32(&c.state) <= _S_FIN1 |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
active close: |
||||
|
1 <- send fin-W: closeW() |
||||
|
before sending, ensure all outQ items has beed sent out and all of them has been acked. |
||||
|
2 -> wait to recv ack{fin-W} |
||||
|
then trigger closeR, including send fin-R and wait to recv ack{fin-R} |
||||
|
|
||||
|
passive close: |
||||
|
-> fin: |
||||
|
if outQ is not empty then self-spin wait. |
||||
|
if outQ empty, send ack{fin-W} then goto closeW(). |
||||
|
*/ |
||||
|
func (c *Conn) Close() (err error) { |
||||
|
if !atomic.CompareAndSwapInt32(&c.state, _S_EST1, _S_FIN0) { |
||||
|
return selfSpinWait(func() bool { |
||||
|
return atomic.LoadInt32(&c.state) == _S_FIN |
||||
|
}) |
||||
|
} |
||||
|
var err0 error |
||||
|
err0 = c.closeW() |
||||
|
// waiting for fin-2 of peer
|
||||
|
err = selfSpinWait(func() bool { |
||||
|
select { |
||||
|
case v := <-c.evClose: |
||||
|
if v == _S_FIN { |
||||
|
return true |
||||
|
} else { |
||||
|
time.AfterFunc(_100ms, func() { c.evClose <- v }) |
||||
|
} |
||||
|
default: |
||||
|
} |
||||
|
return false |
||||
|
}) |
||||
|
defer c.afterShutdown() |
||||
|
if err != nil { |
||||
|
// backup path for wait ack(finW) timeout
|
||||
|
c.closeR(nil) |
||||
|
} |
||||
|
if err0 != nil { |
||||
|
return err0 |
||||
|
} else { |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) beforeCloseW() (err error) { |
||||
|
// check outQ was empty and all has been acked.
|
||||
|
// self-spin waiting
|
||||
|
for i := 0; i < 2; i++ { |
||||
|
err = selfSpinWait(func() bool { |
||||
|
return atomic.LoadInt32(&c.outPending) <= 0 |
||||
|
}) |
||||
|
if err == nil { |
||||
|
break |
||||
|
} |
||||
|
} |
||||
|
// send fin, reliably
|
||||
|
c.outlock.Lock() |
||||
|
c.mySeq++ |
||||
|
c.outPending++ |
||||
|
pk := &packet{seq: c.mySeq, flag: _F_FIN} |
||||
|
item := nodeOf(pk) |
||||
|
c.outQ.appendTail(item) |
||||
|
c.internalWrite(item) |
||||
|
c.outlock.Unlock() |
||||
|
c.evSWnd <- _VSWND_ACTIVE |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) closeW() (err error) { |
||||
|
// close resource of sending
|
||||
|
defer c.afterCloseW() |
||||
|
// send fin
|
||||
|
err = c.beforeCloseW() |
||||
|
var closed bool |
||||
|
var max = 20 |
||||
|
if c.rtt > 200 { |
||||
|
max = int(c.rtt) / 10 |
||||
|
} |
||||
|
// waiting for outQ means:
|
||||
|
// 1. all outQ has been acked, for passive
|
||||
|
// 2. fin has been acked, for active
|
||||
|
for i := 0; i < max && (atomic.LoadInt32(&c.outPending) > 0 || !closed); i++ { |
||||
|
select { |
||||
|
case v := <-c.evClose: |
||||
|
if v == _S_FIN0 { |
||||
|
// namely, last fin has been acked.
|
||||
|
closed = true |
||||
|
} else { |
||||
|
time.AfterFunc(_100ms, func() { c.evClose <- v }) |
||||
|
} |
||||
|
case <-time.After(_100ms): |
||||
|
} |
||||
|
} |
||||
|
if closed || err != nil { |
||||
|
return |
||||
|
} else { |
||||
|
return ErrIOTimeout |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) afterCloseW() { |
||||
|
// can't close(c.evRecv), avoid endpoint dispatch exception
|
||||
|
// stop pending inputAndSend
|
||||
|
select { |
||||
|
case c.evSend <- _CLOSE: |
||||
|
default: |
||||
|
} |
||||
|
// stop internalSendLoop
|
||||
|
c.evSWnd <- _CLOSE |
||||
|
} |
||||
|
|
||||
|
// called by active and passive close()
|
||||
|
func (c *Conn) afterShutdown() { |
||||
|
// stop internalRecvLoop
|
||||
|
c.evRecv <- nil |
||||
|
// remove registry
|
||||
|
c.edp.removeConn(c.connID, c.dest) |
||||
|
log.Println("shutdown", c.state) |
||||
|
} |
||||
|
|
||||
|
// trigger by reset
|
||||
|
func (c *Conn) forceShutdownWithLock() { |
||||
|
c.outlock.Lock() |
||||
|
defer c.outlock.Unlock() |
||||
|
c.forceShutdown() |
||||
|
} |
||||
|
|
||||
|
// called by:
|
||||
|
// 1/ send exception
|
||||
|
// 2/ recv reset
|
||||
|
// drop outQ and force shutdown
|
||||
|
func (c *Conn) forceShutdown() { |
||||
|
if atomic.CompareAndSwapInt32(&c.state, _S_EST1, _S_FIN) { |
||||
|
defer c.afterShutdown() |
||||
|
// stop sender
|
||||
|
for i := 0; i < cap(c.evSend); i++ { |
||||
|
select { |
||||
|
case <-c.evSend: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
select { |
||||
|
case c.evSend <- _CLOSE: |
||||
|
default: |
||||
|
} |
||||
|
c.outQ.reset() |
||||
|
// stop reader
|
||||
|
close(c.evRead) |
||||
|
c.inQ.reset() |
||||
|
// stop internalLoops
|
||||
|
c.evSWnd <- _CLOSE |
||||
|
c.evAck <- _CLOSE |
||||
|
//log.Println("force shutdown")
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// for sending fin failed
|
||||
|
func (c *Conn) fakeShutdown() { |
||||
|
select { |
||||
|
case c.evClose <- _S_FIN0: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) closeR(pk *packet) { |
||||
|
var passive = true |
||||
|
for { |
||||
|
state := atomic.LoadInt32(&c.state) |
||||
|
switch state { |
||||
|
case _S_FIN: |
||||
|
return |
||||
|
case _S_FIN1: // multiple FIN, maybe lost
|
||||
|
c.passiveCloseReply(pk, false) |
||||
|
return |
||||
|
case _S_FIN0: // active close preformed
|
||||
|
passive = false |
||||
|
} |
||||
|
if !atomic.CompareAndSwapInt32(&c.state, state, _S_FIN1) { |
||||
|
continue |
||||
|
} |
||||
|
c.passiveCloseReply(pk, true) |
||||
|
break |
||||
|
} |
||||
|
// here, R is closed.
|
||||
|
// ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
if passive { |
||||
|
// passive closing call closeW contains sending fin and recv ack
|
||||
|
// may the ack of fin-2 was lost, then the closeW will timeout
|
||||
|
c.closeW() |
||||
|
} |
||||
|
// here, R,W both were closed.
|
||||
|
// ^^^^^^^^^^^^^^^^^^^^^
|
||||
|
atomic.StoreInt32(&c.state, _S_FIN) |
||||
|
// stop internalAckLoop
|
||||
|
c.evAck <- _CLOSE |
||||
|
|
||||
|
if passive { |
||||
|
// close evRecv within here
|
||||
|
c.afterShutdown() |
||||
|
} else { |
||||
|
// notify active close thread
|
||||
|
select { |
||||
|
case c.evClose <- _S_FIN: |
||||
|
default: |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (c *Conn) passiveCloseReply(pk *packet, first bool) { |
||||
|
if pk != nil && pk.flag&_F_FIN != 0 { |
||||
|
if first { |
||||
|
c.checkInQ(pk) |
||||
|
close(c.evRead) |
||||
|
} |
||||
|
// ack the FIN
|
||||
|
pk = &packet{seq: _FIN_ACK_SEQ, ack: pk.seq, flag: _F_ACK} |
||||
|
item := nodeOf(pk) |
||||
|
c.internalWrite(item) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// check inQ ends orderly, and copy queue data to user space
|
||||
|
func (c *Conn) checkInQ(pk *packet) { |
||||
|
if nil != selfSpinWait(func() bool { |
||||
|
return c.inQ.maxCtnSeq+1 == pk.seq |
||||
|
}) { // timeout for waiting inQ to finish
|
||||
|
return |
||||
|
} |
||||
|
c.inlock.Lock() |
||||
|
defer c.inlock.Unlock() |
||||
|
if c.inQ.size() > 0 { |
||||
|
for i := c.inQ.head; i != nil; i = i.next { |
||||
|
c.inQReady = append(c.inQReady, i.payload...) |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"os" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
type watch struct { |
||||
|
label string |
||||
|
t1 time.Time |
||||
|
} |
||||
|
|
||||
|
func StartWatch(s string) *watch { |
||||
|
return &watch{ |
||||
|
label: s, |
||||
|
t1: time.Now(), |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
func (w *watch) StopLoops(loop int, size int) { |
||||
|
tu := time.Now().Sub(w.t1).Nanoseconds() |
||||
|
timePerLoop := float64(tu) / float64(loop) |
||||
|
throughput := float64(loop*size) * 1e6 / float64(tu) |
||||
|
tu_ms := float64(tu) / 1e6 |
||||
|
fmt.Fprintf(os.Stderr, "%s tu=%.2f ms tpl=%.0f ns throughput=%.2f K/s\n", w.label, tu_ms, timePerLoop, throughput) |
||||
|
} |
||||
|
|
||||
|
var _kt = float64(1e9 / 1024) |
||||
|
|
||||
|
func (w *watch) Stop(size int) { |
||||
|
tu := time.Now().Sub(w.t1).Nanoseconds() |
||||
|
throughput := float64(size) * _kt / float64(tu) |
||||
|
tu_ms := float64(tu) / 1e6 |
||||
|
fmt.Fprintf(os.Stderr, "%s tu=%.2f ms throughput=%.2f K/s\n", w.label, tu_ms, throughput) |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import "time" |
||||
|
|
||||
|
const Millisecond = 1e6 |
||||
|
|
||||
|
func Now() int64 { |
||||
|
return time.Now().UnixNano()/Millisecond |
||||
|
} |
||||
|
|
||||
|
func NowNS() int64 { |
||||
|
return time.Now().UnixNano() |
||||
|
} |
||||
|
|
||||
|
func NewTimerChan(d int64) <-chan time.Time { |
||||
|
ticker := time.NewTimer(time.Duration(d) * time.Millisecond) |
||||
|
return ticker.C |
||||
|
} |
@ -0,0 +1,32 @@ |
|||||
|
package udptransfer |
||||
|
|
||||
|
import ( |
||||
|
"math" |
||||
|
"testing" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func Test_sleep(t *testing.T) { |
||||
|
const loops = 10 |
||||
|
var intervals = [...]int64{1, 1e3, 1e4, 1e5, 1e6, 1e7} |
||||
|
var ret [len(intervals)][loops]int64 |
||||
|
for j := 0; j < len(intervals); j++ { |
||||
|
v := time.Duration(intervals[j]) |
||||
|
for i := 0; i < loops; i++ { |
||||
|
t0 := NowNS() |
||||
|
time.Sleep(v) |
||||
|
ret[j][i] = NowNS() - t0 |
||||
|
} |
||||
|
} |
||||
|
for j := 0; j < len(intervals); j++ { |
||||
|
var exp, sum, stdev float64 |
||||
|
exp = float64(intervals[j]) |
||||
|
for _, v := range ret[j] { |
||||
|
sum += float64(v) |
||||
|
stdev += math.Pow(float64(v)-exp, 2) |
||||
|
} |
||||
|
stdev /= float64(loops) |
||||
|
stdev = math.Sqrt(stdev) |
||||
|
t.Logf("interval=%s sleeping=%s stdev/k=%.2f", time.Duration(intervals[j]), time.Duration(sum/loops), stdev/1e3) |
||||
|
} |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue