Transfer: Record close reason of underlying library (UDT). This helps for debugging issues. Close #54

This commit is contained in:
Kleissner
2021-11-08 04:40:53 +01:00
parent 2924f0074d
commit f3d5f23cf9
9 changed files with 89 additions and 55 deletions

View File

@@ -5,7 +5,6 @@ import (
"encoding/binary"
"errors"
"fmt"
"io"
"net"
"sync"
"time"
@@ -48,7 +47,7 @@ func (l *listener) Close() (err error) {
close(a)
close(c)
l.m.closer.Close()
l.m.closer.Close(TerminateReasonListenerClosed)
return nil
}
@@ -163,7 +162,7 @@ func (l *listener) readHandshake(m *multiplexer, hsPacket *packet.HandshakePacke
}
// ListenUDT listens for incoming UDT connections using the existing provided packet connection. It creates a UDT server.
func ListenUDT(config *Config, closer io.Closer, incomingData <-chan []byte, outgoingData chan<- []byte, terminationSignal <-chan struct{}) net.Listener {
func ListenUDT(config *Config, closer Closer, incomingData <-chan []byte, outgoingData chan<- []byte, terminationSignal <-chan struct{}) net.Listener {
m := newMultiplexer(closer, config.MaxPacketSize, incomingData, outgoingData, terminationSignal)
l := &listener{

View File

@@ -4,7 +4,6 @@ package udt
import (
"fmt"
"io"
"math/rand"
"github.com/PeernetOfficial/core/udt/packet"
@@ -19,11 +18,11 @@ type multiplexer struct {
incomingData <-chan []byte // source to read packets from
outgoingData chan<- []byte // destination to send packets to
terminationSignal <-chan struct{} // external termination signal to watch
closer io.Closer // external closer to call in case the local socket/listener closes
closer Closer // external closer to call in case the local socket/listener closes
}
// The closer is called when the socket/listener closes. The terminationSignal is an external (upstream) signal to watch for.
func newMultiplexer(closer io.Closer, maxPacketSize uint, incomingData <-chan []byte, outgoingData chan<- []byte, terminationSignal <-chan struct{}) (m *multiplexer) {
func newMultiplexer(closer Closer, maxPacketSize uint, incomingData <-chan []byte, outgoingData chan<- []byte, terminationSignal <-chan struct{}) (m *multiplexer) {
m = &multiplexer{
maxPacketSize: maxPacketSize,
closer: closer,

View File

@@ -12,12 +12,30 @@ implemented:
*/
import (
"io"
"net"
)
// Closer provides a status code indicating why the closing happens.
type Closer interface {
Close(reason int) error // Close is called when the socket is actually closed.
CloseLinger(reason int) error // CloseLinger is called when the socket indicates to be closed soon, after the linger time.
}
// The termination reason is passed on to the close function
const (
TerminateReasonListenerClosed = 1000 // Listener: The listener.Close function was called.
TerminateReasonLingerTimerExpired = 1001 // Socket: The linger timer expired. Use CloseLinger to know the actual closing reason.
TerminateReasonConnectTimeout = 1002 // Socket: The connection timed out when sending the initial handshake.
TerminateReasonRemoteSentShutdown = 1003 // Remote peer sent a shutdown message.
TerminateReasonCannotProcessOutgoing = 1004 // Send: Cannot process outgoing messages.
TerminateReasonInvalidPacketIDAck = 1005 // Send: Invalid packet ID received in ACK message.
TerminateReasonInvalidPacketIDNak = 1006 // Send: Invalid packet ID received in NAK message.
TerminateReasonCorruptPacketNak = 1007 // Send: Invalid NAK packet received.
TerminateReasonExpireTimer = 1008 // Send: EXP timer expired.
)
// DialUDT establishes an outbound UDT connection using the existing provided packet connection. It creates a UDT client.
func DialUDT(config *Config, closer io.Closer, incomingData <-chan []byte, outgoingData chan<- []byte, terminationSignal <-chan struct{}, isStream bool) (net.Conn, error) {
func DialUDT(config *Config, closer Closer, incomingData <-chan []byte, outgoingData chan<- []byte, terminationSignal <-chan struct{}, isStream bool) (net.Conn, error) {
m := newMultiplexer(closer, config.MaxPacketSize, incomingData, outgoingData, terminationSignal)
s := m.newSocket(config, false, !isStream)

View File

@@ -38,6 +38,7 @@ type shutdownMessage struct {
sockState sockState
permitLinger bool
err error
reason int
}
/*
@@ -463,7 +464,7 @@ func (s *udtSocket) goManageConnection() {
for {
select {
case <-s.lingerTimer: // linger timer expired, shut everything down
s.shutdown(sockStateClosed, false, nil)
s.shutdown(sockStateClosed, false, nil, TerminateReasonLingerTimerExpired)
return
case <-s.sockClosed:
return
@@ -473,9 +474,9 @@ func (s *udtSocket) goManageConnection() {
//fmt.Printf("(id=%d) sending %s (id=%d)\n", s.sockID, packet.PacketTypeName(p.PacketType()), s.farSockID)
s.m.sendPacket(s.farSockID, ts, p)
case sd := <-s.shutdownEvent: // connection shut down
s.shutdown(sd.sockState, sd.permitLinger, sd.err)
s.shutdown(sd.sockState, sd.permitLinger, sd.err, sd.reason)
case <-s.connTimeout: // connection timed out
s.shutdown(sockStateTimeout, true, nil)
s.shutdown(sockStateTimeout, true, nil, TerminateReasonConnectTimeout)
case <-s.connRetry: // resend connection attempt
s.connRetry = nil
switch s.sockState {
@@ -596,7 +597,7 @@ func (s *udtSocket) readHandshake(m *multiplexer, p *packet.HandshakePacket) boo
return false
}
func (s *udtSocket) shutdown(sockState sockState, permitLinger bool, err error) {
func (s *udtSocket) shutdown(sockState sockState, permitLinger bool, err error, reason int) {
if !s.isOpen() {
return // already closed
}
@@ -612,6 +613,7 @@ func (s *udtSocket) shutdown(sockState sockState, permitLinger bool, err error)
linger = DefaultConfig().LingerTime
}
s.lingerTimer = time.After(linger)
s.m.closer.CloseLinger(reason)
return
}
@@ -627,7 +629,7 @@ func (s *udtSocket) shutdown(sockState sockState, permitLinger bool, err error)
close(s.sockClosed)
close(s.recvEvent)
s.m.closer.Close()
s.m.closer.Close(reason)
s.messageIn <- nil
}
@@ -688,7 +690,7 @@ func (s *udtSocket) readPacket(m *multiplexer, p packet.Packet) {
case *packet.HandshakePacket: // sent by both peers
s.readHandshake(m, sp)
case *packet.ShutdownPacket: // sent by either peer
s.shutdownEvent <- shutdownMessage{sockState: sockStateClosed, permitLinger: s.isServer} // if client tells us done, it is done.
s.shutdownEvent <- shutdownMessage{sockState: sockStateClosed, permitLinger: s.isServer, reason: TerminateReasonRemoteSentShutdown} // if client tells us done, it is done.
case *packet.AckPacket, *packet.NakPacket: // receiver -> sender
s.sendEvent <- recvPktEvent{pkt: p, now: now}
case *packet.UserDefControlPacket:

View File

@@ -301,7 +301,7 @@ func (s *udtSocketRecv) reassemblePacketPiecesDatagram(p *packet.DataPacket) (pi
nextBoundary, _, nextMsg := nextPiece.pkt.GetMessageData()
if nextMsg != msgID {
// ...oops? previous piece isn't in the same message
break
return nil, false
}
pieces = append(pieces, nextPiece.pkt)
if nextBoundary == packet.MbLast {

View File

@@ -116,7 +116,7 @@ func (s *udtSocketSend) goSendEvent() {
case msg, ok := <-thisMsgChan: // nil if we can't process outgoing messages right now
if !ok {
s.sendPacket <- &packet.ShutdownPacket{}
s.shutdownEvent <- shutdownMessage{sockState: sockStateClosed, permitLinger: !s.socket.isServer}
s.shutdownEvent <- shutdownMessage{sockState: sockStateClosed, permitLinger: !s.socket.isServer, reason: TerminateReasonCannotProcessOutgoing}
return
}
s.msgPartialSend = &msg
@@ -334,10 +334,10 @@ func (s *udtSocketSend) sendDataPacket(dp sendPacketEntry, isResend bool) {
}
}
func (s *udtSocketSend) assertValidSentPktID(pktType string, pktSeq packet.PacketID) bool {
func (s *udtSocketSend) assertValidSentPktID(pktType string, pktSeq packet.PacketID, reason int) bool {
if s.sendPktSeq.BlindDiff(pktSeq) < 0 {
s.shutdownEvent <- shutdownMessage{sockState: sockStateCorrupted, permitLinger: false,
err: fmt.Errorf("FAULT: Received an %s for packet %d, but the largest packet we've sent has been %d", pktType, pktSeq.Seq, s.sendPktSeq.Seq)}
err: fmt.Errorf("FAULT: Received an %s for packet %d, but the largest packet we've sent has been %d", pktType, pktSeq.Seq, s.sendPktSeq.Seq), reason: reason}
return false
}
return true
@@ -350,7 +350,7 @@ func (s *udtSocketSend) ingestAck(p *packet.AckPacket, now time.Time) {
// Send back an ACK2 with the same ACK sequence number in this ACK.
s.sendPacket <- &packet.Ack2Packet{AckSeqNo: p.AckSeqNo}
if !s.assertValidSentPktID("ACK", p.PktSeqHi) || p.PktSeqHi.BlindDiff(s.recvAckSeq) <= 0 {
if !s.assertValidSentPktID("ACK", p.PktSeqHi, TerminateReasonInvalidPacketIDAck) || p.PktSeqHi.BlindDiff(s.recvAckSeq) <= 0 {
return
}
@@ -388,20 +388,20 @@ func (s *udtSocketSend) ingestNak(p *packet.NakPacket, now time.Time) {
thisPktID := packet.PacketID{Seq: thisEntry & 0x7FFFFFFF}
if idx+1 == clen {
s.shutdownEvent <- shutdownMessage{sockState: sockStateCorrupted, permitLinger: false,
err: fmt.Errorf("FAULT: While unpacking a NAK, the last entry (%x) was describing a start-of-range", thisEntry)}
err: fmt.Errorf("FAULT: While unpacking a NAK, the last entry (%x) was describing a start-of-range", thisEntry), reason: TerminateReasonCorruptPacketNak}
return
}
if !s.assertValidSentPktID("NAK", thisPktID) {
if !s.assertValidSentPktID("NAK", thisPktID, TerminateReasonInvalidPacketIDNak) {
return
}
lastEntry := p.CmpLossInfo[idx+1]
if lastEntry&0x80000000 != 0 {
s.shutdownEvent <- shutdownMessage{sockState: sockStateCorrupted, permitLinger: false,
err: fmt.Errorf("FAULT: While unpacking a NAK, a start-of-range (%x) was followed by another start-of-range (%x)", thisEntry, lastEntry)}
err: fmt.Errorf("FAULT: While unpacking a NAK, a start-of-range (%x) was followed by another start-of-range (%x)", thisEntry, lastEntry), reason: TerminateReasonCorruptPacketNak}
return
}
lastPktID := packet.PacketID{Seq: lastEntry}
if !s.assertValidSentPktID("NAK", lastPktID) {
if !s.assertValidSentPktID("NAK", lastPktID, TerminateReasonInvalidPacketIDNak) {
return
}
idx++
@@ -411,7 +411,7 @@ func (s *udtSocketSend) ingestNak(p *packet.NakPacket, now time.Time) {
}
} else {
thisPktID := packet.PacketID{Seq: thisEntry}
if !s.assertValidSentPktID("NAK", thisPktID) {
if !s.assertValidSentPktID("NAK", thisPktID, TerminateReasonInvalidPacketIDNak) {
return
}
s.sendLossList.Add(recvLossEntry{packetID: thisPktID})
@@ -457,7 +457,7 @@ func (s *udtSocketSend) expEvent(currTime time.Time) {
// timeout: at least 16 expirations and must be greater than 10 seconds
if (s.expCount > 16) && (currTime.Sub(s.lastRecvTime) > 5*time.Second) {
// Connection is broken.
s.shutdownEvent <- shutdownMessage{sockState: sockStateTimeout, permitLinger: true}
s.shutdownEvent <- shutdownMessage{sockState: sockStateTimeout, permitLinger: true, reason: TerminateReasonExpireTimer}
return
}