Skip to content

Commit 051731d

Browse files
lorenteymilseman
authored andcommitted
Socket updates (#4)
* Update getsockopt/setsockopt * Add getsockopt/setsockopt docs * Doc updates * Add overloads for bind/connect taking concrete address types * ShutdownKind: Remove Codable conformance. * Listen: close the client connection socket before exiting This is supposed to demonstrate acceptable use, we can’t leave resource cleanup to exit() * Make IPv4.Address and IPv6.Address expressible by string literals * Document SocketAddress better.
1 parent 4afc2de commit 051731d

File tree

6 files changed

+127
-11
lines changed

6 files changed

+127
-11
lines changed

Sources/Samples/Listen.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,14 @@ struct Listen: ParsableCommand {
106106
} else {
107107
let conn = try socket.accept(client: &client)
108108
complain("Connection from \(client.niceDescription)")
109-
while true {
110-
let (count, flags) =
109+
try conn.closeAfter {
110+
while true {
111+
let (count, flags) =
111112
try conn.receive(into: buffer, sender: &client, ancillary: &ancillary)
112-
guard count > 0 else { break }
113-
print(prefix(client: client, flags: flags), terminator: "")
114-
try FileDescriptor.standardOutput.writeAll(buffer[..<count])
113+
guard count > 0 else { break }
114+
print(prefix(client: client, flags: flags), terminator: "")
115+
try FileDescriptor.standardOutput.writeAll(buffer[..<count])
116+
}
115117
}
116118
}
117119
}

Sources/System/Sockets/SocketAddress+IPv4.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ extension SocketAddress {
4747
return IPv4(rawValue: value)
4848
}
4949

50-
/// Construct a `SocketAddress` holding an IPv4 address and port
50+
/// Construct a `SocketAddress` holding an IPv4 address and port number.
5151
@_alwaysEmitIntoClient
5252
public init(ipv4 address: IPv4.Address, port: Port) {
5353
self.init(IPv4(address: address, port: port))
@@ -194,3 +194,13 @@ extension SocketAddress.IPv4.Address: LosslessStringConvertible {
194194
}
195195
}
196196
}
197+
198+
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
199+
extension SocketAddress.IPv4.Address: ExpressibleByStringLiteral {
200+
public init(stringLiteral value: String) {
201+
guard let address = Self(value) else {
202+
preconditionFailure("'\(value)' is not a valid IPv4 address string")
203+
}
204+
self = address
205+
}
206+
}

Sources/System/Sockets/SocketAddress+IPv6.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,3 +238,13 @@ extension SocketAddress.IPv6.Address: LosslessStringConvertible {
238238
}
239239
}
240240
}
241+
242+
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
243+
extension SocketAddress.IPv6.Address: ExpressibleByStringLiteral {
244+
public init(stringLiteral value: String) {
245+
guard let address = Self(value) else {
246+
preconditionFailure("'\(value)' is not a valid IPv6 address string")
247+
}
248+
self = address
249+
}
250+
}

Sources/System/Sockets/SocketAddress.swift

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,57 @@
88
*/
99

