github.com/kelleygo/clashcore@v1.0.2/common/net/packet/packet_windows.go (about)

     1  //go:build windows
     2  
     3  package packet
     4  
     5  import (
     6  	"net"
     7  	"strconv"
     8  	"syscall"
     9  
    10  	"github.com/kelleygo/clashcore/common/pool"
    11  
    12  	"golang.org/x/sys/windows"
    13  )
    14  
    15  type enhanceUDPConn struct {
    16  	*net.UDPConn
    17  	rawConn syscall.RawConn
    18  }
    19  
    20  func (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {
    21  	if c.rawConn == nil {
    22  		c.rawConn, _ = c.UDPConn.SyscallConn()
    23  	}
    24  	var readErr error
    25  	hasData := false
    26  	err = c.rawConn.Read(func(fd uintptr) (done bool) {
    27  		if !hasData {
    28  			hasData = true
    29  			// golang's internal/poll.FD.RawRead will Use a zero-byte read as a way to get notified when this
    30  			// socket is readable if we return false. So the `recvfrom` syscall will not block the system thread.
    31  			return false
    32  		}
    33  		readBuf := pool.Get(pool.UDPBufferSize)
    34  		put = func() {
    35  			_ = pool.Put(readBuf)
    36  		}
    37  		var readFrom windows.Sockaddr
    38  		var readN int
    39  		readN, readFrom, readErr = windows.Recvfrom(windows.Handle(fd), readBuf, 0)
    40  		if readN > 0 {
    41  			data = readBuf[:readN]
    42  		} else {
    43  			put()
    44  			put = nil
    45  			data = nil
    46  		}
    47  		if readErr == windows.WSAEWOULDBLOCK {
    48  			return false
    49  		}
    50  		if readFrom != nil {
    51  			switch from := readFrom.(type) {
    52  			case *windows.SockaddrInet4:
    53  				ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes
    54  				addr = &net.UDPAddr{IP: ip[:], Port: from.Port}
    55  			case *windows.SockaddrInet6:
    56  				ip := from.Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes
    57  				addr = &net.UDPAddr{IP: ip[:], Port: from.Port, Zone: strconv.FormatInt(int64(from.ZoneId), 10)}
    58  			}
    59  		}
    60  		// udp should not convert readN == 0 to io.EOF
    61  		//if readN == 0 {
    62  		//	readErr = io.EOF
    63  		//}
    64  		hasData = false
    65  		return true
    66  	})
    67  	if err != nil {
    68  		return
    69  	}
    70  	if readErr != nil {
    71  		err = readErr
    72  		return
    73  	}
    74  	return
    75  }