Zpět na domů

TCP server v iOS na POSIX soketech

Článek rozebírá implementaci TCP serveru v iOS aplikaci s použitím POSIX API. Jsou popsány síťové frameworky Apple, vytváření socketů, HTTP parsování a file serving. Je uveden kompletní funkční kód se zpracováním chyb.

TCP server uvnitř iOS: kompletní průvodce sokety
Advertisement 728x90

# 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:

Google AdInline article slot
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).
  • setsockopt s SO_REUSEADDR=1 umožň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):

Google AdInline article slot
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.

Google AdInline article slot
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-fetch nebo 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

Advertisement 728x90

Číst dál