We need to talk about Websockets

We need to talk about Websockets

If you're tired of slow and flaky networking, this talk will explain to you the state-of-the-art solution: Websockets. Using technologies such as Apple's freshly announced URLSession Websockets, incredible new open-source SwiftNIO, and Network.framework, we'll look at how you can open a two-way socket between your app and server to accelerate your networking, allowing you to communicate without disruptions, do so securely, and even learn some tips and tricks from Apple. The speaker is the maintainer of Starscream, one of the most popular Swift Websocket libraries.

6c9d1e82615877b0b2866f82cbf5f557?s=128

Kristaps Grinbergs

May 19, 2020
Tweet

Transcript

  1. Kristaps Grinbergs We need to talk about WebSockets fassko

  2. About me Swift developer Open source Swift Weekly Brief

  3. Mr. Byte

  4. Plan What are WebSockets How to use with  platforms

    Demo Pros and Cons
  5. None
  6. Using WebSockets Chat apps ⏳ Real time notifications Stock market

    Multiplayer games ⚽ Sport scores
  7. History

  8. Long Polling

  9. “Innovation is taking two things that already exist and putting

    them together in a new way.” - Tom Freston
  10. History 2008 Michael Carter and Ian Hickson 2010 RFC 6455

    2013 iOS and Android
  11. - Wikipedia WebSocket is a computer communications protocol, providing full-duplex

    communication channels over a single TCP connection.
  12. What are WebSockets? ✌ Two-way, single connection Persistent Low latency

  13. None
  14. RFC 6455 standard https://tools.ietf.org/html/rfc6455

  15. How does it work?

  16. Connection handshake GET Request GET /chat HTTP/1.1 Host: example.com Upgrade:

    websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== Sec-WebSocket-Version: 13 Sec-WebSocket-Protocol: chat, superchat Origin: http://example.com Response HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo= Sec-WebSocket-Prototocol: chat
  17. None
  18. None
  19. Frames

  20. Control byte

  21. OpCode

  22. Mask

  23. Payload length

  24. Masking key

  25. Payload data

  26. PING PONG

  27. None
  28. None
  29. Before iOS 12 CFNetwork Third party dependancies

  30. None
  31. Starscream Written in Swift WebSocket (RFC 6455) Open source on

    Github
  32. Starscream initialize let url = URL(string: "http://localhost:8080")! var request =

    URLRequest(url: url) socket = WebSocket(request: request) socket.delegate = self socket.connect()
  33. Starscream receive func didReceive(event: WebSocketEvent, client: WebSocket) { } switch

    event { case .connected(let headers): isConnected = true print("websocket is connected: \(headers)") case .disconnected(let reason, let code): isConnected = false print("websocket is disconnected: \(reason) with code: \(code)") case .text(let string): print("Received text: \(string)") case .binary(let data): print("Received data: \(data.count)") case .ping(_): break case .pong(_): break case .cancelled: isConnected = false case .error(let error): isConnected = false handleError(error) }
  34. Starscream send socket.write(string: "Hello") { print("Message sent") }

  35. Starscream ping/pong socket.respondToPingWithPong = true socket.write(ping: "PING")

  36. Starscream close socket.disconnect(closeCode: CloseCode.goingAway.rawValue)

  37. Network.framework iOS 13.0+  Lower level

  38. Network.framework initialize let connection = NWConnection(host: .init("echo.websocket.org"), port: .https, using:

    .tls) let queue = DispatchQueue(label: "com.network.framework") connection.start(queue: queue)
  39. Network.framework send let message = "Hello".data(using: .utf8)! connection.send(content: message, completion:

    .contentProcessed { error in if let error = error { print("can't process message \(error)") } })
  40. Network.framework receive connection.receiveMessage { data, messageContext, isComplete, error in if

    let error = error { print("Failed to receive message \(error)") } else if let data = data, isComplete { print(String(data: data, encoding: .utf8)!) } } connection.receive(minimumIncompleteLength: 2, maximumLength: 4096) { data, context, isComplete, error in if let error = error { print("error", error) } else if let data = data { print("received: ", String(data: data, encoding: .utf8)!) } if isComplete, context == nil, error == nil { print("END") } }
  41. Network.framework close connection.cancel()

  42. URLSessionWebSocketTask iOS 13.0+  URLSession

  43. URLSessionWebSocketTask Initialize let session = URLSession(configuration: .default) let url =

    URL(string: "wss://echo.websocket.org")! let webSocketTask = session.webSocketTask(with: url) webSocketTask.resume()
  44. URLSessionWebSocketTask Send webSocketTask.send(.string("Hello")) { error in if let error =

    error { print("Can't send message \(error)") } }
  45. URLSessionWebSocketTask Receive webSocketTask.receive { result in switch result { case

    .success(let message): switch message { case .data(let data): print("Data received: \(data)") case .string(let text): print("Text received: \(text)") } case .failure(let error): print("Error in receiving \(error)") } }
  46. URLSessionWebSocketTask PING/PONG webSocketTask.sendPing { error in if let error =

    error { print("PONG not received \(error)") } }
  47. URLSessionWebSocketTask Close webSocketTask.cancel(with: .goingAway, reason: nil)

  48. None
  49. SwiftNIO  Cross-platform ✨ Async event driven Network framework

  50. NIOWebSocket ➿ Based on event loops Low level Full control

  51. NIO Transport Services SwiftNIO for  platforms Based on Network.framework

    Low level
  52. On top of SwiftNIO Written in Swift Modular

  53. Client & Server On SwiftNIO Medium level

  54. Client _ = try promise.futureResult.wait() var eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2)

    let port: Int = 8709 let promise = eventLoopGroup.next().makePromise(of: String.self) WebSocket.connect(to: "ws://localhost:\(port)", on: eventLoopGroup) { ws in ws.send("hello") ws.onText { ws, string in print(string) } }.cascadeFailure(to: promise)
  55. Server let upgradePipelineHandler: (Channel, HTTPRequestHead) -> EventLoopFuture<Void> = { channel,

    req in WebSocket.server(on: channel) { ws in } } ws.onText { ws, string in print("received") ws.send(string.trimmingCharacters(in: .whitespacesAndNewlines).reversed()) } ws.onBinary { ws, buffer in print(buffer) } ws.onClose.whenSuccess { value in print("onClose") }
  56. var eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 2) let port = Int.random(in: 8000..<9000)

    let promise = eventLoopGroup.next().makePromise(of: String.self) let server = try ServerBootstrap(group: eventLoopGroup).childChannelInitializer { channel in let webSocket = NIOWebSocketServerUpgrader( shouldUpgrade: { channel, req in return channel.eventLoop.makeSucceededFuture([:]) }, upgradePipelineHandler: upgradePipelineHandler ) return channel.pipeline.configureHTTPServerPipeline( withServerUpgrade: ( upgraders: [webSocket], completionHandler: { ctx in // complete }) ) }.bind(host: "localhost", port: port).wait() _ = try promise.futureResult.wait() try server.close(mode: .all).wait() Server
  57. Vapor WebSockets client let websocket = WebSocket.connect(to: url, on: app.eventLoopGroup)

    { ws in ws.onText { ws, text in print(text) } ws.send("Hello") }
  58. func routes(_ app: Application) throws { app.webSocket("") { request, ws

    in } } Vapor WebSockets server ws.send("You have been connected to WebSockets") ws.onText { ws, string in ws.send(string.trimmingCharacters(in: .whitespacesAndNewlines).reversed()) } ws.onClose.whenComplete { result in switch result { case .success(): print("Closed") case .failure(let error): print("Failed to close connection \(error)") } }
  59. Server on iOS Starscream NIO Transport Services Network.framework

  60. Starscream URLWebSocketTask?

  61. None
  62. Demo

  63. None
  64. Tools Charles Proxy Proxyman Cleora websocat

  65. None
  66. Pros Realtime updates Faster apps Supported by  * *

    iOS 13.0+
  67. Cons Before iOS13 can be buggy ⛓ Long polling can

    be more reliable ⚔ Tricky to debug Closing connection
  68. swiftwebsockets.com

  69. fassko www.kristaps.me