Last active
February 15, 2023 12:14
-
-
Save chriseidhof/fa706201ced29a492932 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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<UInt8>) | |
} | |
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<T,U>(p:UnsafePointer<T>) -> UnsafePointer<U> { | |
return UnsafePointer<U>(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) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment