Skip to content

Commit 560ccc6

Browse files
authored
Merge pull request #57 from Borealis-Solutions/udpserver-port-reuse
Add OSCUDPServer Port Reuse
2 parents 3855c3f + 5d3eb19 commit 560ccc6

File tree

2 files changed

+19
-0
lines changed

2 files changed

+19
-0
lines changed

Sources/OSCKit/UDP/Server/OSCUDPServer.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,19 @@ public final class OSCUDPServer {
3434
/// Network interface to restrict connections to.
3535
public private(set) var interface: String?
3636

37+
/// Enable local UDP port reuse by other processes.
38+
/// This property must be set prior to calling ``start()`` in order to take effect.
39+
///
40+
/// By default, only one socket can be bound to a given IP address & port combination at a time. To enable
41+
/// multiple processes to simultaneously bind to the same address & port, you need to enable
42+
/// this functionality in the socket. All processes that wish to use the address & port
43+
/// simultaneously must all enable reuse port on the socket bound to that port.
44+
///
45+
/// Due to limitations of `SO_REUSEPORT` on Apple platforms, enabling this only permits receipt of broadcast
46+
/// or multicast messages for any additional sockets which bind to the same address and port. Unicast
47+
/// messages are only received by the first socket to bind.
48+
public var isPortReuseEnabled: Bool = false
49+
3750
/// Returns a boolean indicating whether the OSC server has been started.
3851
public private(set) var isStarted: Bool = false
3952

@@ -50,19 +63,22 @@ public final class OSCUDPServer {
5063
/// - port: Local port to listen on for inbound OSC packets.
5164
/// If `nil` or `0`, a random available port in the system will be chosen.
5265
/// - interface: Optionally specify a network interface for which to constrain communication.
66+
/// - isPortReuseEnabled: Enable local UDP port reuse by other processes to receive broadcast packets.
5367
/// - timeTagMode: OSC TimeTag mode. (Default is recommended.)
5468
/// - queue: Optionally supply a custom dispatch queue for receiving OSC packets and dispatching the
5569
/// handler callback closure. If `nil`, a dedicated internal background queue will be used.
5670
/// - receiveHandler: Handler to call when OSC bundles or messages are received.
5771
public init(
5872
port: UInt16? = 8000,
5973
interface: String? = nil,
74+
isPortReuseEnabled: Bool = false,
6075
timeTagMode: OSCTimeTagMode = .ignore,
6176
queue: DispatchQueue? = nil,
6277
receiveHandler: OSCHandlerBlock? = nil
6378
) {
6479
_localPort = (port == nil || port == 0) ? nil : port
6580
self.interface = interface
81+
self.isPortReuseEnabled = isPortReuseEnabled
6682
self.timeTagMode = timeTagMode
6783
let queue = queue ?? DispatchQueue(label: "com.orchetect.OSCKit.OSCUDPServer.queue")
6884
self.queue = queue
@@ -84,6 +100,7 @@ extension OSCUDPServer {
84100

85101
stop()
86102

103+
try udpSocket.enableReusePort(isPortReuseEnabled)
87104
try udpSocket.bind(
88105
toPort: _localPort ?? 0, // 0 causes system to assign random open port
89106
interface: interface

Tests/OSCKitTests/UDP/Object Access Tests.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import Testing
3333
_ = oscServer.isStarted
3434
_ = oscServer.localPort
3535
// oscServer.localPort = 9000 // immutable actor
36+
oscServer.isPortReuseEnabled = true
37+
oscServer.isPortReuseEnabled = false
3638
oscServer.setReceiveHandler { message, timeTag, host, port in
3739
print(message)
3840
}

0 commit comments

Comments
 (0)