/
server.go
156 lines (137 loc) · 3.74 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
// udpserver runs a simple UDP server. It is invoked with the PacketHandler
// interface which will Receive packets as byte slices along with the address
// they were received from.
package udpserver
import (
"github.com/adamcolton/err"
"net"
"time"
)
const MaxUdpPacketLength = 65507
type UDPServer struct {
conn *net.UDPConn
packetHandler PacketHandler
localIP string
stop, running bool
}
type PacketHandler interface {
Receive([]byte, *net.UDPAddr)
}
type UDPAddr net.UDPAddr
// New creates a UDPserver
// passing in ":0" for port will select any open port
func New(port string, packetHandler PacketHandler) (*UDPServer, error) {
laddr, e := net.ResolveUDPAddr("udp", port)
if !err.Check(e) {
return nil, e
}
conn, e := net.ListenUDP("udp", laddr)
if !err.Check(e) {
return nil, e
}
server := &UDPServer{
conn: conn,
packetHandler: packetHandler,
localIP: getLocalIP(),
stop: false,
running: false,
}
return server, nil
}
// RunNew is a wrapper around new that also calls Run in a Go routine if the
// server was created without error
func RunNew(port string, packetHandler PacketHandler) (*UDPServer, error) {
s, e := New(port, packetHandler)
if err.Check(e) {
go s.Run()
}
return s, e
}
// Run is the servers listen loop. When it receives a message it will pass that
// message into the packetHandler
func (s *UDPServer) Run() {
if s.running || s.conn == nil {
return
}
s.running = true
buf := make([]byte, MaxUdpPacketLength)
for {
l, addr, e := s.conn.ReadFromUDP(buf)
if s.stop {
break
}
if err.Log(e) {
packet := make([]byte, l)
copy(packet, buf[:l])
go s.packetHandler.Receive(packet, addr)
}
}
s.running = false
}
// Returns true if the server is running and can receive messages
// Even if the server is not running, it can still send
func (s *UDPServer) IsRunning() bool { return s.running }
// Returns true if the connection is open
// If the server is closed, it can neither send nor receive
func (s *UDPServer) IsOpen() bool { return s.conn != nil }
// Stop will stop the server
func (s *UDPServer) Stop() error {
s.stop = true
return s.conn.SetReadDeadline(time.Now()) // kill all reads
}
// Close will close the connection, freeing the port
func (s *UDPServer) Close() error {
e := s.Stop()
if !err.Check(e) {
return e
}
if e = s.conn.Close(); err.Check(e) {
s.conn = nil
}
return e
}
// Send will send a single packe (byte slice) to an address
// just a wrapper around WriteToUDP
func (s *UDPServer) Send(packet []byte, addr *net.UDPAddr) (int, error) {
return s.conn.WriteToUDP(packet, addr)
}
// SendAll sends a slice of packets (byte slices) to an address
// this will return the last error it encoutered, if it encountered any
func (s *UDPServer) SendAll(packets [][]byte, addr *net.UDPAddr) error {
var last error
for _, p := range packets {
if _, e := s.Send(p, addr); e != nil {
last = e
}
time.Sleep(time.Millisecond)
}
return last
}
// LocalIP is a getter for the localIP, which is set when the server starts
func (s *UDPServer) LocalIP() string { return s.localIP }
// getLocalIp loops over the interface addresses to find one that is not a loopback
// address and uses that as it's local IP. It may not be fool proof and requires
// further investigation, but it does seem to work.
func getLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
panic(err)
}
for _, a := range addrs {
var ip *net.IP
if ipnet, ok := a.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
ip = &ipnet.IP
} else if ipaddr, ok := a.(*net.IPAddr); ok && !ipaddr.IP.IsLoopback() {
ip = &ipaddr.IP
}
if ip != nil {
if ip.To4() != nil {
addr := ip.String()
if addr != "0.0.0.0" {
return addr
}
}
}
}
return ""
}