// NetSocket.swift // A simple delegate based buffered read/write TCP socket. // Created by Dustin Mierau import Foundation protocol NetSocketDelegate: AnyObject { func netsocketConnected(socket: NetSocket) func netsocketDisconnected(socket: NetSocket, error: Error?) func netsocketReceived(socket: NetSocket, bytes: [UInt8]) func netsocketSent(socket: NetSocket, count: Int) } extension NetSocketDelegate { func netsocketConnected(socket: NetSocket) {} func netsocketDisconnected(socket: NetSocket, error: Error?) {} func netsocketReceived(socket: NetSocket, bytes: [UInt8]) {} func netsocketSent(socket: NetSocket, count: Int) {} } enum NetSocketStatus { case disconnected case connecting case connected } final class NetSocket: NSObject, StreamDelegate { weak var delegate: NetSocketDelegate? = nil private var output: OutputStream? = nil private var input: InputStream? = nil private var outputBuffer: [UInt8] = [] private var inputBuffer: [UInt8] = [] private var readBuffer: [UInt8] = Array(repeating: 0, count: 4 * 1024) public func peek() -> [UInt8] { self.inputBuffer } public var available: Int { self.inputBuffer.count } private var status: NetSocketStatus = .disconnected @MainActor public func has(_ length: Int) -> Bool { return (self.available >= length) } override init() {} @MainActor public func connect(host: String, port: Int) { self.close() var outputStream: OutputStream? = nil var inputStream: InputStream? = nil self.status = .connecting Stream.getStreamsToHost(withName: host, port: port, inputStream: &inputStream, outputStream: &outputStream) self.input = inputStream self.output = outputStream inputStream?.delegate = self outputStream?.delegate = self inputStream?.schedule(in: .current, forMode: .default) outputStream?.schedule(in: .current, forMode: .default) inputStream?.open() outputStream?.open() } @MainActor public func close(_ err: Error? = nil) { print("NetSocket: Closed") let disconnected = (self.status != .disconnected) self.status = .disconnected self.input?.delegate = nil self.output?.delegate = nil self.input?.close() self.output?.close() self.input?.remove(from: .current, forMode: .default) self.output?.remove(from: .current, forMode: .default) self.input = nil self.output = nil self.inputBuffer = [] self.outputBuffer = [] if disconnected { self.delegate?.netsocketDisconnected(socket: self, error: err) } } @MainActor public func write(_ data: Data) { guard let output = self.output else { return } self.outputBuffer.append(contentsOf: data) if output.hasSpaceAvailable { self.writeBufferToStream() } } @MainActor public func write(_ data: [UInt8]) { guard let output = self.output else { return } self.outputBuffer.append(contentsOf: data) if output.hasSpaceAvailable { self.writeBufferToStream() } } @MainActor public func read(count: Int) -> [UInt8] { guard self.inputBuffer.count > 0, count > 0 else { return [] } let amountToRead = min(count, self.inputBuffer.count) let dataRead: [UInt8] = Array(self.inputBuffer[0.. Data { guard self.inputBuffer.count > 0, count > 0 else { return Data() } let amountToRead = min(count, self.inputBuffer.count) let dataRead: Data = Data(self.inputBuffer[0.. [UInt8] { guard self.inputBuffer.count > 0 else { return [] } let dataRead: [UInt8] = Array(self.inputBuffer) self.inputBuffer = [] return dataRead } @MainActor public func readAll() -> Data { guard self.inputBuffer.count > 0 else { return Data() } let dataRead: Data = Data(self.inputBuffer) self.inputBuffer = [] return dataRead } @MainActor private func writeBufferToStream() { guard let output = self.output, self.outputBuffer.count > 0 else { return } let bytesWritten = output.write(self.outputBuffer, maxLength: self.outputBuffer.count) print("NetSocket => \(bytesWritten) bytes") if bytesWritten > 0 { self.outputBuffer.removeFirst(bytesWritten) self.delegate?.netsocketSent(socket: self, count: bytesWritten) } else if bytesWritten == -1 { self.close(output.streamError) } } @MainActor private func readStreamToBuffer() { guard let input = self.input else { return } let bytesRead = input.read(&self.readBuffer, maxLength: 4 * 1024) print("NetSocket <= \(bytesRead) bytes") if bytesRead > 0 { self.inputBuffer.append(contentsOf: self.readBuffer[0..