TCPTransports/TCPTransport.swift
/* |
Copyright (C) 2018 Apple Inc. All Rights Reserved. |
See LICENSE.txt for this sample’s licensing information |
Abstract: |
Generic TCP transport API adopted by each concrete implementation. |
*/ |
import Foundation |
/// This protocol describes an object that can manage a TCP connection in some |
/// unspecified way. Concrete implementations might use BSD Sockets, |
/// CFSocketStream, or whatever. |
/// |
/// For the sake of simplicity this protocol defines that all work must be |
/// confined to a single queue. All methods must be called from that queue and |
/// all delegate methods will be called back on that queue. The sample as a |
/// whole always uses the main queue, but you could take the code and use it in |
/// a program that has other queues. |
/// |
/// Another simplification is that this protocol does not implement flow control: |
/// |
/// * On the read side, there’s no way to stop the transport from reading all |
/// the data off the connection. |
/// |
/// * On the write side, there’s no indication that data has backed up in the |
/// send buffer, so if the client issues an unbounded number of sends then the |
/// transport will buffer an unbounded amount of data |
/// |
/// In the use case illustrated by this sample — a simple interactive chat app |
/// based on a line-based protocol — this isn’t a problem. In other scenarios, |
/// like sending a large file, this would be disasterous. In that case you |
/// won’t be able to use this protocol or its implementations directly. |
/// |
/// This protocol sends and receives lines. Each implementation is responsible |
/// for framing those lines on the wire. In fact, each implementation uses the |
/// code in `LineFraming.swift` to frame the lines using the standard terminator |
/// used in network protocols (CR LF). It would be relatively easy to change |
/// this protocol to use some other message format (something encoded in binary) |
/// and then change an implementation to use a different framing and |
/// unframing type. In fact, you could even make `TCPTransport` generic in that |
/// type. I didn’t do this because I was trying to keep things simple. |
protocol TCPTransport : AnyObject { |
/// A dispatch queue to which the queue is confined. Specifically: |
/// |
/// * The client must call any methods like `start()` and `stop()` on that |
/// queue. |
/// |
/// * The object will call all delegate methods on that queue. |
/// |
/// This is typically under the client’s control, set via the initialiser |
/// function. The transport typically uses this internally but that’s not |
/// required. |
var queue: DispatchQueue { get } |
/// A delegate object, methods on which are called to indicate events on the |
/// transport. |
var delegate: TCPTransportDelegate? { get set } |
/// A high-level summary of the state of the transport. |
var transportState: TCPTransportState { get } |
/// Starts the TCP connection; can only be called once, in the |
/// `.initialised` state. |
func start() |
/// Sends a message over the transport. Can only be called in the |
/// `.started` state. |
/// |
/// - Parameter message: The message to sent, as a single line of text. |
func send(message: String) |
/// Stops the transport. Can be called in any state, including the |
/// `.stopped` state. Once the transport is stopped it cannot be restarted. |
/// |
/// - warning: If you start the transport you must ensure that it has |
/// stopped before releasing your last reference to it. |
/// |
/// The stream stops in two different ways: |
/// |
/// * You calling this method |
/// |
/// * The stream stopping on its own, in which case it calls the |
/// `didStop(transport:)` delegate callback |
/// |
/// As it’s safe to call `stop()` in the `.stopped` state, it’s a good idea |
/// to call `stop()` before releasing your primary reference. |
func stop() |
} |
/// The delegate protocol for `TCPTransport`. |
protocol TCPTransportDelegate : AnyObject { |
/// Called after the transports has started, that is, when the TCP |
/// connection comes up. The state will be `.started`. |
/// |
/// - Parameter transport: The associated transport object. |
func didStart(transport: TCPTransport) |
/// Called when a message arrives over the transport. The state will be |
/// `.started`. |
/// |
/// - Parameters: |
/// - message: The newly arrived message. |
/// - transport: The associated transport object. |
func didReceive(message: String, transport: TCPTransport) |
/// Called when the transport stops of its own accord. By the time this is |
/// called, the transport is in the `.stopped` state. The associated error |
/// value should give you some indication as to why it stopped. A nil value |
/// means it stopped because of EOF. |
/// |
/// - important: This is not called if the transport stops because you |
/// called `.stop()`. |
/// |
/// - Parameter transport: The associated transport object. |
func didStop(transport: TCPTransport) |
} |
/// The possible transport states. |
/// |
/// - initialised: The transport has been created by not started. |
/// - starting: The transport is starting up, that is, the TCP connection is |
/// connecting. |
/// - started: The transport has started up, that is, the TCP connection is in |
/// place. |
/// - stopped: The transport has stopped. If the error is nil the transport |
/// stopped because either the client called `stop()` or an EOF on the TCP |
/// connection. If the error is not nil, something went wrong with the TCP |
/// connection. |
enum TCPTransportState { |
case initialised |
case starting |
case started |
case stopped(Error?) |
} |
Copyright © 2018 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2018-05-10