├── .gitignore ├── LICENSE ├── README.md ├── event_handler.go ├── event_loop_linux.go ├── go.mod ├── listener_linux.go ├── netpoll └── epoll_linux.go ├── netudp ├── udp.go ├── udp_msgshdr_linux.go ├── udp_read_write_linux.go └── udp_sock_linux.go ├── server_linux.go └── server_windows.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, built with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | 14 | # Misc 15 | *.sum 16 | sample/* 17 | 18 | # Dependency directories (remove the comment below to include it) 19 | # vendor/ 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 shaoyuan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fastudp 2 | fast and more fast 3 | -------------------------------------------------------------------------------- /event_handler.go: -------------------------------------------------------------------------------- 1 | package fastudp 2 | 3 | import "net" 4 | 5 | type EventHandler interface { 6 | OnReaded([]byte, *net.UDPAddr) 7 | OnError(err error) 8 | } 9 | -------------------------------------------------------------------------------- /event_loop_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package fastudp 5 | 6 | import ( 7 | "net" 8 | "os" 9 | "runtime" 10 | "sync" 11 | "sync/atomic" 12 | "unsafe" 13 | 14 | "golang.org/x/sys/unix" 15 | 16 | "github.com/shaoyuan1943/fastudp/netpoll" 17 | "github.com/shaoyuan1943/fastudp/netudp" 18 | ) 19 | 20 | var ( 21 | MsgHdrSize = 128 22 | ReadEventSize = 128 23 | WriteEventSize = 128 24 | ) 25 | 26 | type eventLoop struct { 27 | internalLoop 28 | _ [64 - unsafe.Sizeof(internalLoop{})%64]byte 29 | } 30 | 31 | type internalLoop struct { 32 | l *listener 33 | poller *netpoll.Poller 34 | rw *netudp.ReaderWriter 35 | svr *Server 36 | once sync.Once 37 | readNotifyC chan struct{} 38 | writePool sync.Pool 39 | closed atomic.Value 40 | writeQueue []*netudp.Mmsg 41 | sync.Locker 42 | } 43 | 44 | func newEventLoop(s *Server, l *listener, poller *netpoll.Poller, mtu int) *eventLoop { 45 | loop := &eventLoop{} 46 | loop.l = l 47 | loop.poller = poller 48 | loop.rw = netudp.NewRW(l.fd, MsgHdrSize, mtu) 49 | loop.svr = s 50 | loop.readNotifyC = make(chan struct{}, ReadEventSize) 51 | loop.writePool.New = func() interface{} { 52 | p := &netudp.Mmsg{ 53 | Data: make([]byte, mtu), 54 | } 55 | 56 | return p 57 | } 58 | loop.writeQueue = make([]*netudp.Mmsg, WriteEventSize) 59 | loop.writeQueue = loop.writeQueue[:0] 60 | loop.closed.Store(false) 61 | return loop 62 | } 63 | 64 | func (loop *eventLoop) Close(err error) { 65 | loop.once.Do(func() { 66 | loop.poller.Del(loop.l.fd) 67 | loop.poller.Close() 68 | close(loop.readNotifyC) 69 | loop.closed.Store(true) 70 | loop.svr.eventLoopClosed(loop, err) 71 | }) 72 | } 73 | 74 | func (loop *eventLoop) run(lockThread bool) { 75 | if lockThread { 76 | runtime.LockOSThread() 77 | defer runtime.UnlockOSThread() 78 | } 79 | 80 | err := loop.poller.Polling(loop.pollEvent) 81 | loop.Close(err) 82 | } 83 | 84 | // Epoll return current status of fd, 85 | // EPOLLOUT will only be returned when the fd's status changes from "cannnot ouput" to "can ouput", 86 | // More information: https://www.spinics.net/lists/linux-api/msg01872.html 87 | func (loop *eventLoop) pollEvent(fd int32, events uint32) { 88 | if fd == int32(loop.l.fd) && netudp.IsUDP(loop.l.network) { 89 | if !loop.closed.Load().(bool) { 90 | if events&unix.EPOLLIN != 0 { 91 | loop.readNotifyC <- struct{}{} 92 | } 93 | 94 | if events&unix.EPOLLOUT != 0 { 95 | loop.onEpollout() 96 | } 97 | } 98 | } 99 | } 100 | 101 | func (loop *eventLoop) readLoop() { 102 | for _ = range loop.readNotifyC { 103 | loop.rw.ReadFrom(func(data []byte, addr *net.UDPAddr, err error) { 104 | if err != nil { 105 | loop.Close(err) 106 | return 107 | } 108 | 109 | loop.svr.handler.OnReaded(data, addr) 110 | }) 111 | } 112 | } 113 | 114 | func (loop *eventLoop) writeTo(data []byte, addr *net.UDPAddr) (int, error) { 115 | err := loop.rw.WriteTo(data, addr) 116 | if err != nil { 117 | errno := err.(*os.SyscallError).Unwrap() 118 | if errno.Error() == "EINTR" || errno.Error() == "EAGIN" { 119 | loop.Lock() 120 | defer loop.Unlock() 121 | 122 | p := loop.writePool.Get().(*netudp.Mmsg) 123 | p.Addr = addr 124 | p.Data = p.Data[:len(data)] 125 | copy(p.Data, data) 126 | 127 | loop.writeQueue = append(loop.writeQueue, p) 128 | loop.poller.Mod(loop.l.fd, "rw") 129 | err = errno 130 | } else { 131 | loop.Close(err) 132 | } 133 | } 134 | 135 | return len(data), err 136 | } 137 | 138 | // write return value 139 | const ( 140 | retry = 1 << iota 141 | failed = 1 << iota 142 | succeed = 1 << iota 143 | ) 144 | 145 | func (loop *eventLoop) onEpollout() { 146 | n := len(loop.writeQueue) 147 | if n <= 0 { 148 | return 149 | } 150 | 151 | writeFunc := func(mmsgs []*netudp.Mmsg) (writed int) { 152 | if len(mmsgs) > 0 { 153 | _, err := loop.rw.WriteToN(mmsgs...) 154 | if err != nil { 155 | errno := err.(*os.SyscallError).Unwrap() 156 | if errno.Error() == "EINTR" || errno.Error() == "EAGIN" { 157 | writed = retry 158 | return 159 | } 160 | 161 | loop.Close(err) 162 | writed = failed 163 | return 164 | } 165 | } 166 | 167 | writed = succeed 168 | return 169 | } 170 | 171 | returnBackFunc := func(mmsgs []*netudp.Mmsg) { 172 | for i := 0; i < len(mmsgs); i++ { 173 | loop.writePool.Put(mmsgs[i]) 174 | } 175 | } 176 | 177 | loop.Lock() 178 | defer loop.Unlock() 179 | 180 | if n <= WriteEventSize { 181 | switch writeFunc(loop.writeQueue) { 182 | case retry: 183 | case failed: 184 | return 185 | case succeed: 186 | returnBackFunc(loop.writeQueue) 187 | loop.writeQueue = loop.writeQueue[:0] 188 | } 189 | } else { 190 | step := n / WriteEventSize 191 | surplus := n % WriteEventSize 192 | if surplus > 0 { 193 | switch writeFunc(loop.writeQueue[(n - surplus):n]) { 194 | case retry: 195 | case failed: 196 | return 197 | case succeed: 198 | returnBackFunc(loop.writeQueue[(n - surplus):n]) 199 | loop.writeQueue = loop.writeQueue[:(n - surplus)] 200 | } 201 | } 202 | 203 | n := len(loop.writeQueue) 204 | step-- 205 | for step >= 0 { 206 | switch writeFunc(loop.writeQueue[step*WriteEventSize : n]) { 207 | case retry: 208 | case failed: 209 | return 210 | case succeed: 211 | returnBackFunc(loop.writeQueue[step*WriteEventSize : n]) 212 | loop.writeQueue = loop.writeQueue[:step*WriteEventSize] 213 | n = len(loop.writeQueue) 214 | step-- 215 | } 216 | } 217 | } 218 | 219 | loop.poller.Mod(loop.l.fd, "rw") 220 | } 221 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/shaoyuan1943/fastudp 2 | 3 | go 1.15 4 | 5 | require golang.org/x/sys v0.0.0-20210309074719-68d13333faf2 6 | -------------------------------------------------------------------------------- /listener_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package fastudp 4 | 5 | import ( 6 | "github.com/shaoyuan1943/fastudp/netudp" 7 | "golang.org/x/sys/unix" 8 | ) 9 | 10 | type listener struct { 11 | addr unix.Sockaddr 12 | fd int 13 | network string 14 | } 15 | 16 | func listen(network, addr string, reusePort bool) (*listener, error) { 17 | l := &listener{} 18 | fd, sockaddr, err := netudp.NewUDPSocket(network, addr, reusePort) 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | l.fd = fd 24 | l.addr = sockaddr 25 | l.network = network 26 | return l, nil 27 | } 28 | -------------------------------------------------------------------------------- /netpoll/epoll_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package netpoll 4 | 5 | import ( 6 | "fmt" 7 | "os" 8 | "runtime" 9 | 10 | "golang.org/x/sys/unix" 11 | ) 12 | 13 | const ( 14 | EPollEventSize = 128 15 | EPollErrEvent = unix.EPOLLERR | unix.EPOLLRDHUP | unix.EPOLLHUP 16 | EPollInEvent = EPollErrEvent | unix.EPOLLIN 17 | EPollOutEvent = EPollErrEvent | unix.EPOLLOUT 18 | ) 19 | 20 | type Poller struct { 21 | fd int 22 | } 23 | 24 | func PollerInit() (*Poller, error) { 25 | fd, err := unix.EpollCreate1(unix.EPOLL_CLOEXEC) 26 | if err != nil { 27 | return nil, os.NewSyscallError("epoll_create1", err) 28 | } 29 | 30 | poller := &Poller{ 31 | fd: fd, 32 | } 33 | 34 | return poller, nil 35 | } 36 | 37 | func (poller *Poller) Add(fd int, ev string) error { 38 | e := &unix.EpollEvent{ 39 | Fd: int32(fd), 40 | } 41 | 42 | switch ev { 43 | case "r": 44 | e.Events = unix.EPOLLIN | unix.EPOLLET 45 | case "w": 46 | e.Events = unix.EPOLLOUT | unix.EPOLLET 47 | case "rw": 48 | e.Events = unix.EPOLLIN | unix.EPOLLOUT | unix.EPOLLET 49 | default: 50 | return fmt.Errorf("unknow epoll event type") 51 | } 52 | 53 | return os.NewSyscallError("epoll_ctl add", unix.EpollCtl(poller.fd, unix.EPOLL_CTL_ADD, fd, e)) 54 | } 55 | 56 | func (poller *Poller) Mod(fd int, ev string) error { 57 | e := &unix.EpollEvent{ 58 | Fd: int32(fd), 59 | } 60 | 61 | switch ev { 62 | case "r": 63 | e.Events = unix.EPOLLIN | unix.EPOLLET 64 | case "w": 65 | e.Events = unix.EPOLLOUT | unix.EPOLLET 66 | case "rw": 67 | e.Events = unix.EPOLLIN | unix.EPOLLOUT | unix.EPOLLET 68 | default: 69 | return fmt.Errorf("unknow epoll event type") 70 | } 71 | 72 | return os.NewSyscallError("epoll_ctl mod", unix.EpollCtl(poller.fd, unix.EPOLL_CTL_MOD, fd, e)) 73 | } 74 | 75 | func (poller *Poller) Del(fd int) error { 76 | return os.NewSyscallError("epoll_ctl del", unix.EpollCtl(poller.fd, unix.EPOLL_CTL_DEL, fd, nil)) 77 | } 78 | 79 | func (poller *Poller) Close() error { 80 | return os.NewSyscallError("close", unix.Close(poller.fd)) 81 | } 82 | 83 | func (poller *Poller) Polling(eventHandler func(fd int32, ev uint32)) error { 84 | evs := make([]unix.EpollEvent, EPollEventSize) 85 | for { 86 | // See: https://man7.org/linux/man-pages/man2/epoll_pwait2.2.html 87 | // In Go, if epoll_wait not return event or interrupted by a signal, 88 | // We can hand over to the runtime for scheduling to educe CPU usage 89 | msec := -1 90 | n, err := unix.EpollWait(poller.fd, evs, msec) 91 | if n == 0 || (n < 0 && err == unix.EINTR) { 92 | msec = -1 93 | runtime.Gosched() 94 | continue 95 | } 96 | 97 | if err != nil { 98 | return os.NewSyscallError("epoll_wait", err) 99 | } 100 | 101 | msec = 0 102 | for i := 0; i < n; i++ { 103 | eventHandler(evs[i].Fd, evs[i].Events) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /netudp/udp.go: -------------------------------------------------------------------------------- 1 | package netudp 2 | 3 | import "strings" 4 | 5 | func IsUDP(network string) bool { 6 | switch strings.ToLower(network) { 7 | case "udp", "udp4", "udp6": 8 | return true 9 | } 10 | 11 | return false 12 | } 13 | -------------------------------------------------------------------------------- /netudp/udp_msgshdr_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package netudp 4 | 5 | import "unsafe" 6 | 7 | // This file content copied from golang.org/x/net/internal/socket/zsys_linux_amd64.go 8 | // Don't modify only when you know what to do 9 | 10 | type iovec struct { 11 | Base *byte 12 | Len uint64 13 | } 14 | 15 | type msghdr struct { 16 | Name *byte 17 | Namelen uint32 18 | Pad_cgo_0 [4]byte 19 | Iov *iovec 20 | Iovlen uint64 21 | Control *byte 22 | Controllen uint64 23 | Flags int32 24 | Pad_cgo_1 [4]byte 25 | } 26 | 27 | type mmsghdr struct { 28 | Hdr msghdr 29 | Len uint32 30 | Pad_cgo_0 [4]byte 31 | } 32 | 33 | type sockaddrFamily struct { 34 | Family uint16 35 | } 36 | 37 | const ( 38 | sizeofIovec = 0x10 39 | sizeofMsghdr = 0x38 40 | 41 | sizeofSockaddrInet = 0x10 // IPv4 42 | sizeofSockaddrInet6 = 0x1c // IPv6 43 | ) 44 | 45 | func prepare(n, mtu int) ([]mmsghdr, [][]byte, [][]byte) { 46 | mms := make([]mmsghdr, n) 47 | buffers := make([][]byte, n) 48 | names := make([][]byte, n) 49 | 50 | for i := range mms { 51 | buffers[i] = make([]byte, mtu) 52 | names[i] = make([]byte, sizeofSockaddrInet6) 53 | 54 | v := []iovec{ 55 | {Base: (*byte)(unsafe.Pointer(&buffers[i][0])), Len: uint64(len(buffers[i]))}, 56 | } 57 | 58 | mms[i].Hdr.Iov = &v[0] 59 | mms[i].Hdr.Iovlen = uint64(len(v)) 60 | 61 | mms[i].Hdr.Name = (*byte)(unsafe.Pointer(&names[i][0])) 62 | mms[i].Hdr.Namelen = uint32(len(names[i])) 63 | 64 | // ignore mms[i].Hdr.Control and mms[i].Hdr.Controllen 65 | } 66 | 67 | return mms, buffers, names 68 | } 69 | -------------------------------------------------------------------------------- /netudp/udp_read_write_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package netudp 5 | 6 | import ( 7 | "fmt" 8 | "net" 9 | "os" 10 | "reflect" 11 | "unsafe" 12 | 13 | "golang.org/x/sys/unix" 14 | ) 15 | 16 | var zero [32]byte 17 | 18 | type Mmsg struct { 19 | Addr *net.UDPAddr 20 | Data []byte 21 | } 22 | 23 | type ReaderWriter struct { 24 | fd int 25 | msgs []mmsghdr 26 | buffers [][]byte 27 | names [][]byte 28 | remoteAddr *net.UDPAddr 29 | dc [32]byte 30 | mtu int 31 | sockaddr4 unix.RawSockaddrInet4 32 | sockaddr6 unix.RawSockaddrInet6 33 | } 34 | 35 | func NewRW(fd, n, mtu int) *ReaderWriter { 36 | rw := &ReaderWriter{} 37 | rw.fd = fd 38 | rw.mtu = mtu 39 | rw.msgs, rw.buffers, rw.names = prepare(n, mtu) 40 | rw.remoteAddr = &net.UDPAddr{} 41 | return rw 42 | } 43 | 44 | func (rw *ReaderWriter) ReadFrom(readFunc func([]byte, *net.UDPAddr, error)) { 45 | n, err := rw.read() 46 | if err != nil { 47 | readFunc(nil, nil, err) 48 | return 49 | } 50 | 51 | for i := 0; i < n; i++ { 52 | familyData := rw.names[i][:2] 53 | afNet := (*sockaddrFamily)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&familyData)).Data)) 54 | switch afNet.Family { 55 | case unix.AF_INET: 56 | rw.names[i][2], rw.names[i][3] = rw.names[i][3], rw.names[i][2] //字节序转换 57 | sockaddrInet := (*unix.RawSockaddrInet4)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&rw.names[i])).Data)) 58 | rw.remoteAddr.IP = sockaddrInet.Addr[:] 59 | rw.remoteAddr.Port = int(sockaddrInet.Port) 60 | case unix.AF_INET6: 61 | rw.names[i][2], rw.names[i][3] = rw.names[i][3], rw.names[i][2] //字节序转换 62 | sockaddrInet6 := (*unix.RawSockaddrInet6)(unsafe.Pointer((*reflect.SliceHeader)(unsafe.Pointer(&rw.names[i])).Data)) 63 | rw.remoteAddr.IP = sockaddrInet6.Addr[:] 64 | rw.remoteAddr.Port = int(sockaddrInet6.Port) 65 | rw.remoteAddr.Zone = rw.zoneID2String(int(sockaddrInet6.Scope_id)) 66 | default: 67 | err := fmt.Errorf("unknown net family") 68 | readFunc(nil, nil, err) 69 | return 70 | } 71 | 72 | readFunc(rw.buffers[i][:rw.msgs[i].Len], rw.remoteAddr, nil) 73 | 74 | if afNet.Family == unix.AF_INET6 { 75 | copy(rw.dc[:], zero[:]) 76 | } 77 | } 78 | } 79 | 80 | // See: https://www.man7.org/linux/man-pages/man2/recvmmsg.2.html 81 | func (rw *ReaderWriter) read() (int, error) { 82 | n, _, err := unix.Syscall6(unix.SYS_RECVMMSG, uintptr(rw.fd), 83 | uintptr(unsafe.Pointer(&rw.msgs[0])), uintptr(len(rw.msgs)), unix.MSG_WAITFORONE, 84 | 0, 0, 85 | ) 86 | 87 | if err != 0 { 88 | if err == unix.EAGAIN || err == unix.EWOULDBLOCK { 89 | return 0, nil 90 | } 91 | 92 | return 0, os.NewSyscallError("recvmmsg", fmt.Errorf("%v", unix.ErrnoName(err))) 93 | } 94 | 95 | return int(n), nil 96 | } 97 | 98 | func (rw *ReaderWriter) zoneID2String(zoneID int) string { 99 | if zoneID == 0 { 100 | return "" 101 | } 102 | 103 | if ifi, err := net.InterfaceByIndex(int(zoneID)); err == nil { 104 | return ifi.Name 105 | } 106 | 107 | n := len(rw.dc) 108 | for ; zoneID > 0; zoneID /= 10 { 109 | n-- 110 | rw.dc[n] = byte(zoneID%10) + '0' 111 | } 112 | 113 | return string(rw.dc[n:]) 114 | } 115 | 116 | func dtoi(s string, i0 int) (n int, i int, ok bool) { 117 | n = 0 118 | for i = i0; i < len(s) && '0' <= s[i] && s[i] <= '9'; i++ { 119 | n = n*10 + int(s[i]-'0') 120 | if n >= 0xFFFFFF { 121 | return 0, i, false 122 | } 123 | } 124 | 125 | if i == i0 { 126 | return 0, i, false 127 | } 128 | 129 | return n, i, true 130 | } 131 | 132 | func (rw *ReaderWriter) string2ZoneID(zone string) uint32 { 133 | if zone == "" { 134 | return 0 135 | } 136 | 137 | if ifi, err := net.InterfaceByName(zone); err == nil { 138 | return uint32(ifi.Index) 139 | } 140 | 141 | n, _, _ := dtoi(zone, 0) 142 | return uint32(n) 143 | } 144 | 145 | func (rw *ReaderWriter) WriteTo(data []byte, addr *net.UDPAddr) error { 146 | if addr == nil || data == nil { 147 | return fmt.Errorf("writeto: data or addr invalid") 148 | } 149 | 150 | if len(data) > rw.mtu { 151 | return fmt.Errorf("writeto: data length too long") 152 | } 153 | 154 | if addr.IP.To4() == nil { 155 | return rw.writeToIPv6(data, addr) 156 | } 157 | 158 | return rw.writeToIPv4(data, addr) 159 | } 160 | 161 | func (rw *ReaderWriter) writeToIPv4(data []byte, addr *net.UDPAddr) error { 162 | rw.sockaddr4.Family = unix.AF_INET 163 | port := (*[2]byte)(unsafe.Pointer(&rw.sockaddr4.Port)) 164 | port[0] = byte(addr.Port >> 8) 165 | port[1] = byte(addr.Port) 166 | 167 | copy(rw.sockaddr4.Addr[:], addr.IP) 168 | 169 | return rw.writeto(uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)), uintptr(0), uintptr(unsafe.Pointer(&rw.sockaddr4)), uintptr(unix.SizeofSockaddrInet4)) 170 | } 171 | 172 | func (rw *ReaderWriter) writeToIPv6(data []byte, addr *net.UDPAddr) error { 173 | rw.sockaddr6.Family = unix.AF_INET6 174 | rw.sockaddr6.Scope_id = rw.string2ZoneID(addr.Zone) 175 | port := (*[2]byte)(unsafe.Pointer(&rw.sockaddr6.Port)) 176 | port[0] = byte(addr.Port >> 8) 177 | port[1] = byte(addr.Port) 178 | 179 | copy(rw.sockaddr6.Addr[:], addr.IP) 180 | 181 | return rw.writeto(uintptr(unsafe.Pointer(&data[0])), uintptr(len(data)), uintptr(0), uintptr(unsafe.Pointer(&rw.sockaddr6)), uintptr(unix.SizeofSockaddrInet6)) 182 | } 183 | 184 | func (rw *ReaderWriter) writeto(data uintptr, dataLen uintptr, flags uintptr, sockaddr uintptr, sockaddrSize uintptr) error { 185 | _, _, err := unix.Syscall6(unix.SYS_SENDTO, uintptr(rw.fd), data, dataLen, flags, sockaddr, sockaddrSize) 186 | if err != 0 { 187 | return os.NewSyscallError("sendto", fmt.Errorf("%v", unix.ErrnoName(err))) 188 | } 189 | 190 | return nil 191 | } 192 | 193 | // See: https://man7.org/linux/man-pages/man2/sendmmsg.2.html 194 | func (rw *ReaderWriter) WriteToN(mmsgs ...*Mmsg) (int, error) { 195 | writed := 0 196 | n := len(mmsgs) 197 | mms := make([]mmsghdr, n) 198 | for i := 0; i < n; i++ { 199 | msg := mmsgs[i] 200 | if msg.Addr.IP.To4() == nil { // IPv6 201 | sockaddrInet6 := &unix.RawSockaddrInet6{} 202 | sockaddrInet6.Family = unix.AF_INET6 203 | sockaddrInet6.Scope_id = rw.string2ZoneID(msg.Addr.Zone) 204 | port := (*[2]byte)(unsafe.Pointer(&sockaddrInet6.Port)) 205 | port[0] = byte(msg.Addr.Port >> 8) 206 | port[1] = byte(msg.Addr.Port) 207 | copy(sockaddrInet6.Addr[:], msg.Addr.IP) 208 | 209 | l := unsafe.Sizeof(*sockaddrInet6) 210 | m := &reflect.SliceHeader{ 211 | Data: uintptr(unsafe.Pointer(sockaddrInet6)), 212 | Cap: int(l), 213 | Len: int(l), 214 | } 215 | name := *(*[]byte)(unsafe.Pointer(m)) 216 | mms[i].Hdr.Name = (*byte)(unsafe.Pointer(&name[0])) 217 | mms[i].Hdr.Namelen = uint32(len(name)) 218 | 219 | if len(msg.Data) > rw.mtu { 220 | msg.Data = msg.Data[:rw.mtu] 221 | } 222 | } else { // IPv4 223 | sockaddrInet4 := &unix.RawSockaddrInet4{} 224 | sockaddrInet4.Family = unix.AF_INET 225 | port := (*[2]byte)(unsafe.Pointer(&sockaddrInet4.Port)) 226 | port[0] = byte(msg.Addr.Port >> 8) 227 | port[1] = byte(msg.Addr.Port) 228 | copy(rw.sockaddr4.Addr[:], msg.Addr.IP) 229 | 230 | l := unsafe.Sizeof(*sockaddrInet4) 231 | m := &reflect.SliceHeader{ 232 | Data: uintptr(unsafe.Pointer(sockaddrInet4)), 233 | Cap: int(l), 234 | Len: int(l), 235 | } 236 | name := *(*[]byte)(unsafe.Pointer(m)) 237 | mms[i].Hdr.Name = (*byte)(unsafe.Pointer(&name[0])) 238 | mms[i].Hdr.Namelen = uint32(len(name)) 239 | } 240 | 241 | v := []iovec{ 242 | {Base: (*byte)(unsafe.Pointer(&msg.Data[0])), Len: uint64(len(msg.Data))}, 243 | } 244 | 245 | mms[i].Hdr.Iov = &v[0] 246 | mms[i].Hdr.Iovlen = uint64(len(v)) 247 | writed++ 248 | } 249 | 250 | _, _, err := unix.Syscall6(unix.SYS_SENDMMSG, uintptr(rw.fd), uintptr(unsafe.Pointer(&mms[0])), uintptr(len(mms)), uintptr(0), 0, 0) 251 | if err != 0 { 252 | return 0, os.NewSyscallError("sendmmsg", fmt.Errorf("%v", unix.ErrnoName(err))) 253 | } 254 | 255 | return writed, nil 256 | } 257 | -------------------------------------------------------------------------------- /netudp/udp_sock_linux.go: -------------------------------------------------------------------------------- 1 | // +build linux 2 | 3 | package netudp 4 | 5 | import ( 6 | "fmt" 7 | "net" 8 | "os" 9 | "syscall" 10 | 11 | "golang.org/x/sys/unix" 12 | ) 13 | 14 | func NewUDPSocket(network, addr string, reusePort bool) (int, unix.Sockaddr, error) { 15 | var sa unix.Sockaddr 16 | udpAddr, err := net.ResolveUDPAddr(network, addr) 17 | if err != nil { 18 | return 0, nil, fmt.Errorf("resolve addr err: %v", err) 19 | } 20 | 21 | netFamily := unix.AF_INET 22 | if udpAddr.IP.To4() == nil { 23 | netFamily = unix.AF_INET6 24 | } 25 | 26 | syscall.ForkLock.Lock() 27 | fd, err := unix.Socket(netFamily, unix.SOCK_DGRAM|unix.SOCK_NONBLOCK|unix.SOCK_CLOEXEC, unix.IPPROTO_UDP) 28 | if err == nil { 29 | unix.CloseOnExec(fd) 30 | } 31 | syscall.ForkLock.Unlock() 32 | 33 | defer func() { 34 | if err != nil { 35 | _ = unix.Close(fd) 36 | } 37 | }() 38 | 39 | switch network { 40 | case "udp": 41 | sockaddr := &unix.SockaddrInet4{} 42 | sockaddr.Port = udpAddr.Port 43 | sa = sockaddr 44 | case "udp4": 45 | sockaddr := &unix.SockaddrInet4{} 46 | sockaddr.Port = udpAddr.Port 47 | copy(sockaddr.Addr[:], udpAddr.IP.To4()) 48 | sa = sockaddr 49 | case "udp6": 50 | sockaddr := &unix.SockaddrInet6{} 51 | copy(sockaddr.Addr[:], udpAddr.IP.To16()) 52 | if udpAddr.Zone != "" { 53 | var iface *net.Interface 54 | iface, err = net.InterfaceByName(udpAddr.Zone) 55 | if err != nil { 56 | return 0, nil, fmt.Errorf("parse UDPAddr.Zone err: %v", err) 57 | } 58 | sockaddr.ZoneId = uint32(iface.Index) 59 | } 60 | sockaddr.Port = udpAddr.Port 61 | netFamily = unix.AF_INET6 62 | default: 63 | return 0, nil, fmt.Errorf("not support network") 64 | } 65 | 66 | if reusePort { 67 | if err = os.NewSyscallError("setsockopt", unix.SetsockoptInt(fd, unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)); err != nil { 68 | return 0, nil, err 69 | } 70 | } 71 | 72 | if err = os.NewSyscallError("bind", unix.Bind(fd, sa)); err != nil { 73 | return 0, nil, err 74 | } 75 | 76 | return fd, sa, nil 77 | } 78 | -------------------------------------------------------------------------------- /server_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package fastudp 5 | 6 | import ( 7 | "fmt" 8 | "net" 9 | "runtime" 10 | "sync" 11 | "sync/atomic" 12 | 13 | "github.com/shaoyuan1943/fastudp/netpoll" 14 | "github.com/shaoyuan1943/fastudp/netudp" 15 | ) 16 | 17 | type Server struct { 18 | wg sync.WaitGroup 19 | handler EventHandler 20 | loops map[int]*eventLoop 21 | wp chan []byte 22 | pool sync.Pool 23 | closed atomic.Value 24 | lockThread bool 25 | sync.Mutex 26 | } 27 | 28 | func NewUDPServer(network, addr string, reusePort bool, listenerN int, mtu int, handler EventHandler, enableLockThread bool) (*Server, error) { 29 | if !netudp.IsUDP(network) { 30 | return nil, fmt.Errorf("unknown network: %v", network) 31 | } 32 | 33 | svr := &Server{ 34 | handler: handler, 35 | loops: make(map[int]*eventLoop), 36 | wp: make(chan []byte, WriteEventSize), 37 | lockThread: enableLockThread, 38 | } 39 | 40 | svr.pool.New = func() interface{} { 41 | return make([]byte, mtu) 42 | } 43 | 44 | if reusePort { 45 | if listenerN <= 0 { 46 | listenerN = runtime.NumCPU() 47 | } 48 | } else { 49 | listenerN = 1 50 | } 51 | 52 | if err := svr.start(network, addr, reusePort, listenerN, mtu); err != nil { 53 | return nil, err 54 | } 55 | 56 | svr.closed.Store(false) 57 | return svr, nil 58 | } 59 | 60 | func (s *Server) start(network, addr string, reusePort bool, listenerN, mtu int) error { 61 | for i := 0; i < listenerN; i++ { 62 | l, err := listen(network, addr, reusePort) 63 | if err != nil { 64 | return err 65 | } 66 | 67 | poller, err := netpoll.PollerInit() 68 | if err != nil { 69 | return err 70 | } 71 | 72 | loop := newEventLoop(s, l, poller, mtu) 73 | s.loops[loop.l.fd] = loop 74 | poller.Add(loop.l.fd, "r") 75 | 76 | go loop.run(s.lockThread) 77 | go loop.readLoop() 78 | 79 | s.wg.Add(1) 80 | } 81 | 82 | return nil 83 | } 84 | 85 | func (svr *Server) Shutdown() { 86 | if svr.closed.Load().(bool) { 87 | return 88 | } 89 | 90 | loops := make(map[int]*eventLoop) 91 | svr.Lock() 92 | for fd, loop := range svr.loops { 93 | loops[fd] = loop 94 | } 95 | svr.Unlock() 96 | 97 | for _, loop := range loops { 98 | if !loop.closed.Load().(bool) { 99 | loop.Close(nil) 100 | } 101 | } 102 | 103 | svr.wg.Wait() 104 | svr.closed.Store(true) 105 | } 106 | 107 | func (svr *Server) eventLoopClosed(loop *eventLoop, err error) { 108 | svr.Lock() 109 | defer svr.Unlock() 110 | 111 | delete(svr.loops, loop.l.fd) 112 | svr.wg.Done() 113 | 114 | if err != nil { 115 | svr.handler.OnError(err) 116 | } 117 | } 118 | 119 | // TODO: need load balancing? 120 | func (svr *Server) WriteTo(data []byte, addr *net.UDPAddr) (int, error) { 121 | if svr.closed.Load().(bool) { 122 | return 0, fmt.Errorf("server closed") 123 | } 124 | 125 | var loop *eventLoop 126 | svr.Lock() 127 | for _, l := range svr.loops { 128 | loop = l 129 | break 130 | } 131 | svr.Unlock() 132 | 133 | if loop == nil { 134 | return 0, fmt.Errorf("not found valid event-loop") 135 | } 136 | 137 | return loop.writeTo(data, addr) 138 | } 139 | -------------------------------------------------------------------------------- /server_windows.go: -------------------------------------------------------------------------------- 1 | package fastudp 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | "sync/atomic" 8 | 9 | "github.com/shaoyuan1943/fastudp/netudp" 10 | ) 11 | 12 | type Server struct { 13 | wg sync.WaitGroup 14 | handler EventHandler 15 | conn *net.UDPConn 16 | err error 17 | closed atomic.Value 18 | } 19 | 20 | func NewUDPServer(network, addr string, mtu int, handler EventHandler) (*Server, error) { 21 | if !netudp.IsUDP(network) { 22 | return nil, fmt.Errorf("unknown network: %v", network) 23 | } 24 | 25 | svr := &Server{ 26 | handler: handler, 27 | } 28 | 29 | if err := svr.start(network, addr, mtu); err != nil { 30 | return nil, err 31 | } 32 | 33 | svr.closed.Store(false) 34 | return svr, nil 35 | } 36 | 37 | func (svr *Server) start(network, addr string, mtu int) error { 38 | udpAddr, err := net.ResolveUDPAddr(network, addr) 39 | if err != nil { 40 | return fmt.Errorf("resolve addr: %v", err) 41 | } 42 | 43 | conn, err := net.ListenUDP(network, udpAddr) 44 | if err != nil { 45 | return fmt.Errorf("listen udp: %v", err) 46 | } 47 | 48 | svr.conn = conn 49 | 50 | svr.wg.Add(1) 51 | go func() { 52 | defer svr.wg.Done() 53 | 54 | buffer := make([]byte, mtu) 55 | for { 56 | n, remoteAddr, err := conn.ReadFrom(buffer) 57 | if err != nil { 58 | return 59 | } 60 | 61 | if n > 0 { 62 | svr.handler.OnReaded(buffer[:n], remoteAddr.(*net.UDPAddr)) 63 | } 64 | 65 | } 66 | }() 67 | 68 | return nil 69 | } 70 | 71 | func (svr *Server) Shutdown(err error) { 72 | if svr.IsClosed() { 73 | return 74 | } 75 | 76 | svr.conn.Close() 77 | svr.wg.Wait() 78 | svr.closed.Store(true) 79 | } 80 | 81 | func (svr *Server) IsClosed() bool { 82 | return svr.closed.Load().(bool) 83 | } 84 | 85 | func (svr *Server) WriteTo(addr *net.UDPAddr, data []byte) (int, error) { 86 | if svr.closed.Load().(bool) { 87 | return 0, fmt.Errorf("server is closed") 88 | } 89 | 90 | return svr.conn.WriteTo(data, addr) 91 | } 92 | --------------------------------------------------------------------------------