4
4
package ice
5
5
6
6
import (
7
+ "errors"
7
8
"fmt"
8
9
"net"
10
+ "time"
9
11
10
12
"github.com/pion/logging"
11
13
"github.com/pion/transport/v2"
12
14
"github.com/pion/transport/v2/stdnet"
15
+ tudp "github.com/pion/transport/v2/udp"
16
+ )
17
+
18
+ var (
19
+ errPortBalanceRequireConnCount = errors .New ("Port balance requires UDPMux implements MuxConnCount interface" )
13
20
)
14
21
15
22
// MultiUDPMuxDefault implements both UDPMux and AllConnsGetter,
@@ -18,21 +25,108 @@ import (
18
25
type MultiUDPMuxDefault struct {
19
26
muxes []UDPMux
20
27
localAddrToMux map [string ]UDPMux
28
+
29
+ enablePortBalance bool
30
+ // Manage port balance for mux that listen on multiple ports for same IP,
31
+ // for each IP, only return one addr (one port) for each GetListenAddresses call to
32
+ // avoid duplicate ip candidates be gathered for a single ice agent.
33
+ multiPortsAddresses []* multiPortsAddress
34
+ }
35
+
36
+ type addrMux struct {
37
+ addr net.Addr
38
+ mux MuxConnCount
39
+ }
40
+
41
+ // each multiPortsAddress represents muxes listen on different ports of a same IP
42
+ type multiPortsAddress struct {
43
+ addresseMuxes []* addrMux
44
+ }
45
+
46
+ func (mpa * multiPortsAddress ) next () net.Addr {
47
+ leastAddr , leastConns := mpa .addresseMuxes [0 ].addr , mpa .addresseMuxes [0 ].mux .ConnCount ()
48
+ for i := 1 ; i < len (mpa .addresseMuxes ); i ++ {
49
+ am := mpa .addresseMuxes [i ]
50
+ if count := am .mux .ConnCount (); count < leastConns {
51
+ leastConns = count
52
+ leastAddr = am .addr
53
+ }
54
+ }
55
+ return leastAddr
56
+ }
57
+
58
+ // MultiUDPMuxOption provide options for NewMultiUDPMuxDefault
59
+ type MultiUDPMuxOption func (* multipleUDPMuxDefaultParams )
60
+
61
+ // MultiUDPMuxOptionWithPortBalance enables load balancing traffic on multiple ports belonging to the same IP
62
+ // When enabled, GetListenAddresses will return the port with the least number of connections for each corresponding IP
63
+ func MultiUDPMuxOptionWithPortBalance () MultiUDPMuxOption {
64
+ return func (params * multipleUDPMuxDefaultParams ) {
65
+ params .portBalance = true
66
+ }
67
+ }
68
+
69
+ type multipleUDPMuxDefaultParams struct {
70
+ portBalance bool
21
71
}
22
72
23
73
// NewMultiUDPMuxDefault creates an instance of MultiUDPMuxDefault that
24
74
// uses the provided UDPMux instances.
25
75
func NewMultiUDPMuxDefault (muxes ... UDPMux ) * MultiUDPMuxDefault {
76
+ mux , err := NewMultiUDPMuxDefaultWithOptions (muxes )
77
+ if err != nil {
78
+ panic (err )
79
+ }
80
+ return mux
81
+ }
82
+
83
+ // NewMultiUDPMuxDefaultWithOptions creates an instance of MultiUDPMuxDefault that
84
+ // uses the provided UDPMux instances and options.
85
+ func NewMultiUDPMuxDefaultWithOptions (muxes []UDPMux , opts ... MultiUDPMuxOption ) (* MultiUDPMuxDefault , error ) {
86
+ var params multipleUDPMuxDefaultParams
87
+ for _ , opt := range opts {
88
+ opt (& params )
89
+ }
90
+
91
+ if params .portBalance {
92
+ for _ , mux := range muxes {
93
+ if _ , ok := mux .(MuxConnCount ); ! ok {
94
+ return nil , errPortBalanceRequireConnCount
95
+ }
96
+ }
97
+ }
98
+
26
99
addrToMux := make (map [string ]UDPMux )
100
+ ipToAddrs := make (map [string ]* multiPortsAddress )
27
101
for _ , mux := range muxes {
28
102
for _ , addr := range mux .GetListenAddresses () {
29
103
addrToMux [addr .String ()] = mux
104
+
105
+ if params .portBalance {
106
+ muxCount , _ := mux .(MuxConnCount )
107
+ udpAddr , _ := addr .(* net.UDPAddr )
108
+ ip := udpAddr .IP .String ()
109
+ if mpa , ok := ipToAddrs [ip ]; ok {
110
+ mpa .addresseMuxes = append (mpa .addresseMuxes , & addrMux {addr , muxCount })
111
+ } else {
112
+ ipToAddrs [ip ] = & multiPortsAddress {
113
+ addresseMuxes : []* addrMux {{addr , muxCount }},
114
+ }
115
+ }
116
+ }
30
117
}
31
118
}
32
- return & MultiUDPMuxDefault {
33
- muxes : muxes ,
34
- localAddrToMux : addrToMux ,
119
+
120
+ multiPortsAddresses := make ([]* multiPortsAddress , 0 , len (ipToAddrs ))
121
+ for _ , mpa := range ipToAddrs {
122
+ multiPortsAddresses = append (multiPortsAddresses , mpa )
35
123
}
124
+ return & MultiUDPMuxDefault {
125
+ muxes : muxes ,
126
+ localAddrToMux : addrToMux ,
127
+ multiPortsAddresses : multiPortsAddresses ,
128
+ enablePortBalance : params .portBalance ,
129
+ }, nil
36
130
}
37
131
38
132
// GetConn returns a PacketConn given the connection's ufrag and network
@@ -64,8 +158,18 @@ func (m *MultiUDPMuxDefault) Close() error {
64
158
return err
65
159
}
66
160
67
- // GetListenAddresses returns the list of addresses that this mux is listening on
161
+ // GetListenAddresses returns the list of addresses that this mux is listening on,
162
+ // if port balance enabled and there are multiple muxes listening to different ports of the same IP addr,
163
+ // it will return the mux that has the least number of connections.
68
164
func (m * MultiUDPMuxDefault ) GetListenAddresses () []net.Addr {
165
+ if m .enablePortBalance {
166
+ addrs := make ([]net.Addr , 0 , len (m .multiPortsAddresses ))
167
+ for _ , mpa := range m .multiPortsAddresses {
168
+ addrs = append (addrs , mpa .next ())
169
+ }
170
+ return addrs
171
+ }
172
+
69
173
addrs := make ([]net.Addr , 0 , len (m .localAddrToMux ))
70
174
for _ , mux := range m .muxes {
71
175
addrs = append (addrs , mux .GetListenAddresses ()... )
@@ -76,6 +180,12 @@ func (m *MultiUDPMuxDefault) GetListenAddresses() []net.Addr {
76
180
// NewMultiUDPMuxFromPort creates an instance of MultiUDPMuxDefault that
77
181
// listen all interfaces on the provided port.
78
182
func NewMultiUDPMuxFromPort (port int , opts ... UDPMuxFromPortOption ) (* MultiUDPMuxDefault , error ) {
183
+ return NewMultiUDPMuxFromPorts ([]int {port }, opts ... )
184
+ }
185
+
186
+ // NewMultiUDPMuxFromPorts creates an instance of MultiUDPMuxDefault that
187
+ // listens to all interfaces and balances traffic on the provided ports.
188
+ func NewMultiUDPMuxFromPorts (ports []int , opts ... UDPMuxFromPortOption ) (* MultiUDPMuxDefault , error ) {
79
189
params := multiUDPMuxFromPortParam {
80
190
networks : []NetworkType {NetworkTypeUDP4 , NetworkTypeUDP6 },
81
191
}
@@ -95,20 +205,29 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu
95
205
return nil , err
96
206
}
97
207
98
- conns := make ([]net.PacketConn , 0 , len (ips ))
208
+ conns := make ([]net.PacketConn , 0 , len (ports ) * len ( ips ))
99
209
for _ , ip := range ips {
100
- conn , listenErr := params .net .ListenUDP ("udp" , & net.UDPAddr {IP : ip , Port : port })
101
- if listenErr != nil {
102
- err = listenErr
103
- break
210
+ for _ , port := range ports {
211
+ conn , listenErr := params .net .ListenUDP ("udp" , & net.UDPAddr {IP : ip , Port : port })
212
+ if listenErr != nil {
213
+ err = listenErr
214
+ break
215
+ }
216
+ if params .readBufferSize > 0 {
217
+ _ = conn .SetReadBuffer (params .readBufferSize )
218
+ }
219
+ if params .writeBufferSize > 0 {
220
+ _ = conn .SetWriteBuffer (params .writeBufferSize )
221
+ }
222
+ if params .batchWriteSize > 0 {
223
+ conns = append (conns , tudp .NewBatchConn (conn , params .batchWriteSize , params .batchWriteInterval ))
224
+ } else {
225
+ conns = append (conns , conn )
226
+ }
104
227
}
105
- if params .readBufferSize > 0 {
106
- _ = conn .SetReadBuffer (params .readBufferSize )
107
- }
108
- if params .writeBufferSize > 0 {
109
- _ = conn .SetWriteBuffer (params .writeBufferSize )
228
+ if err != nil {
229
+ break
110
230
}
111
- conns = append (conns , conn )
112
231
}
113
232
114
233
if err != nil {
@@ -128,7 +247,7 @@ func NewMultiUDPMuxFromPort(port int, opts ...UDPMuxFromPortOption) (*MultiUDPMu
128
247
muxes = append (muxes , mux )
129
248
}
130
249
131
- return NewMultiUDPMuxDefault (muxes ... ), nil
250
+ return NewMultiUDPMuxDefaultWithOptions (muxes , MultiUDPMuxOptionWithPortBalance ())
132
251
}
133
252
134
253
// UDPMuxFromPortOption provide options for NewMultiUDPMuxFromPort
@@ -137,14 +256,16 @@ type UDPMuxFromPortOption interface {
137
256
}
138
257
139
258
type multiUDPMuxFromPortParam struct {
140
- ifFilter func (string ) bool
141
- ipFilter func (ip net.IP ) bool
142
- networks []NetworkType
143
- readBufferSize int
144
- writeBufferSize int
145
- logger logging.LeveledLogger
146
- includeLoopback bool
147
- net transport.Net
259
+ ifFilter func (string ) bool
260
+ ipFilter func (ip net.IP ) bool
261
+ networks []NetworkType
262
+ readBufferSize int
263
+ writeBufferSize int
264
+ logger logging.LeveledLogger
265
+ includeLoopback bool
266
+ net transport.Net
267
+ batchWriteSize int
268
+ batchWriteInterval time.Duration
148
269
}
149
270
150
271
type udpMuxFromPortOption struct {
@@ -226,3 +347,13 @@ func UDPMuxFromPortWithNet(n transport.Net) UDPMuxFromPortOption {
226
347
},
227
348
}
228
349
}
350
+
351
+ // UDPMuxFromPortWithBatchWrite enable batch write for UDPMux
352
+ func UDPMuxFromPortWithBatchWrite (batchWriteSize int , batchWriteInterval time.Duration ) UDPMuxFromPortOption {
353
+ return & udpMuxFromPortOption {
354
+ f : func (p * multiUDPMuxFromPortParam ) {
355
+ p .batchWriteSize = batchWriteSize
356
+ p .batchWriteInterval = batchWriteInterval
357
+ },
358
+ }
359
+ }
0 commit comments