-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsocket.go
More file actions
203 lines (174 loc) · 5.7 KB
/
Copy pathsocket.go
File metadata and controls
203 lines (174 loc) · 5.7 KB
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
package netcode
import (
"errors"
"fmt"
"net"
)
const (
clientSocketSndbufSize = 4 * 1024 * 1024
clientSocketRcvbufSize = 4 * 1024 * 1024
serverSocketSndbufSize = 4 * 1024 * 1024
serverSocketRcvbufSize = 4 * 1024 * 1024
// Received packets are buffered on a channel between the reader goroutine
// and Update. If the channel fills up faster than Update drains it, further
// packets are dropped, just as the OS drops packets when a socket buffer
// overflows.
socketPacketChannelSize = 1024
)
var packetTaggingEnabled bool
// EnablePacketTagging tags packets sent from sockets created after this call
// as low latency (DSCP EF) which can significantly reduce jitter on Wi-Fi
// routers. It is off by default because it doesn't play well with some older
// home routers.
func EnablePacketTagging() {
packetTaggingEnabled = true
}
type receivedPacket struct {
from Address
data []byte
}
// socket wraps a UDP socket. The C implementation polls non-blocking sockets;
// here a reader goroutine blocks on the socket and buffers packets on a
// channel, which Update drains. The public API remains poll based and single
// threaded, matching the C library.
type socket struct {
address Address // the address the socket is bound to, with the resolved port
conn *net.UDPConn
packets chan receivedPacket
}
type socketHolder struct {
ipv4 *socket
ipv6 *socket
}
type socketError struct {
bind bool // a bind failure (port in use), as opposed to any other socket error
err error
}
func (e *socketError) Error() string { return e.err.Error() }
func (e *socketError) Unwrap() error { return e.err }
func createSocket(address *Address, sendBufferSize int, receiveBufferSize int) (*socket, error) {
network := "udp4"
if address.Type == AddressIPv6 {
network = "udp6" // Go sets IPV6_V6ONLY for the "udp6" network
}
conn, err := net.ListenUDP(network, net.UDPAddrFromAddrPort(address.toNetip()))
if err != nil {
bind := isBindError(err)
printf(LogLevelError, "error: failed to %s socket (%s)\n", map[bool]string{true: "bind", false: "create"}[bind], network)
return nil, &socketError{bind: bind, err: err}
}
// increase socket send and receive buffer sizes. linux and windows clamp requests
// that exceed the OS limit, but the BSDs reject them instead, so back off until accepted.
{
size := sendBufferSize
for conn.SetWriteBuffer(size) != nil {
size /= 2
if size < 256*1024 {
printf(LogLevelError, "error: failed to set socket send buffer size\n")
_ = conn.Close()
return nil, &socketError{err: errors.New("netcode: failed to set socket send buffer size")}
}
}
if size != sendBufferSize {
printf(LogLevelInfo, "socket send buffer size reduced from %d to %d\n", sendBufferSize, size)
}
}
{
size := receiveBufferSize
for conn.SetReadBuffer(size) != nil {
size /= 2
if size < 256*1024 {
printf(LogLevelError, "error: failed to set socket receive buffer size\n")
_ = conn.Close()
return nil, &socketError{err: errors.New("netcode: failed to set socket receive buffer size")}
}
}
if size != receiveBufferSize {
printf(LogLevelInfo, "socket receive buffer size reduced from %d to %d\n", receiveBufferSize, size)
}
}
// tag packets as low latency
if packetTaggingEnabled {
if err := enablePacketTagging(conn, address.Type == AddressIPv6); err != nil {
printf(LogLevelError, "error: failed to enable packet tagging (%s)\n", network)
_ = conn.Close()
return nil, &socketError{err: fmt.Errorf("netcode: failed to enable packet tagging: %w", err)}
}
}
s := &socket{
address: *address,
conn: conn,
packets: make(chan receivedPacket, socketPacketChannelSize),
}
// if bound to port 0 find the actual port we got
if localAddr, ok := conn.LocalAddr().(*net.UDPAddr); ok {
s.address.Port = uint16(localAddr.Port)
}
go s.readLoop()
return s, nil
}
func (s *socket) readLoop() {
for {
buffer := make([]byte, maxPacketBytes)
bytes, from, err := s.conn.ReadFromUDPAddrPort(buffer)
if err != nil {
if errors.Is(err, net.ErrClosed) {
return
}
printf(LogLevelError, "error: socket receive failed with error %v\n", err)
continue
}
if bytes <= 0 {
continue
}
packet := receivedPacket{from: addressFromNetip(from.Addr(), from.Port()), data: buffer[:bytes]}
select {
case s.packets <- packet:
default:
// channel full: drop the packet
}
}
}
func (s *socket) sendPacket(to *Address, packetData []byte) {
// UDP send is fire and forget: errors are deliberately ignored, matching
// the C implementation
_, _ = s.conn.WriteToUDPAddrPort(packetData, to.toNetip())
}
// receivePacket returns the next buffered packet, or false if none are pending.
// It never blocks.
func (s *socket) receivePacket() (receivedPacket, bool) {
select {
case packet := <-s.packets:
return packet, true
default:
return receivedPacket{}, false
}
}
func (s *socket) destroy() {
if s != nil && s.conn != nil {
_ = s.conn.Close()
}
}
func (h *socketHolder) destroy() {
h.ipv4.destroy()
h.ipv6.destroy()
}
// sendPacketToAddress is shared by the client and server send paths: dispatch a
// written packet to the network simulator, the send override, or the socket
// matching the destination address family.
func sendPacketToAddress(networkSimulator *NetworkSimulator,
sendPacketOverride func(to *Address, packetData []byte),
holder *socketHolder,
from *Address,
to *Address,
packetData []byte) {
if networkSimulator != nil {
networkSimulator.SendPacket(from, to, packetData)
} else if sendPacketOverride != nil {
sendPacketOverride(to, packetData)
} else if to.Type == AddressIPv4 && holder.ipv4 != nil {
holder.ipv4.sendPacket(to, packetData)
} else if to.Type == AddressIPv6 && holder.ipv6 != nil {
holder.ipv6.sendPacket(to, packetData)
}
}