├── .gitignore ├── ChatServer ├── ChatServer.swift ├── ConnectedChatClient.swift └── main.swift ├── LICENSE ├── README.md ├── SocketWrapper.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SocketWrapper ├── Extensions │ └── String+UnsafeBufferPointer.swift └── SocketWrapper │ ├── AddressInfoSequence.swift │ ├── AddressSocketType.swift │ ├── ClientSocketType.swift │ ├── ConnectedClientSocketType.swift │ ├── ReceiveSocketType.swift │ ├── SendReceiveSocketType.swift │ ├── SendSocketType.swift │ ├── ServerSocketType.swift │ ├── Socket.swift │ ├── SocketAddress.swift │ ├── SocketDispatchSource.swift │ └── SocketType.swift ├── client └── main.swift └── server └── main.swift /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ._* 3 | *.swp 4 | *~.nib 5 | build/ 6 | *.pbxuser 7 | *.perspective 8 | *.perspectivev3 9 | *.mode1v3 10 | *.mode2v3 11 | xcuserdata 12 | *.xcscmblueprint 13 | *.xccheckout 14 | /Icon[^#-~] 15 | -------------------------------------------------------------------------------- /ChatServer/ChatServer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ChatServer.swift 3 | // ChatServer 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development Software GmbH. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ChatServer: ServerSocketType { 12 | 13 | let socket: Socket 14 | let address: SocketAddress 15 | var acceptSource: SocketDispatchSource? 16 | var chatClients = [ConnectedChatClient]() 17 | 18 | required init(socket: Socket, address: SocketAddress) { 19 | self.socket = socket 20 | self.address = address 21 | } 22 | 23 | deinit { 24 | stop() 25 | } 26 | 27 | } 28 | 29 | extension ChatServer { 30 | 31 | func start() throws { 32 | try bind() 33 | try listen() 34 | 35 | acceptSource = acceptAsync { 36 | self.acceptChatClient() 37 | } 38 | } 39 | 40 | func stop() { 41 | broadcast("Stopping chat server") 42 | acceptSource?.cancel() 43 | acceptSource = nil 44 | 45 | _ = try? close() 46 | 47 | for client in chatClients { 48 | disconnect(client, broadcastToOthers: false) 49 | } 50 | } 51 | 52 | func log(message: String, terminator: String = "\n") { 53 | print(message, terminator: terminator) 54 | } 55 | 56 | } 57 | 58 | extension ChatServer { 59 | 60 | func acceptChatClient() { 61 | do { 62 | let client = try accept { socket, address in 63 | return try ConnectedChatClient(socket: socket, address: address) 64 | } 65 | client.receiveSource = client.receiveAsync { 66 | self.handlePendingMessage(from: client) 67 | } 68 | chatClients.append(client) 69 | broadcast("\(client) connected") 70 | } catch { 71 | log("Error handling new client: \(error)") 72 | } 73 | } 74 | 75 | func handlePendingMessage(from client: ConnectedChatClient) { 76 | do { 77 | guard let message = try client.receiveUTF8String() else { 78 | send("Invalid message received", to: client, ignoringErrors: true) 79 | disconnect(client) 80 | return 81 | } 82 | 83 | let trimmedMessage = message.stringByTrimmingCharactersInSet(.whitespaceAndNewlineCharacterSet()) 84 | if !handleCommand(trimmedMessage.lowercaseString, from: client) && !trimmedMessage.isEmpty { 85 | broadcast(trimmedMessage, from: client) 86 | } 87 | } catch Socket.Error.ConnectionClosed { 88 | disconnect(client) 89 | } catch { 90 | log("Error handling message from: \(client): \(error)") 91 | disconnect(client) 92 | } 93 | } 94 | 95 | func send(message: String, to client: ConnectedChatClient, ignoringErrors: Bool = false) { 96 | do { 97 | log("Sending to \(client): \(message)") 98 | try client.send(message + "\n") 99 | } catch { 100 | if !ignoringErrors { 101 | log("Error sending message to: \(client), message: \(message)") 102 | disconnect(client) 103 | } 104 | } 105 | } 106 | 107 | func broadcast(message: String, from sender: ConnectedChatClient? = nil) { 108 | let serverName = "Server" 109 | let broadcastMessage = "\(sender?.description ?? serverName): \(message)\n" 110 | log(broadcastMessage, terminator: "") 111 | 112 | for receiver in chatClients where receiver != sender { 113 | do { 114 | try receiver.send(broadcastMessage) 115 | } catch { 116 | log("Error broadcasting message to: \(receiver)") 117 | disconnect(receiver) 118 | } 119 | } 120 | } 121 | 122 | func disconnect(client: ConnectedChatClient, broadcastToOthers: Bool = true) { 123 | if broadcastToOthers { 124 | broadcast("\(client) disconnected") 125 | } 126 | 127 | _ = try? client.close() 128 | 129 | if let index = chatClients.indexOf(client) { 130 | chatClients.removeAtIndex(index) 131 | } 132 | } 133 | 134 | } 135 | 136 | extension ChatServer { 137 | 138 | /// - Returns: `true` if `command` was actually a command, `false` otherwise. 139 | func handleCommand(command: String, from client: ConnectedChatClient) -> Bool { 140 | switch command { 141 | case "/exit": 142 | log("\(client) sent command: \(command)") 143 | disconnect(client) 144 | return true 145 | 146 | case "/who": 147 | log("\(client) sent command: \(command)") 148 | let chatClientDescriptions = chatClients.map { $0.fullDescription }.joinWithSeparator(", ") 149 | send(chatClientDescriptions, to: client) 150 | return true 151 | 152 | case "/stopserver": 153 | log("\(client) sent command: \(command)") 154 | stop() 155 | return true 156 | 157 | case "/whoami": 158 | log("\(client) sent command: \(command)") 159 | send("You are \(client)", to: client) 160 | return true 161 | 162 | default: 163 | // Wasn't a single word command. 164 | break 165 | } 166 | 167 | let commandComponents = command.componentsSeparatedByString(" ") 168 | guard !commandComponents.isEmpty else { 169 | return false 170 | } 171 | 172 | switch commandComponents[0] { 173 | case "/name": 174 | log("\(client) sent command: \(command)") 175 | guard commandComponents.count == 2 else { 176 | send("Invalid command", to: client) 177 | return true 178 | } 179 | let oldName = client.description 180 | client.name = commandComponents[1] 181 | broadcast("\(oldName) is now \(client)") 182 | return true 183 | 184 | default: 185 | break 186 | } 187 | 188 | // Wasn't a command: 189 | return false 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /ChatServer/ConnectedChatClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectedChatClient.swift 3 | // ChatServer 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development Software GmbH. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class ConnectedChatClient: ConnectedClientSocketType { 12 | 13 | let socket: Socket 14 | let address: SocketAddress 15 | let host: String 16 | let port: String 17 | var name: String? 18 | var receiveSource: SocketDispatchSource? 19 | 20 | required init(socket: Socket, address: SocketAddress) throws { 21 | self.socket = socket 22 | self.address = address 23 | 24 | // Cache the name info right away, so it can be printed even after close(): 25 | (host, port) = try address.nameInfo() 26 | } 27 | 28 | } 29 | 30 | extension ConnectedChatClient: Equatable { 31 | 32 | } 33 | 34 | func ==(lhs: ConnectedChatClient, rhs: ConnectedChatClient) -> Bool { 35 | return lhs.socket.fileDescriptor == rhs.socket.fileDescriptor 36 | } 37 | 38 | extension ConnectedChatClient: CustomStringConvertible { 39 | 40 | var description: String { 41 | return name ?? "\"\(host)\":\(port)" 42 | } 43 | 44 | var fullDescription: String { 45 | if let name = name { 46 | return "\(name) is \"\(host)\":\(port)" 47 | } else { 48 | return "\"\(host)\":\(port)" 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /ChatServer/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // ChatServer 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development Software GmbH. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | do { 12 | let port = "1234" 13 | let serverSocket = try ChatServer(port: port) 14 | try serverSocket.start() 15 | print("Chat server listening on port \(port)") 16 | } catch { 17 | print("Error: \(error)") 18 | exit(EXIT_FAILURE) 19 | } 20 | 21 | dispatch_main() // Starts runloop, never returns 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Objective Development Software GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SocketWrapper 2 | 3 | This is a POSIX socket API wrapper written in Swift. 4 | 5 | It is intended for testing networking stuff in [Little Snitch](https://obdev.at/littlesnitch) where exact control over the low-level socket API calls is important. Also, we wanted to try out some concepts in Swift, especially in regard to protocol oriented programming. 6 | 7 | It is not intended as a general purpose networking library that makes your life as an app developer easier. There are other things for that out there. 8 | 9 | A presentation of this code and its implementation was first shown to the public at a CocoaHeads meetup in Vienna on 2016-03-10. 10 | 11 | # TODO 12 | 13 | Here’s a very incomplete list of things that could be added or changed: 14 | - Implement support for other protocols than TCP. The low-level `Socket` struct probably doesn’t need any changes for that, but the higher-level `SocketType` sub-protocols don’t expose initializers for that. 15 | - getaddrinfo() returns a linked list of results and only the very first one is used for `connect()`/`accept()`. That list should probably be traversed until those calls actually succeed. 16 | -------------------------------------------------------------------------------- /SocketWrapper.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | A92924C31C9313A100CA0810 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924C21C9313A100CA0810 /* main.swift */; }; 11 | A92924CE1C9313A500CA0810 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924CD1C9313A500CA0810 /* main.swift */; }; 12 | A92924D21C9314AC00CA0810 /* String+UnsafeBufferPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A11C93138800CA0810 /* String+UnsafeBufferPointer.swift */; }; 13 | A92924D31C9314AC00CA0810 /* AddressInfoSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A31C93138800CA0810 /* AddressInfoSequence.swift */; }; 14 | A92924D41C9314AC00CA0810 /* AddressSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A41C93138800CA0810 /* AddressSocketType.swift */; }; 15 | A92924D61C9314AC00CA0810 /* ConnectedClientSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A61C93138800CA0810 /* ConnectedClientSocketType.swift */; }; 16 | A92924D71C9314AC00CA0810 /* ReceiveSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A71C93138800CA0810 /* ReceiveSocketType.swift */; }; 17 | A92924D81C9314AC00CA0810 /* SendReceiveSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A81C93138800CA0810 /* SendReceiveSocketType.swift */; }; 18 | A92924D91C9314AC00CA0810 /* SendSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A91C93138800CA0810 /* SendSocketType.swift */; }; 19 | A92924DB1C9314AC00CA0810 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AB1C93138800CA0810 /* Socket.swift */; }; 20 | A92924DC1C9314AC00CA0810 /* SocketAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AC1C93138800CA0810 /* SocketAddress.swift */; }; 21 | A92924DD1C9314AC00CA0810 /* SocketDispatchSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AD1C93138800CA0810 /* SocketDispatchSource.swift */; }; 22 | A92924DE1C9314AC00CA0810 /* SocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AE1C93138800CA0810 /* SocketType.swift */; }; 23 | A92924DF1C9314AD00CA0810 /* String+UnsafeBufferPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A11C93138800CA0810 /* String+UnsafeBufferPointer.swift */; }; 24 | A92924E01C9314AD00CA0810 /* AddressInfoSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A31C93138800CA0810 /* AddressInfoSequence.swift */; }; 25 | A92924E11C9314AD00CA0810 /* AddressSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A41C93138800CA0810 /* AddressSocketType.swift */; }; 26 | A92924E31C9314AD00CA0810 /* ConnectedClientSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A61C93138800CA0810 /* ConnectedClientSocketType.swift */; }; 27 | A92924E41C9314AD00CA0810 /* ReceiveSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A71C93138800CA0810 /* ReceiveSocketType.swift */; }; 28 | A92924E51C9314AD00CA0810 /* SendReceiveSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A81C93138800CA0810 /* SendReceiveSocketType.swift */; }; 29 | A92924E61C9314AD00CA0810 /* SendSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A91C93138800CA0810 /* SendSocketType.swift */; }; 30 | A92924E71C9314AD00CA0810 /* ServerSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AA1C93138800CA0810 /* ServerSocketType.swift */; }; 31 | A92924E81C9314AD00CA0810 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AB1C93138800CA0810 /* Socket.swift */; }; 32 | A92924E91C9314AD00CA0810 /* SocketAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AC1C93138800CA0810 /* SocketAddress.swift */; }; 33 | A92924EA1C9314AD00CA0810 /* SocketDispatchSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AD1C93138800CA0810 /* SocketDispatchSource.swift */; }; 34 | A92924EB1C9314AD00CA0810 /* SocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AE1C93138800CA0810 /* SocketType.swift */; }; 35 | A92924EC1C9315C500CA0810 /* ClientSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A51C93138800CA0810 /* ClientSocketType.swift */; }; 36 | A92924ED1C9315ED00CA0810 /* ClientSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A51C93138800CA0810 /* ClientSocketType.swift */; }; 37 | A92924EE1C9315ED00CA0810 /* ServerSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AA1C93138800CA0810 /* ServerSocketType.swift */; }; 38 | A92924F71C933B6000CA0810 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924F61C933B6000CA0810 /* main.swift */; }; 39 | A92924FB1C933B9600CA0810 /* String+UnsafeBufferPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A11C93138800CA0810 /* String+UnsafeBufferPointer.swift */; }; 40 | A92924FC1C933B9600CA0810 /* AddressInfoSequence.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A31C93138800CA0810 /* AddressInfoSequence.swift */; }; 41 | A92924FD1C933B9600CA0810 /* AddressSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A41C93138800CA0810 /* AddressSocketType.swift */; }; 42 | A92924FE1C933B9600CA0810 /* ClientSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A51C93138800CA0810 /* ClientSocketType.swift */; }; 43 | A92924FF1C933B9600CA0810 /* ConnectedClientSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A61C93138800CA0810 /* ConnectedClientSocketType.swift */; }; 44 | A92925001C933B9600CA0810 /* ReceiveSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A71C93138800CA0810 /* ReceiveSocketType.swift */; }; 45 | A92925011C933B9600CA0810 /* SendReceiveSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A81C93138800CA0810 /* SendReceiveSocketType.swift */; }; 46 | A92925021C933B9600CA0810 /* SendSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924A91C93138800CA0810 /* SendSocketType.swift */; }; 47 | A92925031C933B9600CA0810 /* ServerSocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AA1C93138800CA0810 /* ServerSocketType.swift */; }; 48 | A92925041C933B9600CA0810 /* Socket.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AB1C93138800CA0810 /* Socket.swift */; }; 49 | A92925051C933B9600CA0810 /* SocketAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AC1C93138800CA0810 /* SocketAddress.swift */; }; 50 | A92925061C933B9600CA0810 /* SocketDispatchSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AD1C93138800CA0810 /* SocketDispatchSource.swift */; }; 51 | A92925071C933B9600CA0810 /* SocketType.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92924AE1C93138800CA0810 /* SocketType.swift */; }; 52 | A92925091C933C3500CA0810 /* ChatServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = A92925081C933C3500CA0810 /* ChatServer.swift */; }; 53 | A929250B1C933C4700CA0810 /* ConnectedChatClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = A929250A1C933C4700CA0810 /* ConnectedChatClient.swift */; }; 54 | /* End PBXBuildFile section */ 55 | 56 | /* Begin PBXCopyFilesBuildPhase section */ 57 | A92924BE1C9313A100CA0810 /* CopyFiles */ = { 58 | isa = PBXCopyFilesBuildPhase; 59 | buildActionMask = 2147483647; 60 | dstPath = /usr/share/man/man1/; 61 | dstSubfolderSpec = 0; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 1; 65 | }; 66 | A92924C91C9313A500CA0810 /* CopyFiles */ = { 67 | isa = PBXCopyFilesBuildPhase; 68 | buildActionMask = 2147483647; 69 | dstPath = /usr/share/man/man1/; 70 | dstSubfolderSpec = 0; 71 | files = ( 72 | ); 73 | runOnlyForDeploymentPostprocessing = 1; 74 | }; 75 | A92924F21C933B6000CA0810 /* CopyFiles */ = { 76 | isa = PBXCopyFilesBuildPhase; 77 | buildActionMask = 2147483647; 78 | dstPath = /usr/share/man/man1/; 79 | dstSubfolderSpec = 0; 80 | files = ( 81 | ); 82 | runOnlyForDeploymentPostprocessing = 1; 83 | }; 84 | /* End PBXCopyFilesBuildPhase section */ 85 | 86 | /* Begin PBXFileReference section */ 87 | A92924A11C93138800CA0810 /* String+UnsafeBufferPointer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+UnsafeBufferPointer.swift"; sourceTree = ""; }; 88 | A92924A31C93138800CA0810 /* AddressInfoSequence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressInfoSequence.swift; sourceTree = ""; }; 89 | A92924A41C93138800CA0810 /* AddressSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddressSocketType.swift; sourceTree = ""; }; 90 | A92924A51C93138800CA0810 /* ClientSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientSocketType.swift; sourceTree = ""; }; 91 | A92924A61C93138800CA0810 /* ConnectedClientSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectedClientSocketType.swift; sourceTree = ""; }; 92 | A92924A71C93138800CA0810 /* ReceiveSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReceiveSocketType.swift; sourceTree = ""; }; 93 | A92924A81C93138800CA0810 /* SendReceiveSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendReceiveSocketType.swift; sourceTree = ""; }; 94 | A92924A91C93138800CA0810 /* SendSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SendSocketType.swift; sourceTree = ""; }; 95 | A92924AA1C93138800CA0810 /* ServerSocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServerSocketType.swift; sourceTree = ""; }; 96 | A92924AB1C93138800CA0810 /* Socket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Socket.swift; sourceTree = ""; }; 97 | A92924AC1C93138800CA0810 /* SocketAddress.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketAddress.swift; sourceTree = ""; }; 98 | A92924AD1C93138800CA0810 /* SocketDispatchSource.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketDispatchSource.swift; sourceTree = ""; }; 99 | A92924AE1C93138800CA0810 /* SocketType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SocketType.swift; sourceTree = ""; }; 100 | A92924C01C9313A100CA0810 /* server */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = server; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | A92924C21C9313A100CA0810 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 102 | A92924CB1C9313A500CA0810 /* client */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = client; sourceTree = BUILT_PRODUCTS_DIR; }; 103 | A92924CD1C9313A500CA0810 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 104 | A92924EF1C93160B00CA0810 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; 105 | A92924F41C933B6000CA0810 /* ChatServer */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ChatServer; sourceTree = BUILT_PRODUCTS_DIR; }; 106 | A92924F61C933B6000CA0810 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; 107 | A92925081C933C3500CA0810 /* ChatServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ChatServer.swift; sourceTree = ""; }; 108 | A929250A1C933C4700CA0810 /* ConnectedChatClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectedChatClient.swift; sourceTree = ""; }; 109 | /* End PBXFileReference section */ 110 | 111 | /* Begin PBXFrameworksBuildPhase section */ 112 | A92924BD1C9313A100CA0810 /* Frameworks */ = { 113 | isa = PBXFrameworksBuildPhase; 114 | buildActionMask = 2147483647; 115 | files = ( 116 | ); 117 | runOnlyForDeploymentPostprocessing = 0; 118 | }; 119 | A92924C81C9313A500CA0810 /* Frameworks */ = { 120 | isa = PBXFrameworksBuildPhase; 121 | buildActionMask = 2147483647; 122 | files = ( 123 | ); 124 | runOnlyForDeploymentPostprocessing = 0; 125 | }; 126 | A92924F11C933B6000CA0810 /* Frameworks */ = { 127 | isa = PBXFrameworksBuildPhase; 128 | buildActionMask = 2147483647; 129 | files = ( 130 | ); 131 | runOnlyForDeploymentPostprocessing = 0; 132 | }; 133 | /* End PBXFrameworksBuildPhase section */ 134 | 135 | /* Begin PBXGroup section */ 136 | A929248D1C93131800CA0810 = { 137 | isa = PBXGroup; 138 | children = ( 139 | A92924EF1C93160B00CA0810 /* README.md */, 140 | A92924A01C93138800CA0810 /* Extensions */, 141 | A92924A21C93138800CA0810 /* SocketWrapper */, 142 | A92924C11C9313A100CA0810 /* server */, 143 | A92924CC1C9313A500CA0810 /* client */, 144 | A92924F51C933B6000CA0810 /* ChatServer */, 145 | A92924971C93131800CA0810 /* Products */, 146 | ); 147 | sourceTree = ""; 148 | }; 149 | A92924971C93131800CA0810 /* Products */ = { 150 | isa = PBXGroup; 151 | children = ( 152 | A92924C01C9313A100CA0810 /* server */, 153 | A92924CB1C9313A500CA0810 /* client */, 154 | A92924F41C933B6000CA0810 /* ChatServer */, 155 | ); 156 | name = Products; 157 | sourceTree = ""; 158 | }; 159 | A92924A01C93138800CA0810 /* Extensions */ = { 160 | isa = PBXGroup; 161 | children = ( 162 | A92924A11C93138800CA0810 /* String+UnsafeBufferPointer.swift */, 163 | ); 164 | name = Extensions; 165 | path = SocketWrapper/Extensions; 166 | sourceTree = ""; 167 | }; 168 | A92924A21C93138800CA0810 /* SocketWrapper */ = { 169 | isa = PBXGroup; 170 | children = ( 171 | A92924A31C93138800CA0810 /* AddressInfoSequence.swift */, 172 | A92924A41C93138800CA0810 /* AddressSocketType.swift */, 173 | A92924A51C93138800CA0810 /* ClientSocketType.swift */, 174 | A92924A61C93138800CA0810 /* ConnectedClientSocketType.swift */, 175 | A92924A71C93138800CA0810 /* ReceiveSocketType.swift */, 176 | A92924A81C93138800CA0810 /* SendReceiveSocketType.swift */, 177 | A92924A91C93138800CA0810 /* SendSocketType.swift */, 178 | A92924AA1C93138800CA0810 /* ServerSocketType.swift */, 179 | A92924AB1C93138800CA0810 /* Socket.swift */, 180 | A92924AC1C93138800CA0810 /* SocketAddress.swift */, 181 | A92924AD1C93138800CA0810 /* SocketDispatchSource.swift */, 182 | A92924AE1C93138800CA0810 /* SocketType.swift */, 183 | ); 184 | name = SocketWrapper; 185 | path = SocketWrapper/SocketWrapper; 186 | sourceTree = ""; 187 | }; 188 | A92924C11C9313A100CA0810 /* server */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | A92924C21C9313A100CA0810 /* main.swift */, 192 | ); 193 | path = server; 194 | sourceTree = ""; 195 | }; 196 | A92924CC1C9313A500CA0810 /* client */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | A92924CD1C9313A500CA0810 /* main.swift */, 200 | ); 201 | path = client; 202 | sourceTree = ""; 203 | }; 204 | A92924F51C933B6000CA0810 /* ChatServer */ = { 205 | isa = PBXGroup; 206 | children = ( 207 | A92925081C933C3500CA0810 /* ChatServer.swift */, 208 | A929250A1C933C4700CA0810 /* ConnectedChatClient.swift */, 209 | A92924F61C933B6000CA0810 /* main.swift */, 210 | ); 211 | path = ChatServer; 212 | sourceTree = ""; 213 | }; 214 | /* End PBXGroup section */ 215 | 216 | /* Begin PBXNativeTarget section */ 217 | A92924BF1C9313A100CA0810 /* server */ = { 218 | isa = PBXNativeTarget; 219 | buildConfigurationList = A92924C41C9313A100CA0810 /* Build configuration list for PBXNativeTarget "server" */; 220 | buildPhases = ( 221 | A92924BC1C9313A100CA0810 /* Sources */, 222 | A92924BD1C9313A100CA0810 /* Frameworks */, 223 | A92924BE1C9313A100CA0810 /* CopyFiles */, 224 | ); 225 | buildRules = ( 226 | ); 227 | dependencies = ( 228 | ); 229 | name = server; 230 | productName = server; 231 | productReference = A92924C01C9313A100CA0810 /* server */; 232 | productType = "com.apple.product-type.tool"; 233 | }; 234 | A92924CA1C9313A500CA0810 /* client */ = { 235 | isa = PBXNativeTarget; 236 | buildConfigurationList = A92924CF1C9313A500CA0810 /* Build configuration list for PBXNativeTarget "client" */; 237 | buildPhases = ( 238 | A92924C71C9313A500CA0810 /* Sources */, 239 | A92924C81C9313A500CA0810 /* Frameworks */, 240 | A92924C91C9313A500CA0810 /* CopyFiles */, 241 | ); 242 | buildRules = ( 243 | ); 244 | dependencies = ( 245 | ); 246 | name = client; 247 | productName = client; 248 | productReference = A92924CB1C9313A500CA0810 /* client */; 249 | productType = "com.apple.product-type.tool"; 250 | }; 251 | A92924F31C933B6000CA0810 /* ChatServer */ = { 252 | isa = PBXNativeTarget; 253 | buildConfigurationList = A92924F81C933B6000CA0810 /* Build configuration list for PBXNativeTarget "ChatServer" */; 254 | buildPhases = ( 255 | A92924F01C933B6000CA0810 /* Sources */, 256 | A92924F11C933B6000CA0810 /* Frameworks */, 257 | A92924F21C933B6000CA0810 /* CopyFiles */, 258 | ); 259 | buildRules = ( 260 | ); 261 | dependencies = ( 262 | ); 263 | name = ChatServer; 264 | productName = ChatServer; 265 | productReference = A92924F41C933B6000CA0810 /* ChatServer */; 266 | productType = "com.apple.product-type.tool"; 267 | }; 268 | /* End PBXNativeTarget section */ 269 | 270 | /* Begin PBXProject section */ 271 | A929248E1C93131800CA0810 /* Project object */ = { 272 | isa = PBXProject; 273 | attributes = { 274 | LastSwiftUpdateCheck = 0730; 275 | LastUpgradeCheck = 0730; 276 | ORGANIZATIONNAME = "Objective Development Software GmbH"; 277 | TargetAttributes = { 278 | A92924BF1C9313A100CA0810 = { 279 | CreatedOnToolsVersion = 7.3; 280 | }; 281 | A92924CA1C9313A500CA0810 = { 282 | CreatedOnToolsVersion = 7.3; 283 | }; 284 | A92924F31C933B6000CA0810 = { 285 | CreatedOnToolsVersion = 7.3; 286 | }; 287 | }; 288 | }; 289 | buildConfigurationList = A92924911C93131800CA0810 /* Build configuration list for PBXProject "SocketWrapper" */; 290 | compatibilityVersion = "Xcode 3.2"; 291 | developmentRegion = English; 292 | hasScannedForEncodings = 0; 293 | knownRegions = ( 294 | en, 295 | ); 296 | mainGroup = A929248D1C93131800CA0810; 297 | productRefGroup = A92924971C93131800CA0810 /* Products */; 298 | projectDirPath = ""; 299 | projectRoot = ""; 300 | targets = ( 301 | A92924BF1C9313A100CA0810 /* server */, 302 | A92924CA1C9313A500CA0810 /* client */, 303 | A92924F31C933B6000CA0810 /* ChatServer */, 304 | ); 305 | }; 306 | /* End PBXProject section */ 307 | 308 | /* Begin PBXSourcesBuildPhase section */ 309 | A92924BC1C9313A100CA0810 /* Sources */ = { 310 | isa = PBXSourcesBuildPhase; 311 | buildActionMask = 2147483647; 312 | files = ( 313 | A92924E01C9314AD00CA0810 /* AddressInfoSequence.swift in Sources */, 314 | A92924DF1C9314AD00CA0810 /* String+UnsafeBufferPointer.swift in Sources */, 315 | A92924C31C9313A100CA0810 /* main.swift in Sources */, 316 | A92924EB1C9314AD00CA0810 /* SocketType.swift in Sources */, 317 | A92924E11C9314AD00CA0810 /* AddressSocketType.swift in Sources */, 318 | A92924E91C9314AD00CA0810 /* SocketAddress.swift in Sources */, 319 | A92924E71C9314AD00CA0810 /* ServerSocketType.swift in Sources */, 320 | A92924E81C9314AD00CA0810 /* Socket.swift in Sources */, 321 | A92924E31C9314AD00CA0810 /* ConnectedClientSocketType.swift in Sources */, 322 | A92924ED1C9315ED00CA0810 /* ClientSocketType.swift in Sources */, 323 | A92924EA1C9314AD00CA0810 /* SocketDispatchSource.swift in Sources */, 324 | A92924E61C9314AD00CA0810 /* SendSocketType.swift in Sources */, 325 | A92924E51C9314AD00CA0810 /* SendReceiveSocketType.swift in Sources */, 326 | A92924E41C9314AD00CA0810 /* ReceiveSocketType.swift in Sources */, 327 | ); 328 | runOnlyForDeploymentPostprocessing = 0; 329 | }; 330 | A92924C71C9313A500CA0810 /* Sources */ = { 331 | isa = PBXSourcesBuildPhase; 332 | buildActionMask = 2147483647; 333 | files = ( 334 | A92924D31C9314AC00CA0810 /* AddressInfoSequence.swift in Sources */, 335 | A92924EE1C9315ED00CA0810 /* ServerSocketType.swift in Sources */, 336 | A92924D21C9314AC00CA0810 /* String+UnsafeBufferPointer.swift in Sources */, 337 | A92924CE1C9313A500CA0810 /* main.swift in Sources */, 338 | A92924DE1C9314AC00CA0810 /* SocketType.swift in Sources */, 339 | A92924D41C9314AC00CA0810 /* AddressSocketType.swift in Sources */, 340 | A92924DC1C9314AC00CA0810 /* SocketAddress.swift in Sources */, 341 | A92924DB1C9314AC00CA0810 /* Socket.swift in Sources */, 342 | A92924D61C9314AC00CA0810 /* ConnectedClientSocketType.swift in Sources */, 343 | A92924EC1C9315C500CA0810 /* ClientSocketType.swift in Sources */, 344 | A92924DD1C9314AC00CA0810 /* SocketDispatchSource.swift in Sources */, 345 | A92924D91C9314AC00CA0810 /* SendSocketType.swift in Sources */, 346 | A92924D81C9314AC00CA0810 /* SendReceiveSocketType.swift in Sources */, 347 | A92924D71C9314AC00CA0810 /* ReceiveSocketType.swift in Sources */, 348 | ); 349 | runOnlyForDeploymentPostprocessing = 0; 350 | }; 351 | A92924F01C933B6000CA0810 /* Sources */ = { 352 | isa = PBXSourcesBuildPhase; 353 | buildActionMask = 2147483647; 354 | files = ( 355 | A92924FC1C933B9600CA0810 /* AddressInfoSequence.swift in Sources */, 356 | A92924FE1C933B9600CA0810 /* ClientSocketType.swift in Sources */, 357 | A92924FB1C933B9600CA0810 /* String+UnsafeBufferPointer.swift in Sources */, 358 | A92924F71C933B6000CA0810 /* main.swift in Sources */, 359 | A92925071C933B9600CA0810 /* SocketType.swift in Sources */, 360 | A92924FD1C933B9600CA0810 /* AddressSocketType.swift in Sources */, 361 | A92925051C933B9600CA0810 /* SocketAddress.swift in Sources */, 362 | A92925031C933B9600CA0810 /* ServerSocketType.swift in Sources */, 363 | A92925041C933B9600CA0810 /* Socket.swift in Sources */, 364 | A92925091C933C3500CA0810 /* ChatServer.swift in Sources */, 365 | A929250B1C933C4700CA0810 /* ConnectedChatClient.swift in Sources */, 366 | A92924FF1C933B9600CA0810 /* ConnectedClientSocketType.swift in Sources */, 367 | A92925061C933B9600CA0810 /* SocketDispatchSource.swift in Sources */, 368 | A92925021C933B9600CA0810 /* SendSocketType.swift in Sources */, 369 | A92925011C933B9600CA0810 /* SendReceiveSocketType.swift in Sources */, 370 | A92925001C933B9600CA0810 /* ReceiveSocketType.swift in Sources */, 371 | ); 372 | runOnlyForDeploymentPostprocessing = 0; 373 | }; 374 | /* End PBXSourcesBuildPhase section */ 375 | 376 | /* Begin XCBuildConfiguration section */ 377 | A929249B1C93131800CA0810 /* Debug */ = { 378 | isa = XCBuildConfiguration; 379 | buildSettings = { 380 | ALWAYS_SEARCH_USER_PATHS = NO; 381 | CLANG_ANALYZER_NONNULL = YES; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_WARN_BOOL_CONVERSION = YES; 387 | CLANG_WARN_CONSTANT_CONVERSION = YES; 388 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 389 | CLANG_WARN_EMPTY_BODY = YES; 390 | CLANG_WARN_ENUM_CONVERSION = YES; 391 | CLANG_WARN_INT_CONVERSION = YES; 392 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 393 | CLANG_WARN_UNREACHABLE_CODE = YES; 394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 | CODE_SIGN_IDENTITY = "-"; 396 | COPY_PHASE_STRIP = NO; 397 | DEBUG_INFORMATION_FORMAT = dwarf; 398 | ENABLE_STRICT_OBJC_MSGSEND = YES; 399 | ENABLE_TESTABILITY = YES; 400 | GCC_C_LANGUAGE_STANDARD = gnu99; 401 | GCC_DYNAMIC_NO_PIC = NO; 402 | GCC_NO_COMMON_BLOCKS = YES; 403 | GCC_OPTIMIZATION_LEVEL = 0; 404 | GCC_PREPROCESSOR_DEFINITIONS = ( 405 | "DEBUG=1", 406 | "$(inherited)", 407 | ); 408 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 409 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 410 | GCC_WARN_UNDECLARED_SELECTOR = YES; 411 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 412 | GCC_WARN_UNUSED_FUNCTION = YES; 413 | GCC_WARN_UNUSED_VARIABLE = YES; 414 | MACOSX_DEPLOYMENT_TARGET = 10.11; 415 | MTL_ENABLE_DEBUG_INFO = YES; 416 | ONLY_ACTIVE_ARCH = YES; 417 | SDKROOT = macosx; 418 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 419 | }; 420 | name = Debug; 421 | }; 422 | A929249C1C93131800CA0810 /* Release */ = { 423 | isa = XCBuildConfiguration; 424 | buildSettings = { 425 | ALWAYS_SEARCH_USER_PATHS = NO; 426 | CLANG_ANALYZER_NONNULL = YES; 427 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 428 | CLANG_CXX_LIBRARY = "libc++"; 429 | CLANG_ENABLE_MODULES = YES; 430 | CLANG_ENABLE_OBJC_ARC = YES; 431 | CLANG_WARN_BOOL_CONVERSION = YES; 432 | CLANG_WARN_CONSTANT_CONVERSION = YES; 433 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 434 | CLANG_WARN_EMPTY_BODY = YES; 435 | CLANG_WARN_ENUM_CONVERSION = YES; 436 | CLANG_WARN_INT_CONVERSION = YES; 437 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 438 | CLANG_WARN_UNREACHABLE_CODE = YES; 439 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 440 | CODE_SIGN_IDENTITY = "-"; 441 | COPY_PHASE_STRIP = NO; 442 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 443 | ENABLE_NS_ASSERTIONS = NO; 444 | ENABLE_STRICT_OBJC_MSGSEND = YES; 445 | GCC_C_LANGUAGE_STANDARD = gnu99; 446 | GCC_NO_COMMON_BLOCKS = YES; 447 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 448 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 449 | GCC_WARN_UNDECLARED_SELECTOR = YES; 450 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 451 | GCC_WARN_UNUSED_FUNCTION = YES; 452 | GCC_WARN_UNUSED_VARIABLE = YES; 453 | MACOSX_DEPLOYMENT_TARGET = 10.11; 454 | MTL_ENABLE_DEBUG_INFO = NO; 455 | SDKROOT = macosx; 456 | }; 457 | name = Release; 458 | }; 459 | A92924C51C9313A100CA0810 /* Debug */ = { 460 | isa = XCBuildConfiguration; 461 | buildSettings = { 462 | PRODUCT_NAME = "$(TARGET_NAME)"; 463 | }; 464 | name = Debug; 465 | }; 466 | A92924C61C9313A100CA0810 /* Release */ = { 467 | isa = XCBuildConfiguration; 468 | buildSettings = { 469 | PRODUCT_NAME = "$(TARGET_NAME)"; 470 | }; 471 | name = Release; 472 | }; 473 | A92924D01C9313A500CA0810 /* Debug */ = { 474 | isa = XCBuildConfiguration; 475 | buildSettings = { 476 | PRODUCT_NAME = "$(TARGET_NAME)"; 477 | }; 478 | name = Debug; 479 | }; 480 | A92924D11C9313A500CA0810 /* Release */ = { 481 | isa = XCBuildConfiguration; 482 | buildSettings = { 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | }; 485 | name = Release; 486 | }; 487 | A92924F91C933B6000CA0810 /* Debug */ = { 488 | isa = XCBuildConfiguration; 489 | buildSettings = { 490 | PRODUCT_NAME = "$(TARGET_NAME)"; 491 | }; 492 | name = Debug; 493 | }; 494 | A92924FA1C933B6000CA0810 /* Release */ = { 495 | isa = XCBuildConfiguration; 496 | buildSettings = { 497 | PRODUCT_NAME = "$(TARGET_NAME)"; 498 | }; 499 | name = Release; 500 | }; 501 | /* End XCBuildConfiguration section */ 502 | 503 | /* Begin XCConfigurationList section */ 504 | A92924911C93131800CA0810 /* Build configuration list for PBXProject "SocketWrapper" */ = { 505 | isa = XCConfigurationList; 506 | buildConfigurations = ( 507 | A929249B1C93131800CA0810 /* Debug */, 508 | A929249C1C93131800CA0810 /* Release */, 509 | ); 510 | defaultConfigurationIsVisible = 0; 511 | defaultConfigurationName = Release; 512 | }; 513 | A92924C41C9313A100CA0810 /* Build configuration list for PBXNativeTarget "server" */ = { 514 | isa = XCConfigurationList; 515 | buildConfigurations = ( 516 | A92924C51C9313A100CA0810 /* Debug */, 517 | A92924C61C9313A100CA0810 /* Release */, 518 | ); 519 | defaultConfigurationIsVisible = 0; 520 | }; 521 | A92924CF1C9313A500CA0810 /* Build configuration list for PBXNativeTarget "client" */ = { 522 | isa = XCConfigurationList; 523 | buildConfigurations = ( 524 | A92924D01C9313A500CA0810 /* Debug */, 525 | A92924D11C9313A500CA0810 /* Release */, 526 | ); 527 | defaultConfigurationIsVisible = 0; 528 | }; 529 | A92924F81C933B6000CA0810 /* Build configuration list for PBXNativeTarget "ChatServer" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | A92924F91C933B6000CA0810 /* Debug */, 533 | A92924FA1C933B6000CA0810 /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | }; 537 | /* End XCConfigurationList section */ 538 | }; 539 | rootObject = A929248E1C93131800CA0810 /* Project object */; 540 | } 541 | -------------------------------------------------------------------------------- /SocketWrapper.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SocketWrapper/Extensions/String+UnsafeBufferPointer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+UnsafeBufferPointer.swift 3 | // SocketWrapper 4 | // 5 | // Created by Christian Ludl on 2016-02-11. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | 13 | /// Initializes a `String` from a sequence of `NUL`-terminated `UTF8.CodeUnit` as a `WithUnsafeBufferPointerType`. 14 | init?(nulTerminatedUTF8: T) { 15 | guard let string = nulTerminatedUTF8.withUnsafeBufferPointer({ String(UTF8String: UnsafePointer($0.baseAddress))}) else { 16 | return nil 17 | } 18 | self = string 19 | } 20 | 21 | /// Initializes a `String` from a sequence of `UTF8.CodeUnit`s that conforms to `WithUnsafeBufferPointerType`. A `NUL`-terminator is not required. 22 | init?(UTF8CodeUnits: T) { 23 | let string = UTF8CodeUnits.withUnsafeBufferPointer { NSString(bytes: $0.baseAddress, length: $0.count, encoding: NSUTF8StringEncoding) } 24 | if let string = string as? String { 25 | self = string 26 | } else { 27 | return nil 28 | } 29 | } 30 | 31 | /// Initializes a `String` from any sequence of `UTF8.CodeUnit`s. A `NUL`-terminator is not required. 32 | /// 33 | /// Note that this initializer may be slow because it must first convert the input sequence to an `Array`. 34 | init?(UTF8CodeUnitSequence: T) { 35 | self.init(UTF8CodeUnits: Array(UTF8CodeUnitSequence)) 36 | } 37 | 38 | /// Initializes a `String` from a buffer of `UTF8.CodeUnit`. A `NUL`-terminator is not required. 39 | init?(UTF8CodeUnits buffer: UnsafeBufferPointer) { 40 | if let string = NSString(bytes: buffer.baseAddress, length: buffer.count, encoding: NSUTF8StringEncoding) { 41 | self = string as String 42 | } else { 43 | return nil 44 | } 45 | } 46 | 47 | /// Initializes a `String` from a mutable buffer of `UTF8.CodeUnit`. A `NUL`-terminator is not required. 48 | /// 49 | /// This is a convenience initializer for when a `UnsafeMutableBufferPointer` exists already. It does not modify the input. 50 | init?(UTF8CodeUnits buffer: UnsafeMutableBufferPointer) { 51 | self.init(UTF8CodeUnits: UnsafeBufferPointer(start: buffer.baseAddress, count: buffer.count)) 52 | } 53 | 54 | /// Calls the given closure with a `UnsafeBufferPointer` to an optionally `NUL`-terminated UTF-8 representation of the `String`. 55 | func withUTF8UnsafeBufferPointer(includeNulTerminator includeNulTerminator: Bool = true, @noescape f: UnsafeBufferPointer throws -> Result) rethrows -> Result { 56 | return try nulTerminatedUTF8.withUnsafeBufferPointer { codeUnitBuffer in 57 | let cCharBufferCount = includeNulTerminator ? codeUnitBuffer.count : codeUnitBuffer.count - 1 58 | let cCharBuffer = UnsafeBufferPointer(start: UnsafePointer(codeUnitBuffer.baseAddress), count: cCharBufferCount) 59 | return try f(cCharBuffer) 60 | } 61 | } 62 | 63 | } 64 | 65 | 66 | /// A common protocol of all array-like types that implement a `withUnsafeBufferPointer()` method. 67 | protocol WithUnsafeBufferPointerType { 68 | associatedtype Element 69 | func withUnsafeBufferPointer(@noescape body: (UnsafeBufferPointer) throws -> R) rethrows -> R 70 | } 71 | 72 | extension Array: WithUnsafeBufferPointerType { 73 | 74 | } 75 | 76 | extension ArraySlice: WithUnsafeBufferPointerType { 77 | 78 | } 79 | 80 | extension ContiguousArray: WithUnsafeBufferPointerType { 81 | 82 | } 83 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/AddressInfoSequence.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressInfoSequence.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-04. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | 11 | /// A wrapper around a `addrinfo` linked list. 12 | /// 13 | /// - Important: When iterating over an `AddressInfoSequence` using its `SequenceType` conformance, the `addrinfo` 14 | /// struct's internal pointers are only valid as long as the `GeneratorType` returned by `generate` is alive. 15 | struct AddressInfoSequence { 16 | 17 | /// An internal storage that is used by `AddressInfoSequence` and its `SequenceType` implementation. 18 | private class Storage { 19 | 20 | /// The internal pointer to the head of the linked list. 21 | /// 22 | /// - Important: This pointer and the whole linked list - including all the `addrinfo`'s 23 | /// internal pointers - are only valid as long as `self` is referenced. 24 | private let _addrInfoPointer: UnsafeMutablePointer 25 | 26 | /// Creates an instance with an existing `UnsafeMutablePointer`, taking ownership of it. 27 | init(addrInfoPointer: UnsafeMutablePointer) { 28 | _addrInfoPointer = addrInfoPointer 29 | } 30 | 31 | deinit { 32 | freeaddrinfo(_addrInfoPointer) 33 | } 34 | 35 | } 36 | 37 | /// The internal storage for the `addrinfo` linked list. 38 | private let _addrInfoStorage: Storage 39 | 40 | /// Creates an instance intended for a server socket for the given `port` that will be used with `bind()`. 41 | /// 42 | /// - parameter family: Default: `AF_UNSPEC`, i.e. IP4 or IP6. 43 | /// - parameter socketType: Default: `SOCK_STREAM`, i.e. a TCP socket. 44 | /// - parameter flags: Default: `AI_PASSIVE`, i.e. set the IP address of the resulting `sockaddr` to `INADDR_ANY` (IPv4) or `IN6ADDR_ANY_INIT` (IPv6). 45 | init(forBindingToPort port: String, family: Int32 = AF_UNSPEC, socketType: Int32 = SOCK_STREAM, flags: Int32 = AI_PASSIVE) throws { 46 | try self.init(host: nil, port: port, family: family, socketType: socketType, flags: flags) 47 | } 48 | 49 | /// Creates an instance intended for a client socket for the given `host` and `port` that will be used with `connect()`. 50 | /// 51 | /// - parameter family: Default: `AF_UNSPEC`, i.e. IP4 or IP6. 52 | /// - parameter socketType: Default: `SOCK_STREAM`, i.e. a TCP socket. 53 | /// - parameter flags: Default: `AI_DEFAULT` (= `AI_V4MAPPED_CFG | AI_ADDRCONFIG`). 54 | init(forConnectingToHost host: String, port: String, family: Int32 = AF_UNSPEC, socketType: Int32 = SOCK_STREAM, flags: Int32 = AI_V4MAPPED_CFG | AI_ADDRCONFIG) throws { 55 | try self.init(host: host, port: port, family: family, socketType: socketType, flags: flags) 56 | } 57 | 58 | /// Creates an instance for the given `host` and/or `port`. 59 | /// 60 | /// - parameter family: For example `AF_UNSPEC`, i.e. IP4 or IP6. 61 | /// - parameter socketType: For example `SOCK_STREAM`, i.e. a TCP socket. 62 | /// - parameter flags: For example `AI_DEFAULT`. 63 | private init(host: String?, port: String?, family: Int32, socketType: Int32, flags: Int32) throws { 64 | var hints = addrinfo(); 65 | hints.ai_family = family 66 | hints.ai_socktype = socketType 67 | hints.ai_flags = flags 68 | 69 | try self.init(host: host, port: port, hints: &hints) 70 | } 71 | 72 | /// Creates an instance by calling `getaddrinfo()` with the given arguments. 73 | /// 74 | /// Throws an error either if `getaddrinfo` failed, or if it returned an empty list. Therefore, if this initializer doesn't throw, 75 | /// the created instance is guaranteed to have at least one `addrinfo`. 76 | /// 77 | /// - Parameter host: The hostname or IP address to resolve. Pass `nil` when resolving addresses for a server socket. 78 | /// - Parameter port: The port or service name to resolve, e.g. "80" or "http". Pass `nil` when resolving an address 79 | /// for a protocol that does not use ports, e.g. ICMP. 80 | /// - Parameter hints: A pointer to an `addrinfo` struct that is used as hints. This allows specifying things like 81 | /// the protocol family, socket type, or protocol. May be `nil`. 82 | /// 83 | /// - Important: `host` and `port` are both optional, but at least one of them must be given. 84 | /// 85 | /// - Note: See `getaddrinfo(3)` for more details about the parameters. 86 | private init(host: String?, port: String?, hints: UnsafePointer) throws { 87 | var addrInfoPointer: UnsafeMutablePointer = nil 88 | let result: Int32 89 | 90 | // `String` bridges to `UnsafePointer` automatically, but `String?` does not. This 91 | // switch takes care of the various combinations of `host` and `port` that can occur. 92 | switch (host, port) { 93 | case let (host?, port?): 94 | result = getaddrinfo(host, port, hints, &addrInfoPointer) 95 | 96 | case let (nil, port?): 97 | result = getaddrinfo(nil, port, hints, &addrInfoPointer) 98 | 99 | case let (host?, nil): 100 | result = getaddrinfo(host, nil, hints, &addrInfoPointer) 101 | 102 | default: 103 | preconditionFailure("Either host or port must be given") 104 | } 105 | 106 | guard result != -1 else { 107 | throw Socket.Error.GetAddrInfoFailed(code: result) 108 | } 109 | guard addrInfoPointer != nil else { 110 | throw Socket.Error.NoAddressAvailable 111 | } 112 | 113 | _addrInfoStorage = Storage(addrInfoPointer: addrInfoPointer) 114 | } 115 | 116 | } 117 | 118 | 119 | extension AddressInfoSequence: SequenceType { 120 | 121 | func generate() -> AddressInfoGenerator { 122 | return AddressInfoGenerator(storage: _addrInfoStorage) 123 | } 124 | 125 | } 126 | 127 | struct AddressInfoGenerator: GeneratorType { 128 | 129 | private let _storage: AddressInfoSequence.Storage 130 | private var _cursor: UnsafeMutablePointer 131 | 132 | private init(storage: AddressInfoSequence.Storage) { 133 | _storage = storage 134 | _cursor = storage._addrInfoPointer 135 | } 136 | 137 | mutating func next() -> addrinfo? { 138 | guard _cursor != nil else { 139 | return nil 140 | } 141 | var addrInfo = _cursor.memory 142 | _cursor = addrInfo.ai_next 143 | addrInfo.ai_next = nil // Prevent access to the next element of the linked list. 144 | return addrInfo 145 | } 146 | 147 | } 148 | 149 | extension AddressInfoSequence { 150 | 151 | /// Calls `f` with a copy of the first `addrinfo` in the sequence. 152 | func withFirstAddrInfo(@noescape f: (addrinfo) throws -> R) rethrows -> R { 153 | return try f(_addrInfoStorage._addrInfoPointer.memory) 154 | } 155 | 156 | } 157 | 158 | 159 | #if false // Doesn't work yet. 160 | extension AddressInfoSequence: CustomStringConvertible { 161 | 162 | var description: String { 163 | return "[" + map { SocketAddress(addrInfo: $0).displayName }.reduce("") { $0 + ", " + $1 } + "]" 164 | } 165 | 166 | } 167 | #endif 168 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/AddressSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSocketType.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | 11 | /// A `SocketType` that has a `SocketAddress` associated with it. 12 | protocol AddressSocketType: SocketType { 13 | 14 | /// The address that the `socket` is associated with. 15 | /// - In a client, this is the address of the server the client socket `connect()`ed to. 16 | /// - In a client, this is the address of the local port the server socket `bind()`ed to. 17 | var address: SocketAddress { get } 18 | 19 | /// Creates an instance with the given `socket` and `address`. 20 | init(socket: Socket, address: SocketAddress) throws 21 | 22 | } 23 | 24 | // Initializers. 25 | extension AddressSocketType { 26 | 27 | /// Creates an instance with a given `addrinfo`. 28 | init(addrInfo: addrinfo) throws { 29 | let socket = try Socket(addrInfo: addrInfo) 30 | let address = SocketAddress(addrInfo: addrInfo) 31 | try self.init(socket: socket, address: address) 32 | } 33 | 34 | } 35 | 36 | 37 | // Common computed properties. 38 | extension AddressSocketType { 39 | 40 | /// Returns the connected client's `host` and `port` by querying its `address.nameInfo()`. 41 | func nameInfo() throws -> (host: String, port: String) { 42 | return try address.nameInfo() 43 | } 44 | 45 | /// - For a server socket, returns `nil`. 46 | /// - For a client socket, returns the hostname the socket is connected to. 47 | var host: String? { 48 | return try? address.nameInfo().host 49 | } 50 | 51 | /// - For a server socket, returns the port the socket is `bind()`ed to. 52 | /// - For a client socket, returns the port the socket is `connect()`ed to. 53 | var port: String? { 54 | return try? address.nameInfo().port 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/ClientSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClientSocketProtocols.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-04. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | /// Represents a client socket that can `connect()` to a peer identified by a `host` and `port`. 10 | protocol ClientSocketType: AddressSocketType, SendReceiveSocketType { 11 | 12 | } 13 | 14 | extension ClientSocketType { 15 | 16 | /// Creates an instance by resolving the given `host` and `port`. 17 | /// 18 | /// - Parameter host: The hostname or IP address to resolve. 19 | /// - Parameter port: The port or service name to resolve, e.g. "80" or "http". 20 | /// 21 | /// - SeeAlso: `AddressInfoSequence.Storage` 22 | /// 23 | /// - TODO: Don't just pick the first address, but try the whole list for (the first?) one that works. 24 | /// This may require a new initializer like `init(connectingToHost:port:)` that loops over the 25 | /// `AddressInfoSequence` and calls `connect()` on each `addrinfo` until one works, then uses that 26 | /// `addrinfo` to call `init(addrInfo:)`. 27 | init(host: String, port: String) throws { 28 | self = try AddressInfoSequence(forConnectingToHost: host, port: port).withFirstAddrInfo { addrInfo in 29 | try Self.init(addrInfo: addrInfo) 30 | } 31 | } 32 | 33 | /// Connects to `address`. 34 | func connect() throws { 35 | // Note: It would be nice if this method returned a new `ConnectedClientSocketType` that does not allow 36 | // calling `connect()` a second time. `ClientSocketType` could then be a subtype of `AddressSocketType` 37 | // only (without `SendReceiveSocketType`), meaning that you'd first have to create a `ClientSocketType` 38 | // and call `connect()` on it to get back a new `ConnectedClientSocketType` which would then be the only 39 | // one to be able to `send()` and `receive()`. This would also have the nice effect that a socket couldn't 40 | // `send()` or `receive()` before being `connect()`ed. 41 | // 42 | // There are some issues with that approach, though: 43 | // - A mechanism similar to `ServerSocketType.accept()` would be necessary to allow callers to decide 44 | // which concrete adopter of `ConnectedClientSocketType` should be used. This is a bit cumbersome for 45 | // something that isn't strictly necessary here. 46 | // - `self` and the newly created socket would share the same underlying file descriptor, making it 47 | // unclear for the callsite to decide which one should `close()` the socket. 48 | 49 | try address.withSockAddrPointer { sockAddr, length in 50 | try socket.connect(address: sockAddr, length: length) 51 | } 52 | } 53 | 54 | } 55 | 56 | /// A minimal implementation of the `ClientSocketType`. 57 | struct ClientSocket: ClientSocketType { 58 | 59 | let socket: Socket 60 | let address: SocketAddress 61 | 62 | } 63 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/ConnectedClientSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConnectedClientSocketType.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | /// A client socket that was `accept()`ed by a `ServerSocketType`. 10 | protocol ConnectedClientSocketType: AddressSocketType, SendReceiveSocketType { 11 | 12 | } 13 | 14 | /// A minimal implementation of the `ConnectedClientSocketType`. 15 | struct ConnectedClientSocket: ConnectedClientSocketType { 16 | 17 | let socket: Socket 18 | let address: SocketAddress 19 | 20 | } 21 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/ReceiveSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ReceiveSocketType.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// A `SocketType` that can `receive()`. 12 | protocol ReceiveSocketType: SocketType { 13 | 14 | /// Called right before `receive()` will be called on the socket. 15 | /// This is an override point for implementers. The default implementation does nothing. 16 | func willReceive() 17 | 18 | /// Called after `receive()` was called on the socket. 19 | /// This is an override point for implementers. The default implementation does nothing. 20 | func didReceive(buffer: UnsafeMutableBufferPointer, bytesReceived: Int) 21 | 22 | } 23 | 24 | 25 | // Basic functionality. 26 | extension ReceiveSocketType { 27 | 28 | /// Receives data until `Socket.receive()` returns because no more data is available, or because `buffer` is full. 29 | /// 30 | /// - SeeAlso: `Socket.receive(_, flags:, blocking:)` 31 | func receive(buffer: UnsafeMutableBufferPointer, blocking: Bool = false) throws -> Int { 32 | willReceive() 33 | let result = try socket.receive(buffer, blocking: blocking) 34 | didReceive(buffer, bytesReceived: result) 35 | return result 36 | } 37 | 38 | func willReceive() { 39 | // Empty default implementation 40 | } 41 | 42 | func didReceive(buffer: UnsafeMutableBufferPointer, bytesReceived: Int) { 43 | // Empty default implementation 44 | } 45 | 46 | } 47 | 48 | 49 | /// An answer that various `receive()` methods' handler callback can return. 50 | enum ReceiveSocketMessageHandlerResponse { 51 | 52 | /// Continue receiving after the handler callback returns. 53 | case ContinueReceiving 54 | 55 | /// Stop receiving after the handler callback returns. 56 | case StopReceiving 57 | } 58 | 59 | 60 | // Extended functionality that builds upon the basic `receive()` method. 61 | extension ReceiveSocketType { 62 | 63 | /// Receives `maxBytes` or or less. 64 | /// 65 | /// - Note: This method may be slow because it allocates an `UnsafeBufferPointer`, 66 | /// writes into it and then converts the received bytes into an `Array`. 67 | /// 68 | /// - Returns: An `Array` of `Socket.Byte`s whose count is the number of 69 | /// actual bytes received, not `maxBytes`. 70 | /// 71 | /// - SeeAlso: `receive(_:, blocking:)` 72 | func receive(maxBytes maxBytes: Int = 1024, blocking: Bool = false) throws -> [Socket.Byte] { 73 | let pointer = UnsafeMutablePointer.alloc(maxBytes) 74 | let buffer = UnsafeMutableBufferPointer(start: pointer, count: maxBytes) 75 | let bytesReceived = try receive(buffer, blocking: blocking) 76 | let result = Array(buffer[0.. String? { 88 | let buffer = try receive(maxBytes: maxBytes, blocking: blocking) 89 | let string = String(UTF8CodeUnits: buffer) 90 | return string 91 | } 92 | 93 | /// Receives an arbitrary amount of bytes that are terminated by a given `terminator`. 94 | /// 95 | /// This method allocates an internal `Array` that it appends data to as it receives it. 96 | /// After a call to `receive(_, blocking:)` returns, the received data is searched for the `terminator` 97 | /// sequence of bytes and if it was found, the given `messageHandler` is called with that data. 98 | /// 99 | /// - Parameter terminator: A `CollectionType` whose `Element`s are `Socket.Byte`s. 100 | /// - Parameter messageHandler: A callback closure that is called whenever the `terminator` sequence of bytes 101 | /// was found in the received data. The callback's return value determines whether to continue receiving or not. 102 | /// 103 | /// - SeeAlso: `receive(_:, blocking:)` 104 | func receive(terminator terminator: T, blocking: Bool = false, @noescape messageHandler: ArraySlice throws -> ReceiveSocketMessageHandlerResponse) throws { 105 | let terminatorCount = terminator.count 106 | var receivedBytes = Array() 107 | var terminatorEndIndex = 0 108 | 109 | let bufferCount = 1024 110 | let pointer = UnsafeMutablePointer.alloc(bufferCount) 111 | let buffer = UnsafeMutableBufferPointer(start: pointer, count: bufferCount) 112 | // pointer.initializeFrom([Socket.Byte](count: bufferCount, repeatedValue: 0)) 113 | 114 | receiveLoop: while true { 115 | let bytesReceived = try receive(buffer, blocking: blocking) 116 | 117 | let firstSearchRangeStartIndex = max(0, receivedBytes.count - terminatorCount) 118 | receivedBytes.appendContentsOf(buffer[0.. ReceiveSocketMessageHandlerResponse) throws { 149 | return try terminator.withUTF8UnsafeBufferPointer(includeNulTerminator: false) { terminatorBuffer in 150 | try receive(terminator: terminatorBuffer, blocking: blocking) { messageBytes in 151 | let message: String = try messageBytes.withUnsafeBufferPointer { buffer in 152 | guard let message = NSString(bytes: buffer.baseAddress, length: buffer.count, encoding: NSUTF8StringEncoding) else { 153 | throw Socket.Error.ReceivedInvalidData 154 | } 155 | return message as String 156 | } 157 | return try messageHandler(message) 158 | } 159 | } 160 | } 161 | 162 | } 163 | 164 | /// A socket that can receive asynchronously. 165 | protocol ReceiveAsyncSocketType: SocketType { 166 | 167 | } 168 | 169 | extension ReceiveAsyncSocketType { 170 | 171 | /// Creates and returns a `SocketDispatchSource` that calls the given `readyToReceiveHandler` 172 | /// whenever data is available. One of the `receive()` methods can then be called without blocking. 173 | /// 174 | /// To stop receiving data asynchronously, call `cancel()` on the returned `SocketDispatchSource`. 175 | /// This also happens automatically in the `SocketDispatchSource.deinit`. 176 | /// 177 | /// - Returns: A `SocketDispatchSource` that must be held on to by the caller for as long as callbacks are to be received. 178 | func receiveAsync(queue queue: dispatch_queue_t = dispatch_get_global_queue(0, 0), readyToReceiveHandler: () -> Void) -> SocketDispatchSource { 179 | return SocketDispatchSource(socket: socket, queue: queue, eventHandler: readyToReceiveHandler) 180 | } 181 | 182 | } 183 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/SendReceiveSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SendReceiveSocketType.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | /// A socket that can `send()` and `receive()` (synchronously and asynchronously). 10 | /// 11 | /// This is just a grouping of different protocols to allow them being used as a return value. 12 | protocol SendReceiveSocketType: SendSocketType, ReceiveSocketType, ReceiveAsyncSocketType { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/SendSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SendSocketType.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | /// A `SocketType` that can `receive()`. 10 | protocol SendSocketType: SocketType { 11 | 12 | /// Called before `send()` was called on the socket. 13 | /// This is an override point for implementers. The default implementation does nothing. 14 | func willSend(bytes: UnsafeBufferPointer) 15 | 16 | /// Called after `send()` was called on the socket. 17 | /// This is an override point for implementers. The default implementation does nothing. 18 | func didSend(bytes: UnsafeBufferPointer) 19 | 20 | } 21 | 22 | // Basic sending functionality. 23 | extension SendSocketType { 24 | 25 | /// Sends the bytes in the given `buffer`. May call `Socket.send()` repeatedly until all bytes have been sent. 26 | func send(buffer: UnsafeBufferPointer) throws { 27 | let bytesToSend = buffer.count 28 | var bytesSent = 0 29 | 30 | willSend(buffer) 31 | while bytesSent < bytesToSend { 32 | bytesSent += try socket.send(buffer.baseAddress + bytesSent, count: bytesToSend - bytesSent) 33 | } 34 | didSend(buffer) 35 | } 36 | 37 | func willSend(bytes: UnsafeBufferPointer) { 38 | // Empty default implementation 39 | } 40 | 41 | func didSend(bytes: UnsafeBufferPointer) { 42 | // Empty default implementation 43 | } 44 | 45 | } 46 | 47 | 48 | // Extended functionality that builds upon the basic `send()` method. 49 | extension SendSocketType { 50 | 51 | /// Convenience method to send an `Array`. 52 | func send(bytes: [Socket.Byte]) throws { 53 | try bytes.withUnsafeBufferPointer { try send($0) } 54 | } 55 | 56 | /// Convenience method to send an arbitrary `CollectionType` of `Socket.Byte`s. 57 | func send(bytes: T) throws { 58 | try send(Array(bytes)) 59 | } 60 | 61 | /// Convenience method to send a `String`. 62 | /// 63 | /// - Parameter includeNulTerminator: Whether to send a `NUL` terminator (defaults to `false`). 64 | func send(message: String, includeNulTerminator: Bool = false) throws { 65 | try message.withUTF8UnsafeBufferPointer(includeNulTerminator: includeNulTerminator) { try send($0) } 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/ServerSocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServerSocketProtocols.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-04. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | import Foundation 11 | 12 | /// A server socket that can `bind()` to a port, and `listen()` and `accept()` connections from clients. 13 | protocol ServerSocketType: AddressSocketType, AcceptAsyncSocketType { 14 | 15 | } 16 | 17 | 18 | // Convenience initializers. 19 | extension ServerSocketType { 20 | 21 | /// Creates an instance for binding to the given port. 22 | /// 23 | /// - Parameter port: The port for the new server socket. 24 | /// 25 | /// - SeeAlso: `AddressInfoSequence.Storage` 26 | /// 27 | /// - TODO: Don't just pick the first address, but try the whole list for (the first?) one that works. 28 | /// This may require a new initializer like `init(bindingToPort:)` that loops over the 29 | /// `AddressInfoSequence` and calls `bind()` on each `addrinfo` until one works, then uses that 30 | /// `addrinfo` to call `init(addrInfo:)`. 31 | init(port: String) throws { 32 | self = try AddressInfoSequence(forBindingToPort: port).withFirstAddrInfo { addrInfo in 33 | try Self.init(addrInfo: addrInfo) 34 | } 35 | } 36 | 37 | } 38 | 39 | 40 | // Basic server socket methods. 41 | extension ServerSocketType { 42 | 43 | /// Binds the `socket` to the port specified by `address`. 44 | /// 45 | /// - Parameter reuseAddress: Whether to set `SO_REUSEADDR`, which is very 46 | /// likely desired. See `getsockopt(2)` for details. Defaults to `true`. 47 | func bind(reuseAddress reuseAddress: Bool = true) throws { 48 | self[option: SO_REUSEADDR] = reuseAddress ? 1 : 0 49 | try address.withSockAddrPointer { sockAddr, length in 50 | try socket.bind(address: sockAddr, length: length) 51 | } 52 | } 53 | 54 | /// Listen for connections on a socket. 55 | /// 56 | /// - Parameter backlog: The maximum number of client connections that 57 | /// are allowed to queue up for an `accept()` call. Defaults to `10`. 58 | func listen(backlog backlog: Int32 = 10) throws { 59 | try socket.listen(backlog: backlog) 60 | } 61 | 62 | /// Accepts a client connection and returns an instance of the client representation created by the given closure. 63 | /// 64 | /// - Parameter clientConnectedHandler: A closure that is called when a client connection is ready to be accepted. 65 | /// The closure is passed a `Socket` and `SocketAddress` and is expected to return an instance of a server-internal 66 | /// representation of a client connection that conforms to `ConnectedClientSocketType`. This instance is then 67 | /// returned to the caller of `accept()`. 68 | /// 69 | /// - Returns: The server-internal client representation created by `clientConnectedHandler`. 70 | func accept(blocking blocking: Bool = false, @noescape clientConnectedHandler: (socket: Socket, address: SocketAddress) throws -> T) throws -> T { 71 | let (clientSocket, address) = try socket.accept(blocking: blocking) 72 | return try clientConnectedHandler(socket: clientSocket, address: address) 73 | } 74 | 75 | /// Accepts a client connection and returns an instance of a default client representation. 76 | /// 77 | /// - SeeAlso: `accept(blocking:, clientConnectedHandler:)` 78 | func accept(blocking blocking: Bool = false) throws -> ConnectedClientSocketType { 79 | return try accept(blocking: blocking) { socket, address in 80 | return ConnectedClientSocket(socket: socket, address: address) 81 | } 82 | } 83 | 84 | } 85 | 86 | 87 | /// A server socket that can accept asynchronously. 88 | protocol AcceptAsyncSocketType: SocketType { 89 | 90 | } 91 | 92 | extension AcceptAsyncSocketType { 93 | 94 | /// Creates and returns a `SocketDispatchSource` that calls the given `readyToAcceptHandler` 95 | /// whenever data is available. One of the `accept()` methods can then be called without blocking. 96 | /// 97 | /// To stop accepting client connections asynchronously, call `cancel()` on the returned `SocketDispatchSource`. 98 | /// This also happens automatically in the `SocketDispatchSource.deinit`. 99 | /// 100 | /// - Returns: A `SocketDispatchSource` that must be held on to by the caller for as long as callbacks are to be received. 101 | func acceptAsync(queue queue: dispatch_queue_t = dispatch_get_global_queue(0, 0), readyToAcceptHandler: () -> Void) -> SocketDispatchSource { 102 | return SocketDispatchSource(socket: socket, queue: queue, eventHandler: readyToAcceptHandler) 103 | } 104 | 105 | } 106 | 107 | /// A minimal implementation of the `ServerSocketType`. 108 | struct ServerSocket: ServerSocketType { 109 | 110 | let socket: Socket 111 | let address: SocketAddress 112 | 113 | } 114 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/Socket.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Socket.swift 3 | // SocketWrapper 4 | // 5 | // Created by Christian Ludl on 2016-02-09. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | 11 | /// A low-level wrapper around a POSIX socket, i.e. a file descriptor typed as `Int32`. 12 | /// 13 | /// Provides wrapper methods for calling various socket functions that `throw` a `Socket.Error` 14 | /// instead of returning `-1` and setting the global `errno`. 15 | struct Socket { 16 | 17 | typealias Byte = UInt8 18 | 19 | /// The underlying file descriptor. 20 | let fileDescriptor: Int32 21 | 22 | /// Initializer for when a file descriptor exists already. 23 | init(fileDescriptor: Int32) { 24 | self.fileDescriptor = fileDescriptor 25 | } 26 | 27 | /// Initializer for creating a new file descriptor using `Darwin.socket()` using the `addrinfo`. 28 | init(addrInfo: addrinfo) throws { 29 | let fileDescriptor = Darwin.socket(addrInfo.ai_family, addrInfo.ai_socktype, addrInfo.ai_protocol) 30 | guard fileDescriptor != -1 else { 31 | throw Error.CreateFailed(code: errno) 32 | } 33 | self.init(fileDescriptor: fileDescriptor) 34 | } 35 | 36 | } 37 | 38 | 39 | /// Socket errors. 40 | extension Socket { 41 | 42 | /// Most of these errors are thrown whenever a low level socket function returns `-1`. 43 | /// Their associated error code then provides detailed information on the error. 44 | enum Error: ErrorType, CustomStringConvertible { 45 | case BindFailed(code: errno_t) 46 | case CloseFailed(code: errno_t) 47 | case ConnectFailed(code: errno_t) 48 | case ConnectionClosed 49 | case CreateFailed(code: errno_t) 50 | case GetAddrInfoFailed(code: Int32) 51 | case GetNameInfoFailed(code: errno_t) 52 | case GetNameInfoInvalidName 53 | case ListenFailed(code: errno_t) 54 | case NoAddressAvailable 55 | case NoDataAvailable 56 | case ReceivedInvalidData 57 | case ReceiveFailed(code: errno_t) 58 | case SendFailed(code: errno_t) 59 | case AcceptFailed(code: errno_t) 60 | 61 | var description: String { 62 | func errorString(code: errno_t) -> String { 63 | return String(UTF8String: strerror(code))! 64 | } 65 | 66 | switch self { 67 | case .AcceptFailed(let code): 68 | return "accept() failed: " + errorString(code) 69 | 70 | case .BindFailed(let code): 71 | return "bind() failed: " + errorString(code) 72 | 73 | case .CloseFailed(let code): 74 | return "close() failed: " + errorString(code) 75 | 76 | case .ConnectionClosed: 77 | return "Connection closed." 78 | 79 | case .ConnectFailed(let code): 80 | return "connect() failed: " + errorString(code) 81 | 82 | case .CreateFailed(let code): 83 | return "socket() failed: " + errorString(code) 84 | 85 | case .GetAddrInfoFailed(let code): 86 | return "getaddrinfo() failed: " + String(UTF8String: gai_strerror(code))! 87 | 88 | case .GetNameInfoFailed(let code): 89 | return "getnameinfo() failed: " + errorString(code) 90 | 91 | case .GetNameInfoInvalidName: 92 | return "getnameinfo() returned invalid name." 93 | 94 | case .ListenFailed(let code): 95 | return "listen() failed: " + errorString(code) 96 | 97 | case .NoAddressAvailable: 98 | return "getaddrinfo() returned no address." 99 | 100 | case .NoDataAvailable: 101 | return "No data available" 102 | 103 | case .SendFailed(let code): 104 | return "send() failed: " + errorString(code) 105 | 106 | case .ReceivedInvalidData: 107 | return "Received invalid data" 108 | 109 | case .ReceiveFailed(let code): 110 | return "recv() failed: " + errorString(code) 111 | } 112 | } 113 | } 114 | 115 | } 116 | 117 | 118 | /// Sending data. 119 | extension Socket { 120 | 121 | /// Sends the data in the given `buffer`. 122 | /// 123 | /// - SeeAlso: `send(2)` 124 | func send(buffer: UnsafeBufferPointer, flags: Int32 = 0) throws -> Int { 125 | let result = Darwin.send(fileDescriptor, buffer.baseAddress, buffer.count, flags) 126 | guard result != -1 else { 127 | throw Error.SendFailed(code: errno) 128 | } 129 | return result 130 | } 131 | 132 | /// Sends the chunk of data defined by `pointer` and `count`. 133 | func send(pointer: UnsafePointer, count: Int, flags: Int32 = 0) throws -> Int { 134 | return try send(UnsafeBufferPointer(start: pointer, count: count), flags: flags) 135 | } 136 | 137 | } 138 | 139 | 140 | // Receiving data. 141 | extension Socket { 142 | 143 | /// Receives data into `buffer`. 144 | /// 145 | /// - Parameter buffer: A previously allocated buffer that this method writes into. 146 | /// - Parameter flags: Flags that are passed to `Darwin.recv()`. 147 | /// - Parameter blocking: If no data is available and... 148 | /// - `blocking` is `true`: blocks until any data is available. 149 | /// - `blocking` is `false`: throws `Socket.Error.NoDataAvailable`. 150 | /// 151 | /// - SeeAlso: `recv(2)` 152 | func receive(buffer: UnsafeMutableBufferPointer, flags: Int32 = 0, blocking: Bool = false) throws -> Int { 153 | self[fileOption: O_NONBLOCK] = !blocking 154 | let bytesReceived = Darwin.recv(fileDescriptor, buffer.baseAddress, buffer.count, flags) 155 | guard bytesReceived != -1 else { 156 | switch errno { 157 | case EAGAIN: 158 | throw Error.NoDataAvailable 159 | 160 | case let error: 161 | throw Error.ReceiveFailed(code: error) 162 | } 163 | } 164 | guard bytesReceived != 0 else { 165 | throw Error.ConnectionClosed 166 | } 167 | return bytesReceived 168 | } 169 | 170 | /// Receives a chunk of data to `pointer` with a maximum of `count`. 171 | func receive(pointer: UnsafeMutablePointer, count: Int, flags: Int32 = 0, blocking: Bool = false) throws -> Int { 172 | return try receive(UnsafeMutableBufferPointer(start: pointer, count: count), flags: flags, blocking: blocking) 173 | } 174 | 175 | } 176 | 177 | 178 | /// Closing the socket. 179 | extension Socket { 180 | 181 | /// Closes the socket. 182 | /// 183 | /// - SeeAlso: `close(2)` 184 | func close() throws { 185 | guard Darwin.close(fileDescriptor) != -1 else { 186 | throw Error.CloseFailed(code: errno) 187 | } 188 | } 189 | 190 | } 191 | 192 | 193 | /// Server socket methods. 194 | extension Socket { 195 | 196 | /// Binds the given address to the server socket. 197 | /// 198 | /// - SeeAlso: `bind(2)` 199 | func bind(address address: UnsafePointer, length: socklen_t) throws { 200 | guard Darwin.bind(fileDescriptor, address, length) != -1 else { 201 | throw Error.BindFailed(code: errno) 202 | } 203 | } 204 | 205 | /// Starts listening for client connections on the server socket with type `SOCK_STREAM` (i.e. TCP). 206 | /// 207 | /// - SeeAlso: `listen(2)` 208 | func listen(backlog backlog: Int32) throws { 209 | guard Darwin.listen(fileDescriptor, backlog) != -1 else { 210 | throw Error.ListenFailed(code: errno) 211 | } 212 | } 213 | 214 | /// Accept a connection on the server socket and return. If no new client has connected and... 215 | /// - `blocking` is `true`: blocks until a client connects. 216 | /// - `blocking` is `false`: throws `Socket.Error.NoDataAvailable`. 217 | /// 218 | /// - SeeAlso: `accept(2)` 219 | func accept(blocking blocking: Bool = false) throws -> (Socket, SocketAddress) { 220 | self[fileOption: O_NONBLOCK] = !blocking 221 | 222 | var clientFileDescriptor: Int32 = 0 223 | let socketAddress = try SocketAddress() { sockaddr, length in 224 | clientFileDescriptor = Darwin.accept(fileDescriptor, sockaddr, length) 225 | guard clientFileDescriptor != -1 else { 226 | switch errno { 227 | case EAGAIN: 228 | throw Error.NoDataAvailable 229 | 230 | case let error: 231 | throw Error.AcceptFailed(code: error) 232 | } 233 | } 234 | } 235 | return (Socket(fileDescriptor: clientFileDescriptor), socketAddress) 236 | } 237 | 238 | } 239 | 240 | 241 | /// Client socket methods. 242 | extension Socket { 243 | 244 | /// Connects the socket to a peer. 245 | /// 246 | /// - SeeAlso: `connect(2)` 247 | func connect(address address: UnsafePointer, length: socklen_t) throws { 248 | guard Darwin.connect(fileDescriptor, address, length) != -1 else { 249 | throw Error.ConnectFailed(code: errno) 250 | } 251 | } 252 | 253 | } 254 | 255 | /// Subscripts. 256 | extension Socket { 257 | 258 | /// A wrapper around `getsockopt()` and `setsockopt` with a level of `SOL_SOCKET`. 259 | /// 260 | /// - SeeAlso: `getsockopt(2)` 261 | /// 262 | /// - This should probably be a method that can throw. 263 | subscript(socketOption option: Int32) -> Int32 { 264 | 265 | get { 266 | var value: Int32 = 0 267 | var valueLength = socklen_t(sizeofValue(value)) 268 | 269 | guard getsockopt(fileDescriptor, SOL_SOCKET, option, &value, &valueLength) != -1 else { 270 | let errorNumber = errno 271 | print("getsockopt() failed for option \(option). \(errorNumber) \(strerror(errorNumber))") 272 | return 0 273 | } 274 | 275 | return value 276 | } 277 | 278 | nonmutating set { 279 | var value = newValue 280 | 281 | guard setsockopt(fileDescriptor, SOL_SOCKET, option, &value, socklen_t(sizeofValue(value))) != -1 else { 282 | let errorNumber = errno 283 | print("setsockopt() failed for option \(option), value \(value). \(errorNumber) \(strerror(errorNumber))") 284 | return 285 | } 286 | } 287 | 288 | } 289 | 290 | /// A wrapper around `fcntl()` for the `F_GETFL` and `F_SETFL` commands. 291 | /// 292 | /// - SeeAlso: `fcntl(2)` 293 | /// 294 | /// - This should probably be a method that can throw. 295 | subscript(fileOption option: Int32) -> Bool { 296 | 297 | get { 298 | let allFlags = fcntl(fileDescriptor, F_GETFL) 299 | guard allFlags != -1 else { 300 | let errorNumber = errno 301 | print("fcntl() failed for F_GETFL, option: \(option). \(errorNumber) \(strerror(errorNumber))") 302 | return false 303 | } 304 | 305 | return (allFlags & option) != 0 306 | } 307 | 308 | nonmutating set { 309 | var flags = fcntl(fileDescriptor, F_GETFL) 310 | guard flags != -1 else { 311 | let errorNumber = errno 312 | print("fcntl() failed for F_GETFL, option: \(option). \(errorNumber) \(strerror(errorNumber))") 313 | return 314 | } 315 | 316 | if newValue { 317 | flags |= option 318 | } else { 319 | flags &= ~option 320 | } 321 | 322 | guard fcntl(fileDescriptor, F_SETFL, flags) != -1 else { 323 | let errorNumber = errno 324 | print("fcntl() failed for F_SETFL, option: \(option). \(errorNumber) \(strerror(errorNumber))") 325 | return 326 | } 327 | } 328 | 329 | } 330 | 331 | } 332 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/SocketAddress.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocketAddress.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-04. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | 11 | /// A wrapper around the `sockaddr`, `sockaddr_in`, and `sockaddr_in6` family of structs. 12 | /// 13 | /// It provides storage for any socket address and implements methods that allow using that 14 | /// storage as a pointer to a "generic" `sockaddr` struct. 15 | enum SocketAddress { 16 | 17 | /// An IPv4 address represented by a `sockaddr_in`. 18 | case Version4(address: sockaddr_in) 19 | 20 | /// An IPv6 address represented by a `sockaddr_in6`. 21 | case Version6(address: sockaddr_in6) 22 | 23 | /// The length of a `sockaddr_in` as the appropriate type for low-level APIs. 24 | static var lengthOfVersion4: socklen_t { 25 | return socklen_t(sizeof(sockaddr_in)) 26 | } 27 | 28 | /// The length of a `sockaddr_in6` as the appropriate type for low-level APIs. 29 | static var lengthOfVersion6: socklen_t { 30 | return socklen_t(sizeof(sockaddr_in6)) 31 | } 32 | 33 | /// Creates either a `Version4` or `Version6` socket address, depending on what `addressProvider` does. 34 | /// 35 | /// This initializer calls the given `addressProvider` with an `UnsafeMutablePointer` that points to a buffer 36 | /// that can hold either a `sockaddr_in` or a `sockaddr_in6`. After `addressProvider` returns, the pointer is 37 | /// expected to contain an address. For that address, a `SocketAddress` is then created. 38 | /// 39 | /// This initializer is intended to be used with `Darwin.accept()`. 40 | /// 41 | /// - Parameter addressProvider: A closure that will be called and is expected to fill in an address into the given buffer. 42 | init(@noescape addressProvider: (UnsafeMutablePointer, UnsafeMutablePointer) throws -> Void) throws { 43 | 44 | // `sockaddr_storage` is an abstract type that provides storage large enough for any concrete socket address struct: 45 | var addressStorage = sockaddr_storage() 46 | var addressStorageLength = socklen_t(sizeofValue(addressStorage)) 47 | try withUnsafeMutablePointers(&addressStorage, &addressStorageLength) { 48 | try addressProvider(UnsafeMutablePointer($0), $1) 49 | } 50 | 51 | switch Int32(addressStorage.ss_family) { 52 | case AF_INET: 53 | assert(socklen_t(addressStorage.ss_len) == SocketAddress.lengthOfVersion4) 54 | self = withUnsafePointer(&addressStorage) { .Version4(address: UnsafePointer($0).memory) } 55 | 56 | case AF_INET6: 57 | assert(socklen_t(addressStorage.ss_len) == SocketAddress.lengthOfVersion6) 58 | self = withUnsafePointer(&addressStorage) { .Version6(address: UnsafePointer($0).memory) } 59 | 60 | default: 61 | throw Socket.Error.NoAddressAvailable 62 | } 63 | } 64 | 65 | /// Creates an instance by inspecting the given `addrinfo`'s protocol family and socket address. 66 | /// 67 | /// - Important: The given `addrinfo` must contain either an IPv4 or IPv6 address. 68 | init(addrInfo: addrinfo) { 69 | switch addrInfo.ai_family { 70 | case AF_INET: 71 | assert(addrInfo.ai_addrlen == SocketAddress.lengthOfVersion4) 72 | self = .Version4(address: UnsafePointer(addrInfo.ai_addr).memory) 73 | 74 | case AF_INET6: 75 | assert(addrInfo.ai_addrlen == SocketAddress.lengthOfVersion6) 76 | self = .Version6(address: UnsafePointer(addrInfo.ai_addr).memory) 77 | 78 | default: 79 | fatalError("Unknown address size") 80 | } 81 | } 82 | 83 | /// Creates an instance for a given IPv4 socket address. 84 | init(address: sockaddr_in) { 85 | self = .Version4(address: address) 86 | } 87 | 88 | /// Creates an instance for a given IPv6 socket address. 89 | init(address: sockaddr_in6) { 90 | self = .Version6(address: address) 91 | } 92 | 93 | /// Makes a copy of `address` and calls the given closure with an `UnsafePointer` to that. 94 | func withSockAddrPointer(@noescape body: (UnsafePointer, socklen_t) throws -> Result) rethrows -> Result { 95 | 96 | func castAndCall(address: T, @noescape _ body: (UnsafePointer, socklen_t) throws -> Result) rethrows -> Result { 97 | var localAddress = address // We need a `var` here for the `&`. 98 | return try withUnsafePointer(&localAddress) { 99 | try body(UnsafePointer($0), socklen_t(sizeof(T))) 100 | } 101 | } 102 | 103 | switch self { 104 | case .Version4(let address): 105 | return try castAndCall(address, body) 106 | 107 | case .Version6(let address): 108 | return try castAndCall(address, body) 109 | } 110 | } 111 | 112 | /// Returns the host and port as returned by `getnameinfo()`. 113 | func nameInfo() throws -> (host: String, port: String) { 114 | var hostBuffer = [CChar](count:256, repeatedValue:0) 115 | var portBuffer = [CChar](count:256, repeatedValue:0) 116 | 117 | let result = withSockAddrPointer { sockAddr, length in 118 | Darwin.getnameinfo(sockAddr, length, &hostBuffer, socklen_t(hostBuffer.count), &portBuffer, socklen_t(portBuffer.count), 0) 119 | } 120 | 121 | guard result != -1 else { 122 | throw Socket.Error.GetNameInfoFailed(code: errno) 123 | } 124 | 125 | guard let host = String(UTF8String: hostBuffer) else { 126 | throw Socket.Error.GetNameInfoInvalidName 127 | } 128 | 129 | guard let port = String(UTF8String: portBuffer) else { 130 | throw Socket.Error.GetNameInfoInvalidName 131 | } 132 | 133 | return (host, port) 134 | } 135 | 136 | #if false // Doesn't work yet. 137 | var displayName: String { 138 | func createDisplayName(address address:UnsafePointer , family: Int32, maxLength: Int32) -> String { 139 | let pointer = UnsafeMutablePointer.alloc(Int(maxLength)) 140 | guard inet_ntop(family, address, pointer, socklen_t(maxLength)) != nil else { 141 | fatalError("Error converting IP address to displayName") 142 | } 143 | guard let displayName = String.fromCString(pointer) else { 144 | fatalError("Error converting IP address to displayName") 145 | } 146 | return displayName 147 | } 148 | 149 | switch self { 150 | case .Version4(var address): 151 | return createDisplayName(address: &address, family: AF_INET, maxLength: INET_ADDRSTRLEN) 152 | 153 | case .Version6(var address): 154 | return createDisplayName(address: &address, family: AF_INET6, maxLength: INET6_ADDRSTRLEN) 155 | } 156 | } 157 | #endif 158 | 159 | } 160 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/SocketDispatchSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SocketDispatchSource.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-04. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// A wrapper around a `dispatch_source_t` that takes care of calling `dispatch_source_cancel()` in `deinit`. 12 | /// 13 | /// This class is used for managing the lifetime of asynchronous callbacks in the `ReceiveAsyncSocketType` 14 | /// and `AcceptAsyncSocketType` protocols. 15 | class SocketDispatchSource { 16 | 17 | /// The managed dispatch source. 18 | private var _dispatchSource: dispatch_source_t? 19 | 20 | /// Creates an instance for the given `Socket`. 21 | /// 22 | /// The caller is responsible for keeping a reference to the new instance. If the instance is deinit'ed, 23 | /// the internal `dispatch_source_t` is cancelled and `eventHandler` won't be called anymore. 24 | /// 25 | /// - Parameter socket: The socket for which to create the dispatch source. 26 | /// - Parameter queue: The dispatch queue on which `eventHandler` should be called. 27 | /// - Parameter eventHandler: The closure to call whenever the dispatch source fires, that is: 28 | /// - For a socket that can `receive()`, whenever the peer sent data and the socket is ready to be `received()` from. 29 | /// - For a server socket, whenever a client has connected and its connection is ready to be `accept()`ed. 30 | init(socket: Socket, queue: dispatch_queue_t, eventHandler: () -> Void) { 31 | let dispatchSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, UInt(socket.fileDescriptor), 0, queue) 32 | self._dispatchSource = dispatchSource 33 | dispatch_source_set_event_handler(dispatchSource, eventHandler) 34 | dispatch_resume(dispatchSource) 35 | } 36 | 37 | deinit { 38 | cancel() 39 | } 40 | 41 | /// Cancels the dispatch source, therefore stopping calling the `eventHandler`. 42 | /// Called automatically in `deinit`. 43 | func cancel() { 44 | if let dispatchSource = _dispatchSource { 45 | dispatch_source_cancel(dispatchSource) 46 | _dispatchSource = nil 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /SocketWrapper/SocketWrapper/SocketType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CommonSocketProtocols.swift 3 | // SocketWrapper 4 | // 5 | // Created by Marco Masser on 2016-03-04. 6 | // Copyright © 2016 Objective Development. All rights reserved. 7 | // 8 | 9 | import Darwin 10 | 11 | /// Represents the base type for more specialized socket types. 12 | protocol SocketType { 13 | 14 | /// The underlying `Socket` that is used for various methods of the sub types, e.g. `send()`, `receive()`. etc. 15 | var socket: Socket { get } 16 | 17 | /// Called whenever `close()` was called on the socket. 18 | /// This is an override point for implementers. The default implementation does nothing. 19 | func didClose() 20 | 21 | } 22 | 23 | 24 | // Common methods. 25 | extension SocketType { 26 | 27 | /// Closes the socket and calls `didClose()`. 28 | func close() throws { 29 | defer { 30 | didClose() 31 | } 32 | try socket.close() 33 | } 34 | 35 | func didClose() { 36 | // Empty default implementation 37 | } 38 | 39 | /// Pass through to `Socket`'s `socketOption` subscript. 40 | subscript(option option: Int32) -> Int32 { 41 | get { 42 | return socket[socketOption: option] 43 | } 44 | nonmutating set { 45 | socket[socketOption: option] = newValue 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /client/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // client 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development Software GmbH. All rights reserved. 7 | // 8 | 9 | struct SimpleClientSocket: ClientSocketType { 10 | let socket: Socket 11 | let address: SocketAddress 12 | } 13 | 14 | do { 15 | let socket = try SimpleClientSocket(host: "127.0.0.1", port: "1234") 16 | try socket.connect() 17 | try socket.send("Hello, Server!") 18 | if let string = try socket.receiveUTF8String(blocking: true) { 19 | print("Server said: \(string)") 20 | } 21 | } catch { 22 | print("Error: \(error)") 23 | } 24 | -------------------------------------------------------------------------------- /server/main.swift: -------------------------------------------------------------------------------- 1 | // 2 | // main.swift 3 | // server 4 | // 5 | // Created by Marco Masser on 2016-03-11. 6 | // Copyright © 2016 Objective Development Software GmbH. All rights reserved. 7 | // 8 | 9 | struct SimpleServerSocket: ServerSocketType { 10 | let socket: Socket 11 | let address: SocketAddress 12 | } 13 | 14 | do { 15 | let port = "1234" 16 | let socket = try SimpleServerSocket(port: port) 17 | try socket.bind(reuseAddress: true) 18 | try socket.listen() 19 | 20 | print("Listening on port \(port)") 21 | while true { 22 | let clientSocket = try socket.accept(blocking: true) 23 | print("Client connected, sending string and closing connection") 24 | try clientSocket.send("Hello, Client!\n") 25 | try clientSocket.close() 26 | } 27 | } catch { 28 | print("Error: \(error)") 29 | } 30 | --------------------------------------------------------------------------------