1010
// @available(macOS 9999, iOS 9999, watchOS 9999, tvOS 9999, *)
11-
/// An opaque type holding a socket address and port number in some address family.
11+
/// An opaque type representing a socket address in some address family,
12+
/// such as an IP address along with a port number.
1213
///
13-
/// TODO: Show examples of creating an ipv4 and ipv6 address
14+
/// `SocketAddress` values can be passed directly to `SocketDescriptor.connect`
15+
/// or `.bind` to establish a network connection.
1416
///
15-
/// The corresponding C type is `sockaddr_t`
17+
/// We can use the `SocketAddress.resolveName` method resolve a pair of
18+
/// host/service name strings to a list of socket addresses:
19+
///
20+
/// let results =
21+
/// try SocketAddress.resolveName(hostname: "swift.org", service: "https")
22+
/// for result in results {
23+
/// let try socket =
24+
/// SocketDescriptor.open(result.domain, result.type, result.protocol)
25+
/// do {
26+
/// try socket.connect(to: result.address)
27+
/// } catch {
28+
/// try? socket.close()
29+
/// throw error
30+
/// }
31+
/// return socket
32+
/// }
33+
///
34+
/// To create an IPv4, IPv6 or Local domain address, we can use convenience
35+
/// initializers that take the corresponding information:
36+
///
37+
/// let ipv4 = SocketAddress(ipv4: "127.0.0.1", port: 8080)!
38+
/// let ipv6 = SocketAddress(ipv6: "::1", port: 80)!
39+
/// let local = SocketAddress(local: "/var/run/example.sock")
40+
///
41+
/// (Note that you may prefer to use the concrete address types
42+
/// `SocketAddress.IPv4`, `SocketAddress.IPv6` and `SocketAddress.Local`
43+
/// instead -- they provide easy access to the address parameters.)
44+
///
45+
/// `SocketAddress` also provides ways to access its underlying contents
46+
/// as a raw unsafe memory buffer. This is useful for dealing with address
47+
/// families that `System` doesn't model, or for passing the socket address
48+
/// to C functions that expect a pointer to a `sockaddr` value.
49+
///
50+
/// `SocketAddress` stores its contents in a managed storage buffer, and
51+
/// it can serve as a reusable receptacle for addresses that are returned
52+
/// by system calls. You can use the `init(minimumCapacity:)` initializer
53+
/// to create an empty socket address with the specified storage capacity,
54+
/// then you can pass it to functions like `.accept(client:)` to retrieve
55+
/// addresses without repeatedly allocating memory.
56+
///
57+
/// `SocketAddress` is able to hold any IPv4 or IPv6 address without allocating
58+
/// any memory. For other address families, it may need to heap allocate a
59+
/// storage buffer, depending on the size of the stored value.
60+
///
61+
/// The corresponding C type is `sockaddr_t`.
1662
public struct SocketAddress {
1763
internal var _variant: _Variant
1864

Sources/System/Sockets/SocketDescriptor.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ extension SocketDescriptor {
330330

331331
/// Specify the part (or all) of a full-duplex connection to shutdown.
332332
@frozen
333-
public struct ShutdownKind: RawRepresentable, Hashable, Codable, CustomStringConvertible {
333+
public struct ShutdownKind: RawRepresentable, Hashable, CustomStringConvertible {
334334
@_alwaysEmitIntoClient
335335
public var rawValue: CInt
336336

Sources/System/Sockets/SocketOperations.swift

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,38 @@ extension SocketDescriptor {
4848
}.map(SocketDescriptor.init(rawValue:))
4949
}
5050

51-
/// Bind a name to a socket.
51+
/// Bind a socket to an address.
5252
///
5353
/// The corresponding C function is `bind`.
5454
@_alwaysEmitIntoClient
5555
public func bind(to address: SocketAddress) throws {
5656
try _bind(to: address).get()
5757
}
5858

59+
/// Bind a socket to an IPv4 address.
60+
///
61+
/// The corresponding C function is `bind`.
62+
@_alwaysEmitIntoClient
63+
public func bind(to address: SocketAddress.IPv4) throws {
64+
try _bind(to: SocketAddress(address)).get()
65+
}
66+
67+
/// Bind a socket to an IPv6 address.
68+
///
69+
/// The corresponding C function is `bind`.
70+
@_alwaysEmitIntoClient
71+
public func bind(to address: SocketAddress.IPv6) throws {
72+
try _bind(to: SocketAddress(address)).get()
73+
}
74+
75+
/// Bind a socket to an address in the local domain.
76+
///
77+
/// The corresponding C function is `bind`.
78+
@_alwaysEmitIntoClient
79+
public func bind(to address: SocketAddress.Local) throws {
80+
try _bind(to: SocketAddress(address)).get()
81+
}
82+
5983
@usableFromInline
6084
internal func _bind(to address: SocketAddress) -> Result<(), Errno> {
6185
let success = address.withUnsafeCInterop { addr, len in
@@ -139,6 +163,30 @@ extension SocketDescriptor {
139163
try _connect(to: address).get()
140164
}
141165

166+
/// Initiate a connection to an IPv4 address.
167+
///
168+
/// The corresponding C function is `connect`.
169+
@_alwaysEmitIntoClient
170+
public func connect(to address: SocketAddress.IPv4) throws {
171+
try _connect(to: SocketAddress(address)).get()
172+
}
173+
174+
/// Initiate a connection to an IPv6 address.
175+
///
176+
/// The corresponding C function is `connect`.
177+
@_alwaysEmitIntoClient
178+
public func connect(to address: SocketAddress.IPv6) throws {
179+
try _connect(to: SocketAddress(address)).get()
180+
}
181+
182+
/// Initiate a connection to an address in the local domain.
183+
///
184+
/// The corresponding C function is `connect`.
185+
@_alwaysEmitIntoClient
186+
public func connect(to address: SocketAddress.Local) throws {
187+
try _connect(to: SocketAddress(address)).get()
188+
}
189+
142190
@usableFromInline
143191
internal func _connect(to address: SocketAddress) -> Result<(), Errno> {
144192
let success = address.withUnsafeCInterop { addr, len in

0 commit comments

Comments
 (0)