# TCP server v iOS aplikaci na POSIX socketech: od teorie k praxi
V iOS vývoji síťový stack pokrývá úrovně od vysoceúrovňových HTTP požadavků po nízkourovňové BSD sockety. URLSession je vhodný pro standardní HTTP/WebSocket operace, Network framework – pro TCP/UDP s TLS, CFNetwork implementuje legacy protokoly jako FTP a Bonjour. POSIX API poskytuje přímý přístup k transportní úrovni, SwiftNIO přidává serverové možnosti ve Swiftu.
Všechny nástroje se opírají o sockety. Pro vlastní protokoly (SMTP, IMAP) nebo serverovou logiku je potřebná transportní úroveň. V článku implementujeme TCP server přes POSIX API – minimální závislosti, plná kontrola.
POSIX sockety: vytvoření a nastavení
Třída TCPServer zapouzdřuje socket, port a lifecycle:
final class TCPServer {
let port: UInt16
init(port: UInt16 = 8081) {
self.port = port
}
deinit {
stop()
}
func start() throws { }
func stop() { }
}
Chyby jsou typizovány:
enum SocketError: LocalizedError {
case socketCreationFailed(errno: Int32)
case bindFailed(errno: Int32)
case listenFailed(errno: Int32)
case writeFailed(errno: Int32)
case writeTimeout
var errorDescription: String? { /* ... */ }
}
V start():
socket(AF_INET, SOCK_STREAM, 0)vytváří IPv4 TCP socket. Vrátí deskriptor nebo -1 při chybě (errno).
setsockoptsSO_REUSEADDR=1umožňuje opětovné bindování adresy bez zpoždění TIME_WAIT.
var reuseAddr: Int32 = 1
setsockopt(currentSocket, SOL_SOCKET, SO_REUSEADDR, &reuseAddr, socklen_t(MemoryLayout<Int32>.size))
Bindování, naslouchání a zpracování spojení
Struktura adresy sockaddr_in se vyplňuje pro INADDR_ANY (0.0.0.0):
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) čeká na až 10 spojení ve frontě.
Cyklus zpracování v samostatném vlákně:
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)
// Zpracování clientSocket
}
}
HTTP parsování a file-serving
Server implementuje základní HTTP/1.1. Parser extrahuje metodu, cestu, hlavičky z raw requestu.
struct HTTPRequest {
let method: String
let path: String
let headers: [String: String]
let body: Data?
}
Pro GET /files/ vrací seznam souborů z Documents složky:
<html><body><h1>App Files</h1><ul>
<li><a href="/file/filename">filename</a></li>
</ul></body></html>
Stahování přes /file/: sendfile pro efektivitu.
Response se vytváří ručně:
"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)!
Bezpečnost a omezení
- Lokální síť: server se binduje na 0.0.0.0:8081, přístupný přes IP zařízení.
- Sandbox: přístup pouze k Documents. Žádné systémové soubory.
- Timeout: 30 s na požadavek, close při chybách.
- iOS omezení: background networking vyžaduje
background-fetchnebo VoIP entitlements.
Co je důležité:
- POSIX API – univerzální nízká úroveň, identická s macOS/Linux.
- SO_REUSEADDR je nutný pro dev-restarty.
- accept() v async cyklu, bez blokování main threadu.
- HTTP parsování minimalistické: metoda+cesta+Content-Length.
- sendfile() > write() v produktech na velkých souborech.
Testování a debugging
- Spuštění:
try server.start() - Lokální přístup:
http://localhost:8081/files - Síť:
http://192.168.1.100:8081/files - Logy:
print(String(cString: strerror(errno))) - Instruments: Network Link Conditioner pro simulaci.
Projekt demonstruje celý cyklus: od socketu po webové rozhraní. Kód lze rozšířit pro WebSocket nebo vlastní protokoly.
— Editorial Team
Zatím žádné komentáře.