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 }