// Created by Chris Eidhof on 04-01-16. // Copyright © 2016 Chris Eidhof. All rights reserved. // // Large parts copy/pasted from https://github.com/Eonil/TCPIPSocket.Swift import Foundation struct TCPIPSocketAddress { init(_ a:UInt8, _ b:UInt8, _ c:UInt8, _ d:UInt8) { let a1 = UInt32(a) << 24 let b1 = UInt32(b) << 16 let c1 = UInt32(c) << 8 let d1 = UInt32(d) number = a1 + b1 + c1 + d1 } var number:UInt32 /// Uses host-endian. static let localhost = TCPIPSocketAddress(127,0,0,1) } final class TCPIPConnection { static let bufLen = 1024 private var data: [UInt8] = Array(count: bufLen, repeatedValue: 0) // Keep it around for efficiency private let _conn: Int32 private init(_ conn: Int32) throws { _conn = conn try postconditionDarwinAPICallResultCodeState(_conn > 0) } func read() -> ReadResult { while true { let n = Darwin.read(_conn, &data, TCPIPConnection.bufLen) guard n != 0 else { return .Empty } guard n > 0 else { return .Error("Read error \(n)") } return .Chunk(AnySequence(data.prefix(n))) } } func write(bytes: [UInt8]) throws { let r = Darwin.write(_conn, bytes, bytes.count) try postconditionDarwinAPICallResultCodeState(r == bytes.count) } func close() { Darwin.close(_conn) } } enum ReadResult { case Error(ErrorType) case Empty case Chunk(AnySequence) } final class TCPIPSocket { let socketDescriptor:Int32 private var ds : dispatch_source_t? var handler: TCPIPConnection throws -> () = { _ in () } init(address: TCPIPSocketAddress, port: UInt16) throws { socketDescriptor = Darwin.socket(PF_INET, SOCK_STREAM, IPPROTO_TCP) try postconditionDarwinAPICallResultCodeState(socketDescriptor != -1) try bind(address, port) } deinit { let r = close(socketDescriptor) try! postconditionDarwinAPICallResultCodeState(r == 0) } private func bind(address: TCPIPSocketAddress, _ port: UInt16) throws { let f = sa_family_t(AF_INET) let p = in_port_t(port.bigEndian) let a = in_addr(s_addr: address.number.bigEndian) var addr = sockaddr_in(sin_len: 0, sin_family: f, sin_port: p, sin_addr: a, sin_zero: (0,0,0,0,0,0,0,0)) let sz = socklen_t(sizeofValue(addr)) let r = Darwin.bind(socketDescriptor, unsafePointerCast(&addr), sz) try postconditionDarwinAPICallResultCodeState(r == 0) } func listen(handler: TCPIPConnection throws -> ()) throws { self.handler = handler let r = Darwin.listen(socketDescriptor, SOMAXCONN) try postconditionDarwinAPICallResultCodeState(r == 0) ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, UInt(socketDescriptor), 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) guard let source = ds else { throw "Couldn't create dispatch source" } dispatch_source_set_event_handler(source, self.handleConnection) dispatch_resume(source) } func handleConnection() { do { let connection = try TCPIPConnection(Darwin.accept(socketDescriptor, nil, nil)) try handler(connection) } catch { print(error) } } } extension String: ErrorType { } private func postconditionDarwinAPICallResultCodeState(ok:Bool) throws { if ok { return } let n = Darwin.errno let s = String(UTF8String: strerror(n)) throw "Darwin API call error: (\(n)) \(s)" } private func unsafePointerCast(p:UnsafePointer) -> UnsafePointer { return UnsafePointer(p) } // Actually using the socket let socket = try TCPIPSocket(address: TCPIPSocketAddress.localhost, port: 2016) try socket.listen { connection in var result: [UInt8] = [] var done = false while !done { switch connection.read() { case .Empty: done = true case .Chunk(let chunk): result += chunk case .Error(let error): throw error } } print(result) sleep(10) try connection.write(result.reverse()) connection.close() } sleep(100)