Skip to content

Commit 5683c03

Browse files
authored
Merge pull request #16 from zamderax/update-readme
Add comprehensive README with installation and usage examples
2 parents f63e5bb + 03affbc commit 5683c03

1 file changed

Lines changed: 314 additions & 11 deletions

File tree

README.md

Lines changed: 314 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,320 @@
11
# Socket
2-
Swift async/await based socket library
32

4-
## Introduction
3+
A modern Swift library for working with POSIX sockets using async/await.
54

6-
This library exposes an idiomatic Swift API for interacting with POSIX sockets via an async/await interface. What makes this library unique (even to the point that Swift NIO is still using a [custom socket / thread pool](https://forums.swift.org/t/swift-5-5-supports-concurrency-is-there-any-change-in-swift-nio/50940/2)) is that it was built exclusively using Swift Concurrency and doesn't use old blocking C APIs, CFSocket, DispatchIO, CFRunloop, GCD, or explicitly create a single thread outside of the Swift's global cooperative thread pool to manage the sockets and polling.
5+
## Overview
76

8-
The result is a Socket API that is optimized for async/await and built from the group up. Additionally, like the System, and Concurrency APIs, the Socket is represented as a `struct` instead of a class, greatly reducing ARC overhead. The internal state for the socket is managed by a singleton that stores both its state, and keeps an array of managed file descriptors so polling is global.
7+
Socket is a low-level networking library that provides a Swift-native interface for socket programming. It leverages Swift's async/await concurrency model to offer non-blocking I/O operations without the complexity of callbacks or legacy technologies like CFSocket or GCD.
98

10-
## Goals
9+
### Key Features
1110

12-
- Minimal overhead for Swift Async/Await
13-
- Minimal ARC overhead, keep state outside of `Socket`
14-
- Avoid thread explosion and overcomitting the system
15-
- Use actors to prevent blocking threads
16-
- Optimize polling and C / System API usage
17-
- Low energy usage and memory overhead
11+
-**Pure Swift Concurrency** - Built exclusively with async/await
12+
-**Cross-Platform** - Supports macOS, iOS, tvOS, watchOS, and Linux
13+
-**Multiple Protocols** - TCP, UDP, Unix domain sockets, and raw sockets
14+
-**IPv4 & IPv6** - Full support for both IP versions
15+
-**Type-Safe** - Leverages Swift's type system for socket options and addresses
16+
-**High Performance** - Minimal overhead with value types and efficient polling
17+
-**Event Streams** - Monitor socket events with AsyncStream
18+
19+
## Installation
20+
21+
### Swift Package Manager
22+
23+
Add Socket to your `Package.swift` file:
24+
25+
```swift
26+
// swift-tools-version:5.7
27+
import PackageDescription
28+
29+
let package = Package(
30+
name: "MyApp",
31+
platforms: [
32+
.macOS(.v10_15),
33+
.iOS(.v13),
34+
.tvOS(.v13),
35+
.watchOS(.v6)
36+
],
37+
dependencies: [
38+
.package(url: "https://github.com/PureSwift/Socket.git", from: "0.5.0")
39+
],
40+
targets: [
41+
.target(
42+
name: "MyApp",
43+
dependencies: ["Socket"]
44+
)
45+
]
46+
)
47+
```
48+
49+
Then run:
50+
```bash
51+
swift package resolve
52+
```
53+
54+
### Xcode
55+
56+
1. In Xcode, select **File > Add Package Dependencies...**
57+
2. Enter the repository URL: `https://github.com/PureSwift/Socket`
58+
3. Select the version you want to use
59+
4. Add Socket to your target
60+
61+
## Quick Start
62+
63+
### TCP Client
64+
65+
```swift
66+
import Socket
67+
68+
// Connect to a TCP server
69+
let socket = try await Socket(IPv4Protocol.tcp)
70+
let address = IPv4SocketAddress(address: .init(127, 0, 0, 1), port: 8080)
71+
try await socket.connect(to: address)
72+
73+
// Send data
74+
let message = "Hello, Server!".data(using: .utf8)!
75+
try await socket.write(message)
76+
77+
// Receive response
78+
let response = try await socket.read(1024)
79+
print("Received: \(String(data: response, encoding: .utf8) ?? "")")
80+
81+
// Close the socket
82+
await socket.close()
83+
```
84+
85+
### TCP Server
86+
87+
```swift
88+
import Socket
89+
90+
// Create a server socket
91+
let address = IPv4SocketAddress(address: .any, port: 8080)
92+
let server = try await Socket(IPv4Protocol.tcp, bind: address)
93+
try await server.listen(backlog: 10)
94+
95+
print("Server listening on port 8080")
96+
97+
// Accept connections
98+
while true {
99+
let client = try await server.accept()
100+
101+
// Handle client in a task
102+
Task {
103+
do {
104+
// Read request
105+
let data = try await client.read(1024)
106+
print("Received: \(String(data: data, encoding: .utf8) ?? "")")
107+
108+
// Send response
109+
let response = "Hello, Client!".data(using: .utf8)!
110+
try await client.write(response)
111+
112+
await client.close()
113+
} catch {
114+
print("Client error: \(error)")
115+
}
116+
}
117+
}
118+
```
119+
120+
### UDP Socket
121+
122+
```swift
123+
import Socket
124+
125+
// Create UDP socket
126+
let socket = try await Socket(IPv4Protocol.udp)
127+
let address = IPv4SocketAddress(address: .any, port: 9090)
128+
try await socket.bind(to: address)
129+
130+
// Send datagram
131+
let message = "Hello, UDP!".data(using: .utf8)!
132+
let remoteAddress = IPv4SocketAddress(address: .init(127, 0, 0, 1), port: 9091)
133+
try await socket.sendMessage(message, to: remoteAddress)
134+
135+
// Receive datagram
136+
let (data, sender) = try await socket.receiveMessage(1024, fromAddressOf: IPv4SocketAddress.self)
137+
print("Received from \(sender): \(String(data: data, encoding: .utf8) ?? "")")
138+
```
139+
140+
### Unix Domain Socket
141+
142+
```swift
143+
import Socket
144+
import System
145+
146+
// Server
147+
let path = FilePath("/tmp/my.sock")
148+
let address = UnixSocketAddress(path: path)
149+
let server = try await Socket(UnixProtocol.stream, bind: address)
150+
try await server.listen()
151+
152+
// Client
153+
let client = try await Socket(UnixProtocol.stream)
154+
try await client.connect(to: address)
155+
```
156+
157+
### Using Event Streams
158+
159+
Monitor socket events asynchronously:
160+
161+
```swift
162+
import Socket
163+
164+
let socket = try await Socket(IPv4Protocol.tcp)
165+
166+
// Monitor events
167+
Task {
168+
for await event in socket.event {
169+
switch event {
170+
case .read:
171+
let data = try await socket.read(1024)
172+
print("Read \(data.count) bytes")
173+
case .write:
174+
print("Socket ready for writing")
175+
case .error(let error):
176+
print("Socket error: \(error)")
177+
case .close:
178+
print("Socket closed")
179+
break
180+
default:
181+
break
182+
}
183+
}
184+
}
185+
186+
// Use the socket...
187+
```
188+
189+
### Socket Options
190+
191+
Configure socket behavior with type-safe options:
192+
193+
```swift
194+
import Socket
195+
196+
let socket = try await Socket(IPv4Protocol.tcp)
197+
198+
// Set socket options
199+
try socket.setOption(.reuseAddress, true)
200+
try socket.setOption(.keepAlive, true)
201+
try socket.setOption(.noDelay, true) // Disable Nagle's algorithm
202+
203+
// Get socket option
204+
let keepAlive: Bool = try socket[.keepAlive]
205+
print("Keep-alive enabled: \(keepAlive)")
206+
```
207+
208+
### IPv6 Support
209+
210+
```swift
211+
import Socket
212+
213+
// IPv6 TCP socket
214+
let socket = try await Socket(IPv6Protocol.tcp)
215+
let address = IPv6SocketAddress(address: .loopback, port: 8080)
216+
try await socket.connect(to: address)
217+
218+
// IPv6 address
219+
let linkLocal = IPv6SocketAddress(
220+
address: .init("fe80::1"),
221+
port: 8080
222+
)
223+
```
224+
225+
## Advanced Usage
226+
227+
### Non-blocking Accept with Timeout
228+
229+
```swift
230+
import Socket
231+
232+
let server = try await Socket(IPv4Protocol.tcp, bind: address)
233+
try await server.listen()
234+
235+
// Accept with timeout using tasks
236+
let acceptTask = Task {
237+
try await server.accept()
238+
}
239+
240+
let timeoutTask = Task {
241+
try await Task.sleep(nanoseconds: 5_000_000_000) // 5 seconds
242+
acceptTask.cancel()
243+
}
244+
245+
do {
246+
let client = try await acceptTask.value
247+
timeoutTask.cancel()
248+
// Handle client...
249+
} catch {
250+
print("Accept timed out or failed")
251+
}
252+
```
253+
254+
### Broadcasting with UDP
255+
256+
```swift
257+
import Socket
258+
259+
let socket = try await Socket(IPv4Protocol.udp)
260+
try socket.setOption(.broadcast, true)
261+
262+
// Broadcast to all hosts on the local network
263+
let broadcastAddress = IPv4SocketAddress(
264+
address: .broadcast,
265+
port: 9999
266+
)
267+
try await socket.sendMessage(data, to: broadcastAddress)
268+
```
269+
270+
### Raw Sockets (Requires Privileges)
271+
272+
```swift
273+
import Socket
274+
275+
// Create raw socket (ICMP)
276+
let socket = try await Socket(IPv4Protocol.raw)
277+
278+
// Send ICMP packet
279+
let icmpPacket = createICMPPacket() // Your ICMP packet data
280+
try await socket.write(icmpPacket)
281+
```
282+
283+
## Error Handling
284+
285+
Socket operations throw `SocketError` which conforms to Swift's `Error` protocol:
286+
287+
```swift
288+
import Socket
289+
290+
do {
291+
let socket = try await Socket(IPv4Protocol.tcp)
292+
try await socket.connect(to: address)
293+
} catch let error as SocketError {
294+
switch error.code {
295+
case .connectionRefused:
296+
print("Connection refused by server")
297+
case .networkUnreachable:
298+
print("Network is unreachable")
299+
case .timeout:
300+
print("Operation timed out")
301+
default:
302+
print("Socket error: \(error)")
303+
}
304+
} catch {
305+
print("Unexpected error: \(error)")
306+
}
307+
```
308+
309+
## Requirements
310+
311+
- Swift 5.7+
312+
- macOS 10.15+, iOS 13+, tvOS 13+, watchOS 6+, or Linux
313+
314+
## Contributing
315+
316+
Contributions are welcome! Please feel free to submit a Pull Request.
317+
318+
## License
319+
320+
This project is licensed under the MIT License - see the LICENSE file for details.

0 commit comments

Comments
 (0)