Skip to content

Commit d5a4573

Browse files
committed
OSCTCPServer: init: port parameter now allows nil or 0 to assign a random available port
1 parent 2824048 commit d5a4573

File tree

3 files changed

+24
-18
lines changed

3 files changed

+24
-18
lines changed

Sources/OSCKit/TCP/Server/OSCTCPServer.swift

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ public final class OSCTCPServer {
3737
public var timeTagMode: OSCTimeTagMode
3838

3939
/// Local network port.
40-
public private(set) var localPort: UInt16
40+
public var localPort: UInt16 {
41+
tcpSocket.localPort
42+
}
43+
private var _localPort: UInt16?
4144

4245
/// Network interface to restrict connections to.
4346
public let interface: String?
@@ -51,21 +54,22 @@ public final class OSCTCPServer {
5154
///
5255
/// - Parameters:
5356
/// - port: Local network port to listen for inbound connections.
57+
/// If `nil` or `0`, a random available port in the system will be chosen.
5458
/// - interface: Optionally specify a network interface for which to constrain connections.
5559
/// - timeTagMode: OSC TimeTag mode. Default is recommended.
5660
/// - framingMode: TCP framing mode. Both server and client must use the same framing mode. (Default is recommended.)
5761
/// - queue: Optionally supply a custom dispatch queue for receiving OSC packets and dispatching the
5862
/// handler callback closure. If `nil`, a dedicated internal background queue will be used.
5963
/// - receiveHandler: Handler to call when OSC bundles or messages are received.
6064
public init(
61-
port: UInt16,
65+
port: UInt16?,
6266
interface: String? = nil,
6367
timeTagMode: OSCTimeTagMode = .ignore,
6468
framingMode: OSCTCPFramingMode = .osc1_1,
6569
queue: DispatchQueue? = nil,
6670
receiveHandler: OSCHandlerBlock? = nil
6771
) {
68-
self.localPort = port
72+
_localPort = (port == nil || port == 0) ? nil : port
6973
self.interface = interface
7074
self.timeTagMode = timeTagMode
7175
self.framingMode = framingMode
@@ -90,10 +94,10 @@ extension OSCTCPServer: @unchecked Sendable { } // TODO: unchecked
9094
extension OSCTCPServer {
9195
/// Starts listening for inbound connections.
9296
public func start() throws {
93-
try tcpSocket.accept(onInterface: interface, port: localPort)
94-
95-
// update local port in case port 0 was passed and the system assigned a new port
96-
localPort = tcpSocket.localPort
97+
try tcpSocket.accept(
98+
onInterface: interface,
99+
port: _localPort ?? 0 // 0 causes system to assign random open port
100+
)
97101
}
98102

99103
/// Closes any open client connections and stops listening for inbound connection requests.

Tests/OSCKitTests/UDP/Server/OSCUDPServer Tests.swift

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -102,12 +102,12 @@ struct OSCUDPServer_Tests {
102102
}
103103
}
104104

105-
let possibleValuePacks: [OSCValues] = [
105+
var possibleValuePacks: [OSCValues] { [
106106
[],
107107
[UUID().uuidString],
108108
[Int.random(in: 10_000 ... 10_000_000)],
109109
[Int.random(in: 10_000 ... 10_000_000), UUID().uuidString, 456.78, true]
110-
]
110+
] }
111111

112112
let sourceMessages: [OSCMessage] = Array(1 ... 1000).map { value in
113113
OSCMessage("/some/address/\(UUID().uuidString)", values: possibleValuePacks.randomElement()!)
@@ -120,7 +120,7 @@ struct OSCUDPServer_Tests {
120120
}
121121
}
122122

123-
try await wait(require: { await receiver.messages.count == 1000 }, timeout: 5.0)
123+
try await wait(require: { await receiver.messages.count == 1000 }, timeout: 10.0)
124124

125125
await #expect(receiver.messages == sourceMessages)
126126
}
@@ -130,13 +130,15 @@ struct OSCUDPServer_Tests {
130130
func stressTestOnline() async throws {
131131
let isFlakey = !isSystemTimingStable()
132132

133-
let server = OSCUDPServer(port: 8888, timeTagMode: .ignore, queue: nil, receiveHandler: nil)
133+
// selects a random available port
134+
let server = OSCUDPServer(port: nil, timeTagMode: .ignore, queue: nil, receiveHandler: nil)
134135
try await Task.sleep(seconds: isFlakey ? 5.0 : 0.1)
135136

136137
try server.start()
137138
try await Task.sleep(seconds: isFlakey ? 5.0 : 0.5)
138139

139-
print("Using server listen port \(server.localPort)")
140+
let port = server.localPort
141+
print("Using server listen port \(port)")
140142

141143
final actor Receiver {
142144
var messages: [OSCMessage] = []
@@ -153,12 +155,12 @@ struct OSCUDPServer_Tests {
153155
}
154156
}
155157

156-
let possibleValuePacks: [OSCValues] = [
158+
var possibleValuePacks: [OSCValues] { [
157159
[],
158160
[UUID().uuidString],
159161
[Int.random(in: 10_000 ... 10_000_000)],
160162
[Int.random(in: 10_000 ... 10_000_000), UUID().uuidString, 456.78, true]
161-
]
163+
] }
162164

163165
let sourceMessages: [OSCMessage] = Array(1 ... 1000).map { value in
164166
OSCMessage("/some/address/\(UUID().uuidString)", values: possibleValuePacks.randomElement()!)
@@ -171,12 +173,12 @@ struct OSCUDPServer_Tests {
171173
let srcLocSendToServer: SourceLocation = #_sourceLocation
172174
DispatchQueue.global().async {
173175
for message in sourceMessages {
174-
do { try client.send(message, to: "localhost", port: 8888) }
176+
do { try client.send(message, to: "localhost", port: port) }
175177
catch { Issue.record(error, sourceLocation: srcLocSendToServer) }
176178
}
177179
}
178180

179-
await wait(expect: { await receiver.messages.count == 1000 }, timeout: isFlakey ? 20.0 : 5.0)
181+
await wait(expect: { await receiver.messages.count == 1000 }, timeout: isFlakey ? 20.0 : 10.0)
180182
try await #require(receiver.messages.count == 1000)
181183

182184
await #expect(receiver.messages == sourceMessages)

Tests/OSCKitTests/UDP/Socket/OSCUDPSocket Tests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,9 +140,9 @@ struct OSCUDPSocket_Tests {
140140
let isFlakey = !isSystemTimingStable()
141141

142142
let socket = OSCUDPSocket(
143-
localPort: nil,
143+
localPort: nil, // selects a random available port
144144
remoteHost: "localhost",
145-
remotePort: nil,
145+
remotePort: nil, // gets set to same port as localPort
146146
timeTagMode: .ignore,
147147
isIPv4BroadcastEnabled: false,
148148
queue: nil,

0 commit comments

Comments
 (0)