# TCP-Server in einer iOS-App mit POSIX-Sockets: Von der Theorie zur Praxis
In der iOS-Entwicklung deckt der Netzwerkstapel alles ab, von hochstufigen HTTP-Anfragen bis zu niedrigstufigen BSD-Sockets. URLSession eignet sich ideal für standardmäßige HTTP- und WebSocket-Operationen, der Network framework behandelt TCP/UDP mit TLS, und CFNetwork unterstützt Legacy-Protokolle wie FTP und Bonjour. Die POSIX API bietet direkten Zugriff auf die Transportschicht, während SwiftNIO serverseitige Funktionen in Swift hinzufügt.
Alle diese Tools basieren auf Sockets. Für benutzerdefinierte Protokolle (wie SMTP oder IMAP) oder Serverlogik benötigen Sie Zugriff auf die Transportschicht. Dieser Artikel führt Sie Schritt für Schritt durch die Implementierung eines TCP-Servers mit der POSIX API – minimale Abhängigkeiten und volle Kontrolle.
POSIX-Sockets: Erstellung und Einrichtung
Die TCPServer-Klasse kapselt den Socket, den Port und den Lebenszyklus:
final class TCPServer {
let port: UInt16
init(port: UInt16 = 8081) {
self.port = port
}
deinit {
stop()
}
func start() throws { }
func stop() { }
}
Fehler sind stark typisiert:
enum SocketError: LocalizedError {
case socketCreationFailed(errno: Int32)
case bindFailed(errno: Int32)
case listenFailed(errno: Int32)
case writeFailed(errno: Int32)
case writeTimeout
var errorDescription: String? { /* ... */ }
}
In start():
socket(AF_INET, SOCK_STREAM, 0)erstellt einen IPv4-TCP-Socket. Es gibt einen Deskriptor zurück oder -1 bei Fehler (errno prüfen).
setsockoptmitSO_REUSEADDR=1erlaubt das Neubinden der Adresse ohne TIME_WAIT-Verzögerungen.
var reuseAddr: Int32 = 1
setsockopt(currentSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, socklen_t(MemoryLayout<Int32>.size))
Binden, Lauschen und Behandeln von Verbindungen
Die sockaddr_in-Adressstruktur wird für INADDR_ANY (0.0.0.0) befüllt:
var addr = sockaddr_in()
addr.sin_family = sa_family_t(AF_INET)
addr.sin_port = in_port_t(port.bigEndian)
addr.sin_addr.s_addr = in_addr_t(0)
bind(currentSocket, sockaddr_cast(&addr), socklen_t(MemoryLayout<sockaddr_in>.size))
listen(currentSocket, 10) stellt bis zu 10 ausstehende Verbindungen in die Warteschlange.
Die Behandlungsschleife läuft in einem Hintergrund-Thread:
DispatchQueue.global().async { [weak self] in
while let server = self, server.isRunning {
var clientAddr = sockaddr_in()
var len = socklen_t(MemoryLayout<sockaddr_in>.size)
let clientSocket = accept(server.currentSocket, sockaddr_cast(&clientAddr), &len)
// Handle clientSocket
}
}
HTTP-Parsing und Dateiservieren
Der Server implementiert grundlegendes HTTP/1.1. Der Parser extrahiert die Methode, den Pfad und die Header aus der rohen Anfrage.
struct HTTPRequest {
let method: String
let path: String
let headers: [String: String]
let body: Data?
}
Für GET /files/ gibt es eine Liste von Dateien aus dem Documents-Verzeichnis zurück:
<html><body><h1>App Files</h1><ul>
<li><a href="/file/filename">filename</a></li>
</ul></body></html>
Downloads über /file/: verwendet sendfile für Effizienz.
Die Antwort wird manuell aufgebaut:
"HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/html\r\n"
+ "Content-Length: \(data.count)\r\n"
+ "\r\n" + String(data: data, encoding: .utf8)!
Sicherheit und Einschränkungen
- Lokales Netzwerk: Der Server bindet an 0.0.0.0:8081, erreichbar über die IP des Geräts.
- Sandbox: Zugriff beschränkt auf Documents. Keine Systemdateien.
- Timeout: 30 Sekunden pro Anfrage; bei Fehlern schließen.
- iOS-Einschränkungen: Hintergrund-Netzwerkverbindungen erfordern
background-fetchoder VoIP-Berechtigungen.
Wichtige Erkenntnisse:
- POSIX API ist eine universelle Low-Level-Schnittstelle, identisch zu macOS/Linux.
- SO_REUSEADDR ist essenziell für schnelle Entwicklungs-Neustarts.
- Führen Sie
accept()in einer asynchronen Schleife aus, ohne den Hauptthread zu blockieren. - HTTP-Parsing ist minimalistisch: Methode + Pfad + Content-Length.
sendfile()übertrifftwrite()bei großen Dateien.
Testen und Debuggen
- Starten:
try server.start() - Lokaler Zugriff:
http://localhost:8081/files - Netzwerkzugriff:
http://192.168.1.100:8081/files - Logs:
print(String(cString: strerror(errno))) - Instruments: Verwenden Sie Network Link Conditioner für Simulationen.
Das Projekt demonstriert den vollen Zyklus: vom Socket bis zur Weboberfläche. Der Code ist erweiterbar für WebSocket oder benutzerdefinierte Protokolle.
— Editorial Team
Noch keine Kommentare.