├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── FastServerSocket.h ├── FastServerSocket.m ├── FastSocket.h ├── FastSocket.m ├── FastSocket.podspec ├── FastSocket.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── FastSocketTest.m ├── LICENSE ├── README.md └── UnitTests-Info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | _Store 2 | */build/* 3 | *.pbxuser 4 | !default.pbxuser 5 | *.mode1v3 6 | !default.mode1v3 7 | *.mode2v3 8 | !default.mode2v3 9 | *.perspectivev3 10 | !default.perspectivev3 11 | xcuserdata 12 | profile 13 | *.moved-aside 14 | DerivedData 15 | .idea/ 16 | *.hmap 17 | *.xccheckout 18 | 19 | #CocoaPods 20 | Pods 21 | 22 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | FastSocket 2 | =============== 3 | 4 | Release Notes 5 | --------------- 6 | 2017 Nov 01 — [v1.6](https://github.com/dreese/FastSocket/releases/tag/v1.6) 7 | 8 | • Fixed a long-standing bug where the internal socket descriptor could become closed but not zeroed out. 9 | 10 | 2017 Oct 28 — [v1.5](https://github.com/dreese/FastSocket/releases/tag/v1.5) 11 | 12 | • Annotated code to improve auto-generated Swift interface. 13 | • Fixed several documentation issues. 14 | • Added document for contributions. 15 | 16 | 2017 Oct 06 — [v1.4](https://github.com/dreese/FastSocket/releases/tag/v1.4) 17 | 18 | • Changed -[FaskSocket timeout] and -[FaskSocket setTimeout:] methods so that the timeout value is a float, in order to handle sub-second values. 19 | • Fixed a compatibility issue with Xcode 9. 20 | • Added several unit tests. 21 | 22 | 2015 Jan 27 — [v1.3](https://github.com/dreese/FastSocket/releases/tag/v1.3) 23 | 24 | • Changed -[FaskSocket sendBytes:count:] method to return the actual number of bytes received instead of a BOOL. Now it matches the Readme. 25 | • Fixed a compiler warning caused by returning NO instead of nil from one of the init methods. 26 | • Added several unit tests. 27 | 28 | 2014 Feb 03 — [v1.2](https://github.com/dreese/FastSocket/releases/tag/v1.2) 29 | 30 | • Added -[FastSocket connect:] method for specifying a connection timeout, which is separate from the read/write timeout. 31 | • Added CocoaPod support with new podspec file. 32 | 33 | 2013 Oct 03 — [v1.1](https://github.com/dreese/FastSocket/releases/tag/v1.1) 34 | 35 | • Converted to ARC. 36 | • Added -[FastSocket isConnected] method. 37 | • Added -[FastSocket receiveBytes:count:] method for receiving an exact number of bytes. This differs from -[FastSocket receiveBytes:limit:] in that the new method waits for the given number of bytes is received, or a timeout, before returning. 38 | • Added header documentation for use in Xcode 5. 39 | 40 | 2012 Jun 24 — [v1.0](https://github.com/dreese/FastSocket/releases/tag/v1.0) 41 | 42 | • Initial release. 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | This document contains information and guidelines about contributing to this project. 4 | Please read it before you start participating. 5 | 6 | ## Reporting Issues 7 | 8 | A great way to contribute to the project is to send a detailed issue when you encounter an problem. 9 | 10 | Check that the project issues database doesn't already include that problem or suggestion before submitting an issue. 11 | If you find a match, add a quick "+1" or "I have this problem too" which helps me prioritize the most common problems and requests. 12 | 13 | When reporting issues, please include the following. This information will help me review and fix your issue faster. 14 | 15 | * The version of Xcode you are using 16 | * The version of iOS or macOS you are targeting 17 | * The full output of any stack trace or compiler error 18 | * A code snippet that reproduces the described behavior, if applicable 19 | * Any other details that would be useful in understanding the problem 20 | 21 | If you do not hear back within a few days, please feel free to reach out directly. 22 | Mentioning me in a comment will get my attention. 23 | 24 | ## Submitting Pull Requests 25 | 26 | Pull requests are welcome, and greatly encouraged. 27 | When submitting a pull request, please create proper test cases demonstrating the issue to be fixed or the new feature. 28 | 29 | ## Developer's Certificate of Origin 1.1 30 | 31 | By making a contribution to this project, I certify that: 32 | 33 | - (a) The contribution was created in whole or in part by me and I 34 | have the right to submit it under the open source license 35 | indicated in the file; or 36 | 37 | - (b) The contribution is based upon previous work that, to the best 38 | of my knowledge, is covered under an appropriate open source 39 | license and I have the right under that license to submit that 40 | work with modifications, whether created in whole or in part 41 | by me, under the same open source license (unless I am 42 | permitted to submit under a different license), as indicated 43 | in the file; or 44 | 45 | - (c) The contribution was provided directly to me by some other 46 | person who certified (a), (b) or (c) and I have not modified 47 | it. 48 | 49 | - (d) I understand and agree that this project and the contribution 50 | are public and that a record of the contribution (including all 51 | personal information I submit with it, including my sign-off) is 52 | maintained indefinitely and may be redistributed consistent with 53 | this project or the open source license(s) involved. 54 | -------------------------------------------------------------------------------- /FastServerSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // FastServerSocket.h 3 | // Copyright (c) 2011-2013 Daniel Reese 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | @class FastSocket; 27 | @interface FastServerSocket : NSObject { 28 | @protected 29 | long timeout; 30 | } 31 | 32 | #pragma mark - Properties 33 | 34 | /** 35 | The file descriptor used to communicate to the remote machine. 36 | */ 37 | @property (nonatomic, readonly) int sockfd; 38 | 39 | /** 40 | The port number of the local machine on which to listen for incoming connections. 41 | */ 42 | @property (nonatomic, readonly) NSString *port; 43 | 44 | /** 45 | The last error that occured. This value is not set to nil after a successful call, so it is not 46 | appropriate to test this value to check for error conditions. Check for a NO or nil return value. 47 | */ 48 | @property (nonatomic, readonly) NSError *lastError; 49 | 50 | #pragma mark - Initializers 51 | 52 | /** 53 | Returns an initialized FastSocketSocket object configured to connect to the given host name and port number. 54 | 55 | @param port The port number on which to connect. 56 | @return An initialized FastSocketSocket object configured to connect to the given host name and port number. 57 | */ 58 | - (id)initWithPort:(NSString *)port __attribute__((nonnull)); 59 | 60 | /** 61 | Returns an initialized FastSocketSocket object configured to communicate throught the given file descriptor. 62 | This method is primary used by a server socket to receive an incoming connection. 63 | 64 | @param fd The file descriptor to use for communication. 65 | @return An initialized FastSocketSocket object configured to communicate throught the given file descriptor. 66 | */ 67 | - (id)initWithFileDescriptor:(int)fd; 68 | 69 | #pragma mark Actions 70 | 71 | /** 72 | Starts listening for incoming connections. 73 | 74 | @return YES if the listen succeeded, NO otherwise. 75 | */ 76 | - (BOOL)listen; 77 | 78 | /** 79 | Accepts an incoming connection from a remote host. Blocks until a connection is received. 80 | 81 | @return A socket connected to the remote host. 82 | */ 83 | - (FastSocket *)accept; 84 | 85 | /** 86 | Closes the connection to the remote host. 87 | 88 | @return YES if the close succeeded, NO otherwise. 89 | */ 90 | - (BOOL)close; 91 | 92 | #pragma mark Settings 93 | 94 | /** 95 | Returns the number of seconds to wait without any network activity before giving up and 96 | returning an error. The default is zero seconds, in which case it will never time out. 97 | 98 | @return The current timeout value in seconds. 99 | */ 100 | - (long)timeout; 101 | 102 | /** 103 | Sets the number of seconds to wait without any network activity before giving up and 104 | returning an error. The default is zero seconds, in which case it will never time out. 105 | 106 | @param seconds The number of seconds to wait before timing out. 107 | @return YES if the timeout value was set successfully, NO otherwise. 108 | */ 109 | - (BOOL)setTimeout:(long)seconds; 110 | 111 | @end 112 | -------------------------------------------------------------------------------- /FastServerSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // FastServerSocket.m 3 | // Copyright (c) 2011-2013 Daniel Reese 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | 25 | #import "FastServerSocket.h" 26 | #import "FastSocket.h" 27 | #include 28 | #include 29 | #include 30 | 31 | @implementation FastServerSocket 32 | 33 | @synthesize sockfd; 34 | @synthesize port; 35 | @synthesize lastError; 36 | 37 | - (id)initWithPort:(NSString *)localPort { 38 | if ((self = [super init])) { 39 | port = [localPort copy]; 40 | } 41 | return self; 42 | } 43 | 44 | - (id)initWithFileDescriptor:(int)fd { 45 | if ((self = [super init])) { 46 | struct sockaddr_storage currentSocket; 47 | 48 | int error = getsockname(fd, (struct sockaddr *)¤tSocket, &(socklen_t){sizeof(currentSocket)}); 49 | if(error){ 50 | lastError = NEW_ERROR(error, gai_strerror(error)); 51 | return nil; 52 | } 53 | 54 | sockfd = fd; 55 | 56 | if(currentSocket.ss_family == AF_INET6){ 57 | struct sockaddr_in6 *currentSocketIn6 = (struct sockaddr_in6 *)¤tSocket; 58 | port = [NSString stringWithFormat:@"%i", currentSocketIn6->sin6_port]; 59 | } 60 | 61 | else if(currentSocket.ss_family == AF_INET){ 62 | struct sockaddr_in *currentSocketIn = (struct sockaddr_in *)¤tSocket; 63 | port = [NSString stringWithFormat:@"%i", currentSocketIn->sin_port]; 64 | } 65 | 66 | else{ 67 | return nil; 68 | } 69 | } 70 | return self; 71 | } 72 | 73 | - (void)dealloc { 74 | [self close]; 75 | } 76 | 77 | #pragma mark Actions 78 | 79 | - (BOOL)listen { 80 | struct addrinfo hints, *serverinfo, *p; 81 | 82 | bzero(&hints, sizeof(hints)); 83 | hints.ai_family = AF_UNSPEC; 84 | hints.ai_socktype = SOCK_STREAM; 85 | hints.ai_flags = AI_PASSIVE; 86 | 87 | int error = getaddrinfo(NULL, [port UTF8String], &hints, &serverinfo); 88 | if (error) { 89 | lastError = NEW_ERROR(error, gai_strerror(error)); 90 | return NO; 91 | } 92 | 93 | // Loop through the results and bind to the first we can. 94 | @try { 95 | for (p = serverinfo; p != NULL; p = p->ai_next) { 96 | if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) { 97 | lastError = NEW_ERROR(errno, strerror(errno)); 98 | return NO; 99 | } 100 | 101 | // Reuse local address if it still exists. 102 | if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &(int){1}, sizeof(int)) < 0) { 103 | lastError = NEW_ERROR(errno, strerror(errno)); 104 | return NO; 105 | } 106 | 107 | // Bind the socket. 108 | if (bind(sockfd, p->ai_addr, p->ai_addrlen) < 0) { 109 | close(sockfd); 110 | continue; 111 | } 112 | 113 | // Set timeout if requested. 114 | if (timeout && ![self setTimeout:timeout]) { 115 | return NO; 116 | } 117 | 118 | // Found a working address, so move on. 119 | break; 120 | } 121 | if (p == NULL) { 122 | lastError = NEW_ERROR(errno, strerror(errno)); 123 | return NO; 124 | } 125 | } 126 | @finally { 127 | freeaddrinfo(serverinfo); // All done with this structure. 128 | } 129 | 130 | if (listen(sockfd, 10) == -1) { 131 | lastError = NEW_ERROR(errno, strerror(errno)); 132 | return NO; 133 | } 134 | return YES; 135 | } 136 | 137 | - (BOOL)close { 138 | if (sockfd > 0 && close(sockfd) < 0) { 139 | lastError = NEW_ERROR(errno, strerror(errno)); 140 | return NO; 141 | } 142 | sockfd = 0; 143 | return YES; 144 | } 145 | 146 | - (FastSocket *)accept { 147 | struct sockaddr_storage remoteAddr; 148 | int clientfd = accept(sockfd, (struct sockaddr *)&remoteAddr, &(socklen_t){sizeof(remoteAddr)}); 149 | if (clientfd == -1) { 150 | lastError = NEW_ERROR(errno, strerror(errno)); 151 | return nil; 152 | } 153 | return [[FastSocket alloc] initWithFileDescriptor:clientfd]; 154 | } 155 | 156 | #pragma mark Settings 157 | 158 | - (long)timeout { 159 | if (sockfd > 0) { 160 | struct timeval tv; 161 | if (getsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, &(socklen_t){sizeof(tv)}) < 0) { 162 | lastError = NEW_ERROR(errno, strerror(errno)); 163 | return NO; 164 | } 165 | timeout = tv.tv_sec; 166 | } 167 | return timeout; 168 | } 169 | 170 | - (BOOL)setTimeout:(long)seconds { 171 | if (sockfd > 0) { 172 | struct timeval tv = {seconds, 0}; 173 | if (setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 || setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { 174 | lastError = NEW_ERROR(errno, strerror(errno)); 175 | return NO; 176 | } 177 | } 178 | timeout = seconds; 179 | return YES; 180 | } 181 | 182 | @end 183 | -------------------------------------------------------------------------------- /FastSocket.h: -------------------------------------------------------------------------------- 1 | // 2 | // FastSocket.h 3 | // Copyright (c) 2011-2013 Daniel Reese 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #import 25 | 26 | 27 | #define NEW_ERROR(num, str) [[NSError alloc] initWithDomain:@"FastSocketErrorDomain" code:(num) userInfo:[NSDictionary dictionaryWithObject:[NSString stringWithFormat:@"%s", (str)] forKey:NSLocalizedDescriptionKey]] 28 | 29 | NS_ASSUME_NONNULL_BEGIN 30 | @interface FastSocket : NSObject 31 | 32 | #pragma mark - Properties 33 | 34 | /** 35 | The file descriptor used to communicate to the remote machine. 36 | */ 37 | @property (nonatomic, readonly) int sockfd; 38 | 39 | /** 40 | The host name of the remote machine. 41 | */ 42 | @property (nonatomic, readonly) NSString *host; 43 | 44 | /** 45 | The port number of the remote machine. 46 | */ 47 | @property (nonatomic, readonly) NSString *port; 48 | 49 | /** 50 | The last error that occured. This value is not set to nil after a successful call, so it is not 51 | appropriate to test this value to check for error conditions. Check for a NO or nil return value. 52 | */ 53 | @property (nullable, nonatomic, readonly) NSError *lastError; 54 | 55 | #pragma mark - Initializers 56 | 57 | /** 58 | Returns an initialized FastSocket object configured to connect to the given host name and port number. 59 | 60 | @param host The host name of the remote host. 61 | @param port The port number on which to connect. 62 | @return An initialized FastSocket object configured to connect to the given host name and port number. 63 | */ 64 | - (nullable instancetype)initWithHost:(NSString *)host andPort:(NSString *)port; 65 | 66 | /** 67 | Returns an initialized FastSocket object configured to communicate throught the given file descriptor. 68 | This method is used primarily by a server socket to receive an incoming connection. 69 | 70 | @param fd The file descriptor to use for communication. 71 | @return An initialized FastSocket object configured to communicate throught the given file descriptor. 72 | */ 73 | - (nullable instancetype)initWithFileDescriptor:(int)fd; 74 | 75 | /** 76 | Retrieves the internal buffer and its size for use outside the class. This buffer is good 77 | to use for sending and receiving bytes because it is a multiple of the segment size and 78 | allocated so that it is aligned on a memory page boundary. 79 | */ 80 | - (void)buffer:(void * _Nonnull * _Nonnull)buf size:(long *)size; 81 | 82 | #pragma mark - Actions 83 | 84 | /** 85 | Connect the socket to the remote host. 86 | 87 | @return YES if the connection succeeded, NO otherwise. 88 | */ 89 | - (BOOL)connect; 90 | 91 | /** 92 | Connect the socket to the remote host with the given timeout value. 93 | 94 | @param timeout The maximum amount of time to wait for a connection to succeed. 95 | @return YES if the connection succeeded, NO otherwise. 96 | */ 97 | - (BOOL)connect:(long)timeout; 98 | 99 | /** 100 | Returns whether the socket is currently connected. 101 | 102 | @return YES if the socket is connected, NO otherwise. 103 | */ 104 | - (BOOL)isConnected; 105 | 106 | /** 107 | Closes the connection to the remote host. 108 | 109 | @return YES if the close succeeded, NO otherwise. 110 | */ 111 | - (BOOL)close; 112 | 113 | /** 114 | Sends the specified number bytes from the given buffer. 115 | 116 | @param buf The buffer containing the bytes to send. 117 | @param count The number of bytes to send, typically the size of the buffer. 118 | @return The actual number of bytes sent. 119 | */ 120 | - (long)sendBytes:(const void *)buf count:(long)count; 121 | 122 | /** 123 | Receives an unpredictable number bytes up to the specified limit. Stores the bytes 124 | in the given buffer and returns the actual number received. 125 | 126 | @param buf The buffer in which to store the bytes received. 127 | @param limit The maximum number of bytes to receive, typically the size of the buffer. 128 | @return The actual number of bytes received. 129 | */ 130 | - (long)receiveBytes:(void *)buf limit:(long)limit; 131 | 132 | /** 133 | Receives the exact number of bytes specified unless a timeout or other error occurs. 134 | Stores the bytes in the given buffer and returns the actual number received. To check 135 | whether the correct number of bytes was received, compare the return value with the 136 | count specified. 137 | 138 | @param buf The buffer in which to store the bytes received. 139 | @param count The exact number of bytes to receive, typically the size of the buffer. 140 | @return The actual number of bytes received. 141 | */ 142 | - (long)receiveBytes:(void *)buf count:(long)count; 143 | 144 | /** 145 | Sends the contents of the file at the specified path. Uses an internal buffer to read 146 | a block of data from the file and send it over the network. 147 | 148 | @param path The path of the file to send. 149 | @return The actual number of bytes sent. 150 | */ 151 | - (long)sendFile:(NSString *)path; 152 | 153 | /** 154 | Receives a file with the given length and writes it to the specified path. Uses an 155 | internal buffer to receive a block of data from the network and write it to disk. 156 | Overwrites any existing file. 157 | 158 | @param path The path of the file to create. 159 | @param length The length of the file, measured in bytes. 160 | @return The actual number of bytes received. 161 | */ 162 | - (long)receiveFile:(NSString *)path length:(long)length; 163 | 164 | /** 165 | Receives a file with the given length and writes it to the specified path and calculates 166 | its MD5 hash. The hash can be used for error checking. Uses an internal buffer to receive 167 | a block of data from the network and write it to disk. Overwrites any existing file. 168 | 169 | @param path The path of the file to create. 170 | @param length The length of the file, measured in bytes. 171 | @param hash An optional data object in which to store the calculated MD5 hash of the file. 172 | @return The actual number of bytes received. 173 | */ 174 | - (long)receiveFile:(NSString *)path length:(long)length md5:(NSData * _Nonnull * _Nullable)hash; 175 | 176 | #pragma mark - Settings 177 | 178 | /** 179 | Returns the number of seconds to wait without any network activity before giving up and 180 | returning an error. The default is zero seconds, in which case it will never time out. 181 | 182 | @return The current timeout value in seconds. 183 | */ 184 | - (float)timeout; 185 | 186 | /** 187 | Sets the number of seconds to wait without any network activity before giving up and 188 | returning an error. The default is zero seconds, in which case it will never time out. 189 | 190 | @param seconds The number of seconds to wait before timing out. 191 | @return YES if the timeout value was set successfully, NO otherwise. 192 | */ 193 | - (BOOL)setTimeout:(float)seconds; 194 | 195 | /** 196 | Returns the maximum segment size. The segment size is the largest amount of 197 | data that can be transmitted within a single packet. Too large of a value may result in packets 198 | being broken apart and reassembled during transmission, which is normal but can slow things 199 | down. Too small of a value may result in lots of overhead, which can also slow things down. 200 | 201 | A default value is automatically negotiated when a connection is established. However, the 202 | negotiation process may incorporate assumptions that are incorrect. If you understand the 203 | network condidtions, setting this value correctly may increase performance. 204 | 205 | @return The current maximum segment value, measured in bytes. 206 | */ 207 | - (int)segmentSize; 208 | 209 | /** 210 | Sets the maximum segment size. The segment size is the largest amount of 211 | data that can be transmitted within a single packet, excluding headers. Too large of a value 212 | may result in packets being broken apart and reassembled during transmission, which is normal 213 | but can slow things down. Too small of a value may result in lots of overhead, which can 214 | also slow things down. 215 | 216 | A default value is automatically negotiated when a connection is established. However, the 217 | negotiation process may incorporate assumptions that are incorrect. If you understand the 218 | network condidtions, setting this value correctly may increase performance. 219 | 220 | @param bytes The maximum segment size, measured in bytes. 221 | @return YES if the segment size value was set successfully, NO otherwise. 222 | */ 223 | - (BOOL)setSegmentSize:(int)bytes; 224 | 225 | @end 226 | NS_ASSUME_NONNULL_END 227 | -------------------------------------------------------------------------------- /FastSocket.m: -------------------------------------------------------------------------------- 1 | // 2 | // FastSocket.m 3 | // Copyright (c) 2011-2013 Daniel Reese 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | // 25 | // See the following for source information for this code: 26 | // http://beej.us/guide/bgnet/ 27 | // http://www.phildev.net/mss/blackhole_description.shtml 28 | // http://www.mikeash.com/pyblog/friday-qa-2011-02-18-compound-literals.html 29 | // http://cr.yp.to/docs/connect.html 30 | // 31 | // Set optimal packet size: 1500 bytes (Ethernet) - 40 bytes (TCP) - 12 bytes (Optional TCP timestamp) = 1448 bytes. 32 | // http://www.faqs.org/docs/gazette/tcp.html 33 | // http://smallvoid.com/article/windows-tcpip-settings.html 34 | // http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man4/tcp.4.html 35 | // 36 | // Disk caching is not needed unless the file will be accessed again soon (avoids 37 | // a buffer copy). Increasing the size of the TCP receive window is better for 38 | // fast networks. Using page-aligned memory allows the kernel to skip a buffer 39 | // copy. Using a multiple of the max segment size avoids partially filled network 40 | // buffers. Ethernet and IPv4 headers are each 20 bytes. OS X uses the optional 41 | // 12 byte TCP timestamp. Disabling TCP wait (Nagle's algorithm) is better for 42 | // sending files since all packets are large and the waiting slows things down. 43 | // 44 | 45 | 46 | #import "FastSocket.h" 47 | #import 48 | #include 49 | #include 50 | #include 51 | #include 52 | 53 | int connect_timeout(int sockfd, const struct sockaddr *address, socklen_t address_len, long timeout); 54 | 55 | 56 | @interface FastSocket () { 57 | @protected 58 | void *_buffer; 59 | long _size; 60 | float _timeout; 61 | int _segmentSize; 62 | } 63 | @end 64 | 65 | 66 | @implementation FastSocket 67 | 68 | - (id)initWithHost:(NSString *)remoteHost andPort:(NSString *)remotePort { 69 | if ((self = [super init])) { 70 | _sockfd = 0; 71 | _host = [remoteHost copy]; 72 | _port = [remotePort copy]; 73 | _size = getpagesize() * 1448 / 4; 74 | _buffer = valloc(_size); 75 | } 76 | return self; 77 | } 78 | 79 | - (id)initWithFileDescriptor:(int)fd { 80 | if ((self = [super init])) { 81 | // Assume the descriptor is an already connected socket. 82 | _sockfd = fd; 83 | _size = getpagesize() * 1448 / 4; 84 | _buffer = valloc(_size); 85 | 86 | // Instead of receiving a SIGPIPE signal, have write() return an error. 87 | if (setsockopt(_sockfd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int)) < 0) { 88 | _lastError = NEW_ERROR(errno, strerror(errno)); 89 | return nil; 90 | } 91 | 92 | // Disable Nagle's algorithm. 93 | if (setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) { 94 | _lastError = NEW_ERROR(errno, strerror(errno)); 95 | return nil; 96 | } 97 | 98 | // Increase receive buffer size. 99 | if (setsockopt(_sockfd, SOL_SOCKET, SO_RCVBUF, &_size, sizeof(_size)) < 0) { 100 | // Ignore this because some systems have small hard limits. 101 | } 102 | 103 | // Set timeout or segment size if requested. 104 | if (_timeout != 0.0f && ![self setTimeout:_timeout]) { 105 | return nil; 106 | } 107 | } 108 | return self; 109 | } 110 | 111 | - (void)buffer:(void **)outBuf size:(long *)outSize { 112 | if (outBuf && outSize) { 113 | *outBuf = _buffer; 114 | *outSize = _size; 115 | } 116 | } 117 | 118 | - (void)dealloc { 119 | [self close]; 120 | free(_buffer); 121 | } 122 | 123 | #pragma mark Actions 124 | 125 | - (BOOL)connect { 126 | return [self connect:0]; 127 | } 128 | 129 | - (BOOL)connect:(long)nsec { 130 | // Construct server address information. 131 | struct addrinfo hints, *serverinfo, *p; 132 | 133 | bzero(&hints, sizeof(hints)); 134 | hints.ai_family = AF_UNSPEC; 135 | hints.ai_socktype = SOCK_STREAM; 136 | 137 | int error = getaddrinfo([_host UTF8String], [_port UTF8String], &hints, &serverinfo); 138 | if (error) { 139 | _lastError = NEW_ERROR(error, gai_strerror(error)); 140 | return NO; 141 | } 142 | 143 | // Loop through the results and connect to the first we can. 144 | @try { 145 | for (p = serverinfo; p != NULL; p = p->ai_next) { 146 | if ((_sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) { 147 | _lastError = NEW_ERROR(errno, strerror(errno)); 148 | return NO; 149 | } 150 | 151 | // Instead of receiving a SIGPIPE signal, have write() return an error. 152 | if (setsockopt(_sockfd, SOL_SOCKET, SO_NOSIGPIPE, &(int){1}, sizeof(int)) < 0) { 153 | _lastError = NEW_ERROR(errno, strerror(errno)); 154 | return NO; 155 | } 156 | 157 | // Disable Nagle's algorithm. 158 | if (setsockopt(_sockfd, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) { 159 | _lastError = NEW_ERROR(errno, strerror(errno)); 160 | return NO; 161 | } 162 | 163 | // Increase receive buffer size. 164 | if (setsockopt(_sockfd, SOL_SOCKET, SO_RCVBUF, &_size, sizeof(_size)) < 0) { 165 | // Ignore this because some systems have small hard limits. 166 | } 167 | 168 | if (nsec) { 169 | // Connect the socket using the given timeout. 170 | if (connect_timeout(_sockfd, p->ai_addr, p->ai_addrlen, nsec) < 0) { 171 | _lastError = NEW_ERROR(errno, strerror(errno)); 172 | continue; 173 | } 174 | } 175 | else { 176 | // Connect the socket (default connect timeout is 75 seconds). 177 | if (connect(_sockfd, p->ai_addr, p->ai_addrlen) < 0) { 178 | _lastError = NEW_ERROR(errno, strerror(errno)); 179 | continue; 180 | } 181 | } 182 | 183 | // Set timeout or segment size if requested. 184 | if (_timeout != 0.0f && ![self setTimeout:_timeout]) { 185 | return NO; 186 | } 187 | if (_segmentSize && ![self setSegmentSize:_segmentSize]) { 188 | return NO; 189 | } 190 | 191 | // Found a working address, so move on. 192 | break; 193 | } 194 | if (p == NULL) { 195 | _lastError = NEW_ERROR(1, "Could not contact server"); 196 | return NO; 197 | } 198 | } 199 | @finally { 200 | freeaddrinfo(serverinfo); 201 | } 202 | return YES; 203 | } 204 | 205 | - (BOOL)isConnected { 206 | if (_sockfd == 0) { 207 | return NO; 208 | } 209 | 210 | struct sockaddr remoteAddr; 211 | if (getpeername(_sockfd, &remoteAddr, &(socklen_t){sizeof(remoteAddr)}) < 0) { 212 | _lastError = NEW_ERROR(errno, strerror(errno)); 213 | return NO; 214 | } 215 | return YES; 216 | } 217 | 218 | - (BOOL)close { 219 | if (_sockfd > 0 && close(_sockfd) < 0) { 220 | _lastError = NEW_ERROR(errno, strerror(errno)); 221 | return NO; 222 | } 223 | _sockfd = 0; 224 | return YES; 225 | } 226 | 227 | - (long)sendBytes:(const void *)buf count:(long)count { 228 | long sent; 229 | if ((sent = send(_sockfd, buf, count, 0)) < 0) { 230 | _lastError = NEW_ERROR(errno, strerror(errno)); 231 | } 232 | return sent; 233 | } 234 | 235 | - (long)receiveBytes:(void *)buf limit:(long)limit { 236 | long received = recv(_sockfd, buf, limit, 0); 237 | if (received < 0) { 238 | _lastError = NEW_ERROR(errno, strerror(errno)); 239 | } 240 | return received; 241 | } 242 | 243 | - (long)receiveBytes:(void *)buf count:(long)count { 244 | long expected = count; 245 | while (expected > 0) { 246 | long received = [self receiveBytes:buf limit:expected]; 247 | if (received < 1) { 248 | break; 249 | } 250 | expected -= received; 251 | buf += received; 252 | } 253 | return (count - expected); 254 | } 255 | 256 | - (long)sendFile:(NSString *)path { 257 | int fd = 0; 258 | long sent = 0; 259 | @try { 260 | const char *cPath = [path fileSystemRepresentation]; 261 | if ((fd = open(cPath, O_RDONLY)) < 0) { 262 | _lastError = NEW_ERROR(errno, strerror(errno)); 263 | return -1; 264 | } 265 | if (fcntl(fd, F_NOCACHE, 1) < 0) { 266 | // Ignore because this will still work with disk caching on. 267 | } 268 | 269 | long count; 270 | while (1) { 271 | count = read(fd, _buffer, _size); 272 | if (count == 0) { 273 | break; // Reached end of file. 274 | } 275 | if (count < 0) { 276 | _lastError = NEW_ERROR(errno, strerror(errno)); 277 | break; 278 | } 279 | if ([self sendBytes:_buffer count:count] < 0) { 280 | _lastError = NEW_ERROR(errno, strerror(errno)); 281 | break; 282 | } 283 | sent += count; 284 | } 285 | } 286 | @finally { 287 | close(fd); 288 | } 289 | return sent; 290 | } 291 | 292 | - (long)receiveFile:(NSString *)path length:(long)length { 293 | return [self receiveFile:path length:length md5:nil]; 294 | } 295 | 296 | - (long)receiveFile:(NSString *)path length:(long)length md5:(NSData **)outHash { 297 | int fd = 0; 298 | long remaining = length; 299 | @try { 300 | const char *cPath = [path fileSystemRepresentation]; 301 | if ((fd = open(cPath, O_WRONLY | O_CREAT | O_TRUNC, 0664)) < 0) { 302 | _lastError = NEW_ERROR(errno, strerror(errno)); 303 | return -1; 304 | } 305 | if (fcntl(fd, F_NOCACHE, 1) < 0) { 306 | // Ignore because this will still work with disk caching on. 307 | } 308 | 309 | uint8_t digest[CC_MD5_DIGEST_LENGTH]; 310 | CC_MD5_CTX context; 311 | if (outHash) { 312 | CC_MD5_Init(&context); 313 | } 314 | 315 | long received; 316 | while (remaining > 0) { 317 | received = [self receiveBytes:_buffer limit:MIN(length, _size)]; 318 | if (received == 0) { 319 | break; // Peer closed the connection. 320 | } 321 | if (received < 0) { 322 | _lastError = NEW_ERROR(errno, strerror(errno)); 323 | break; 324 | } 325 | if (write(fd, _buffer, received) < 0) { 326 | _lastError = NEW_ERROR(errno, strerror(errno)); 327 | break; 328 | } 329 | if (outHash) { 330 | CC_MD5_Update(&context, _buffer, (CC_LONG)received); 331 | } 332 | remaining -= received; 333 | } 334 | 335 | if (outHash) { 336 | CC_MD5_Final(digest, &context); 337 | *outHash = [NSData dataWithBytes:digest length:CC_MD5_DIGEST_LENGTH]; 338 | } 339 | } 340 | @finally { 341 | close(fd); 342 | } 343 | return (length - remaining); 344 | } 345 | 346 | #pragma mark Settings 347 | 348 | - (float)timeout { 349 | if (_sockfd > 0) { 350 | struct timeval tv; 351 | if (getsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, &(socklen_t){sizeof(tv)}) < 0) { 352 | _lastError = NEW_ERROR(errno, strerror(errno)); 353 | return NO; 354 | } 355 | _timeout = ((tv.tv_sec * 1000000L) + tv.tv_usec) / 1000000.0f; 356 | } 357 | return _timeout; 358 | } 359 | 360 | - (BOOL)setTimeout:(float)seconds { 361 | NSAssert(seconds >= 0.0, @"Timeout value must be positive"); 362 | if (_sockfd > 0) { 363 | struct timeval tv = {0, 0}; 364 | if (seconds != 0.0f) { 365 | tv.tv_sec = (long)floorf(seconds); 366 | tv.tv_usec = (int)(fmodf(seconds, 1.0f) * 1000000L); 367 | } 368 | if (setsockopt(_sockfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0 || setsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { 369 | _lastError = NEW_ERROR(errno, strerror(errno)); 370 | return NO; 371 | } 372 | } 373 | _timeout = seconds; 374 | return YES; 375 | } 376 | 377 | - (int)segmentSize { 378 | if (_sockfd > 0 && getsockopt(_sockfd, IPPROTO_TCP, TCP_MAXSEG, &_segmentSize, &(socklen_t){sizeof(_segmentSize)}) < 0) { 379 | _lastError = NEW_ERROR(errno, strerror(errno)); 380 | return NO; 381 | } 382 | return _segmentSize; 383 | } 384 | 385 | - (BOOL)setSegmentSize:(int)bytes { 386 | if (_sockfd > 0 && setsockopt(_sockfd, IPPROTO_TCP, TCP_MAXSEG, &bytes, sizeof(bytes)) < 0) { 387 | _lastError = NEW_ERROR(errno, strerror(errno)); 388 | return NO; 389 | } 390 | _segmentSize = bytes; 391 | return YES; 392 | } 393 | 394 | @end 395 | 396 | 397 | /** 398 | This method is adapted from section 16.3 in Unix Network Programming (2003) by Richard Stevens et al. 399 | See http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PP1&pg=PA448 400 | */ 401 | int connect_timeout(int sockfd, const struct sockaddr *address, socklen_t address_len, long timeout) { 402 | fd_set rset, wset; 403 | struct timeval tval; 404 | int error = 0; 405 | 406 | // Get current flags to restore after. 407 | int flags = fcntl(sockfd, F_GETFL, 0); 408 | 409 | // Set socket to non-blocking. 410 | fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 411 | 412 | // Connect should return immediately in the "in progress" state. 413 | int result = 0; 414 | if ((result = connect(sockfd, address, address_len)) < 0) { 415 | if (errno != EINPROGRESS) { 416 | return -1; 417 | } 418 | } 419 | 420 | // If connection completed immediately, skip waiting. 421 | if (result == 0) { 422 | goto done; 423 | } 424 | 425 | // Call select() to wait for the connection. 426 | // NOTE: If timeout is zero, then pass NULL in order to use default timeout. Zero seconds indicates no waiting. 427 | FD_ZERO(&rset); 428 | FD_SET(sockfd, &rset); 429 | wset = rset; 430 | tval.tv_sec = timeout; 431 | tval.tv_usec = 0; 432 | if ((result = select(sockfd + 1, &rset, &wset, NULL, timeout ? &tval : NULL)) == 0) { 433 | errno = ETIMEDOUT; 434 | return -1; 435 | } 436 | 437 | // Check whether the connection succeeded. If the socket is readable or writable, check for an error. 438 | if (FD_ISSET(sockfd, &rset) || FD_ISSET(sockfd, &wset)) { 439 | socklen_t len = sizeof(error); 440 | if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) { 441 | return -1; 442 | } 443 | } 444 | 445 | done: 446 | // Restore original flags. 447 | fcntl(sockfd, F_SETFL, flags); 448 | 449 | // NOTE: On some systems, getsockopt() will fail and set errno. On others, it will succeed and set the error parameter. 450 | if (error) { 451 | errno = error; 452 | return -1; 453 | } 454 | return 0; 455 | } 456 | -------------------------------------------------------------------------------- /FastSocket.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "FastSocket" 3 | s.version = "1.6" 4 | s.summary = "A fast, synchronous Objective-C wrapper around BSD sockets for iOS and OS X." 5 | s.description = <<-DESC 6 | A fast, synchronous Objective-C wrapper around BSD sockets for iOS and OS X. 7 | Send and receive raw bytes over a socket as fast as possible. Includes methods 8 | for transferring files while optionally computing a checksum for verification. 9 | 10 | Use this class if fast network communication is what you need. If you want to 11 | do something else while your network operations finish, then an asynchronous 12 | API might be better. 13 | DESC 14 | s.homepage = "https://github.com/dreese/FastSocket" 15 | s.license = { :type => 'MIT', :file => 'LICENSE' } 16 | s.author = { "Dan Reese" => "dan@danielreese.com" } 17 | s.social_media_url = "http://twitter.com/dreese" 18 | s.source = { :git => "https://github.com/dreese/FastSocket.git", :tag => "v1.6" } 19 | s.source_files = 'FastSocket.{h,m}', 'FastServerSocket.{h,m}' 20 | s.requires_arc = true 21 | s.framework = 'Foundation' 22 | end 23 | -------------------------------------------------------------------------------- /FastSocket.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3754B1F5188C7E0F0052728A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3754B1F4188C7E0F0052728A /* Foundation.framework */; }; 11 | 3754B204188C7E0F0052728A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3754B1F4188C7E0F0052728A /* Foundation.framework */; }; 12 | 3754B209188C7E0F0052728A /* libFastSocket-iOS.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3754B1F1188C7E0F0052728A /* libFastSocket-iOS.a */; }; 13 | 3754B21E188C7E650052728A /* FastSocketTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3754B21D188C7E650052728A /* FastSocketTest.m */; }; 14 | 3754B242188C81C30052728A /* libFastSocket-OSX.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3754B22C188C81C30052728A /* libFastSocket-OSX.a */; }; 15 | 3754B252188C82860052728A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3754B230188C81C30052728A /* Foundation.framework */; }; 16 | 3754B255188C82C30052728A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3754B1F4188C7E0F0052728A /* Foundation.framework */; }; 17 | 3754B256188C8B340052728A /* FastSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 3754B222188C7F120052728A /* FastSocket.m */; }; 18 | 3754B257188C8B340052728A /* FastSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 3754B222188C7F120052728A /* FastSocket.m */; }; 19 | 3754B258188C8B380052728A /* FastServerSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 3754B21B188C7E5A0052728A /* FastServerSocket.m */; }; 20 | 3754B259188C8B390052728A /* FastServerSocket.m in Sources */ = {isa = PBXBuildFile; fileRef = 3754B21B188C7E5A0052728A /* FastServerSocket.m */; }; 21 | 3754B25A188C8B3B0052728A /* FastSocketTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 3754B21D188C7E650052728A /* FastSocketTest.m */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 3754B25B188D047B0052728A /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 3754B1E9188C7E0F0052728A /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 3754B1F0188C7E0F0052728A; 30 | remoteInfo = "FastSocket iOS"; 31 | }; 32 | 3754B25D188D04810052728A /* PBXContainerItemProxy */ = { 33 | isa = PBXContainerItemProxy; 34 | containerPortal = 3754B1E9188C7E0F0052728A /* Project object */; 35 | proxyType = 1; 36 | remoteGlobalIDString = 3754B22B188C81C30052728A; 37 | remoteInfo = "FastSocket OSX"; 38 | }; 39 | /* End PBXContainerItemProxy section */ 40 | 41 | /* Begin PBXCopyFilesBuildPhase section */ 42 | 3754B1EF188C7E0F0052728A /* CopyFiles */ = { 43 | isa = PBXCopyFilesBuildPhase; 44 | buildActionMask = 2147483647; 45 | dstPath = "include/$(PRODUCT_NAME)"; 46 | dstSubfolderSpec = 16; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXCopyFilesBuildPhase section */ 52 | 53 | /* Begin PBXFileReference section */ 54 | 370985EA207C7425009ABEEC /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; }; 55 | 373F4A8E189EF6C400A326ED /* FastSocket.podspec */ = {isa = PBXFileReference; lastKnownFileType = text; path = FastSocket.podspec; sourceTree = SOURCE_ROOT; }; 56 | 3754B1F1188C7E0F0052728A /* libFastSocket-iOS.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libFastSocket-iOS.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 57 | 3754B1F4188C7E0F0052728A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 58 | 3754B201188C7E0F0052728A /* FastSocket-iOS-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "FastSocket-iOS-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 59 | 3754B21A188C7E5A0052728A /* FastServerSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastServerSocket.h; sourceTree = SOURCE_ROOT; }; 60 | 3754B21B188C7E5A0052728A /* FastServerSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FastServerSocket.m; sourceTree = SOURCE_ROOT; }; 61 | 3754B21D188C7E650052728A /* FastSocketTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FastSocketTest.m; sourceTree = SOURCE_ROOT; }; 62 | 3754B221188C7F120052728A /* FastSocket.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastSocket.h; sourceTree = SOURCE_ROOT; }; 63 | 3754B222188C7F120052728A /* FastSocket.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FastSocket.m; sourceTree = SOURCE_ROOT; }; 64 | 3754B225188C7F3A0052728A /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = SOURCE_ROOT; }; 65 | 3754B226188C7F3A0052728A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; }; 66 | 3754B227188C7F3A0052728A /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = SOURCE_ROOT; }; 67 | 3754B22C188C81C30052728A /* libFastSocket-OSX.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libFastSocket-OSX.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 68 | 3754B230188C81C30052728A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 69 | 3754B23D188C81C30052728A /* FastSocket-OSX-Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "FastSocket-OSX-Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 70 | 37E6599E1F96CEF800C8E81B /* CONTRIBUTING.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONTRIBUTING.md; sourceTree = SOURCE_ROOT; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 3754B1EE188C7E0F0052728A /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | 3754B1F5188C7E0F0052728A /* Foundation.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | 3754B1FE188C7E0F0052728A /* Frameworks */ = { 83 | isa = PBXFrameworksBuildPhase; 84 | buildActionMask = 2147483647; 85 | files = ( 86 | 3754B204188C7E0F0052728A /* Foundation.framework in Frameworks */, 87 | 3754B209188C7E0F0052728A /* libFastSocket-iOS.a in Frameworks */, 88 | ); 89 | runOnlyForDeploymentPostprocessing = 0; 90 | }; 91 | 3754B229188C81C30052728A /* Frameworks */ = { 92 | isa = PBXFrameworksBuildPhase; 93 | buildActionMask = 2147483647; 94 | files = ( 95 | 3754B252188C82860052728A /* Foundation.framework in Frameworks */, 96 | ); 97 | runOnlyForDeploymentPostprocessing = 0; 98 | }; 99 | 3754B23A188C81C30052728A /* Frameworks */ = { 100 | isa = PBXFrameworksBuildPhase; 101 | buildActionMask = 2147483647; 102 | files = ( 103 | 3754B255188C82C30052728A /* Foundation.framework in Frameworks */, 104 | 3754B242188C81C30052728A /* libFastSocket-OSX.a in Frameworks */, 105 | ); 106 | runOnlyForDeploymentPostprocessing = 0; 107 | }; 108 | /* End PBXFrameworksBuildPhase section */ 109 | 110 | /* Begin PBXGroup section */ 111 | 3754B1E8188C7E0F0052728A = { 112 | isa = PBXGroup; 113 | children = ( 114 | 3754B1F6188C7E0F0052728A /* FastSocket */, 115 | 3754B224188C7F1F0052728A /* Resources */, 116 | 3754B1F3188C7E0F0052728A /* Frameworks */, 117 | 3754B1F2188C7E0F0052728A /* Products */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | 3754B1F2188C7E0F0052728A /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 3754B1F1188C7E0F0052728A /* libFastSocket-iOS.a */, 125 | 3754B201188C7E0F0052728A /* FastSocket-iOS-Tests.xctest */, 126 | 3754B22C188C81C30052728A /* libFastSocket-OSX.a */, 127 | 3754B23D188C81C30052728A /* FastSocket-OSX-Tests.xctest */, 128 | ); 129 | name = Products; 130 | sourceTree = ""; 131 | }; 132 | 3754B1F3188C7E0F0052728A /* Frameworks */ = { 133 | isa = PBXGroup; 134 | children = ( 135 | 3754B251188C824D0052728A /* iOS */, 136 | 3754B22F188C81C30052728A /* OSX */, 137 | ); 138 | name = Frameworks; 139 | sourceTree = ""; 140 | }; 141 | 3754B1F6188C7E0F0052728A /* FastSocket */ = { 142 | isa = PBXGroup; 143 | children = ( 144 | 3754B221188C7F120052728A /* FastSocket.h */, 145 | 3754B222188C7F120052728A /* FastSocket.m */, 146 | 3754B21A188C7E5A0052728A /* FastServerSocket.h */, 147 | 3754B21B188C7E5A0052728A /* FastServerSocket.m */, 148 | 3754B21D188C7E650052728A /* FastSocketTest.m */, 149 | ); 150 | name = FastSocket; 151 | sourceTree = ""; 152 | }; 153 | 3754B224188C7F1F0052728A /* Resources */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 3754B225188C7F3A0052728A /* LICENSE */, 157 | 370985EA207C7425009ABEEC /* CHANGELOG.md */, 158 | 37E6599E1F96CEF800C8E81B /* CONTRIBUTING.md */, 159 | 3754B226188C7F3A0052728A /* README.md */, 160 | 373F4A8E189EF6C400A326ED /* FastSocket.podspec */, 161 | 3754B227188C7F3A0052728A /* UnitTests-Info.plist */, 162 | ); 163 | name = Resources; 164 | sourceTree = ""; 165 | }; 166 | 3754B22F188C81C30052728A /* OSX */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | 3754B230188C81C30052728A /* Foundation.framework */, 170 | ); 171 | name = OSX; 172 | sourceTree = ""; 173 | }; 174 | 3754B251188C824D0052728A /* iOS */ = { 175 | isa = PBXGroup; 176 | children = ( 177 | 3754B1F4188C7E0F0052728A /* Foundation.framework */, 178 | ); 179 | name = iOS; 180 | sourceTree = ""; 181 | }; 182 | /* End PBXGroup section */ 183 | 184 | /* Begin PBXHeadersBuildPhase section */ 185 | 3754B22A188C81C30052728A /* Headers */ = { 186 | isa = PBXHeadersBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | /* End PBXHeadersBuildPhase section */ 193 | 194 | /* Begin PBXNativeTarget section */ 195 | 3754B1F0188C7E0F0052728A /* FastSocket iOS */ = { 196 | isa = PBXNativeTarget; 197 | buildConfigurationList = 3754B214188C7E0F0052728A /* Build configuration list for PBXNativeTarget "FastSocket iOS" */; 198 | buildPhases = ( 199 | 3754B1ED188C7E0F0052728A /* Sources */, 200 | 3754B1EE188C7E0F0052728A /* Frameworks */, 201 | 3754B1EF188C7E0F0052728A /* CopyFiles */, 202 | ); 203 | buildRules = ( 204 | ); 205 | dependencies = ( 206 | ); 207 | name = "FastSocket iOS"; 208 | productName = FastSocket; 209 | productReference = 3754B1F1188C7E0F0052728A /* libFastSocket-iOS.a */; 210 | productType = "com.apple.product-type.library.static"; 211 | }; 212 | 3754B200188C7E0F0052728A /* FastSocket iOS Tests */ = { 213 | isa = PBXNativeTarget; 214 | buildConfigurationList = 3754B217188C7E0F0052728A /* Build configuration list for PBXNativeTarget "FastSocket iOS Tests" */; 215 | buildPhases = ( 216 | 3754B1FD188C7E0F0052728A /* Sources */, 217 | 3754B1FE188C7E0F0052728A /* Frameworks */, 218 | 3754B1FF188C7E0F0052728A /* Resources */, 219 | ); 220 | buildRules = ( 221 | ); 222 | dependencies = ( 223 | 3754B25C188D047B0052728A /* PBXTargetDependency */, 224 | ); 225 | name = "FastSocket iOS Tests"; 226 | productName = FastSocketTests; 227 | productReference = 3754B201188C7E0F0052728A /* FastSocket-iOS-Tests.xctest */; 228 | productType = "com.apple.product-type.bundle.unit-test"; 229 | }; 230 | 3754B22B188C81C30052728A /* FastSocket OSX */ = { 231 | isa = PBXNativeTarget; 232 | buildConfigurationList = 3754B24B188C81C30052728A /* Build configuration list for PBXNativeTarget "FastSocket OSX" */; 233 | buildPhases = ( 234 | 3754B228188C81C30052728A /* Sources */, 235 | 3754B229188C81C30052728A /* Frameworks */, 236 | 3754B22A188C81C30052728A /* Headers */, 237 | ); 238 | buildRules = ( 239 | ); 240 | dependencies = ( 241 | ); 242 | name = "FastSocket OSX"; 243 | productName = "FastSocket OSX"; 244 | productReference = 3754B22C188C81C30052728A /* libFastSocket-OSX.a */; 245 | productType = "com.apple.product-type.library.static"; 246 | }; 247 | 3754B23C188C81C30052728A /* FastSocket OSX Tests */ = { 248 | isa = PBXNativeTarget; 249 | buildConfigurationList = 3754B24E188C81C30052728A /* Build configuration list for PBXNativeTarget "FastSocket OSX Tests" */; 250 | buildPhases = ( 251 | 3754B239188C81C30052728A /* Sources */, 252 | 3754B23A188C81C30052728A /* Frameworks */, 253 | 3754B23B188C81C30052728A /* Resources */, 254 | ); 255 | buildRules = ( 256 | ); 257 | dependencies = ( 258 | 3754B25E188D04810052728A /* PBXTargetDependency */, 259 | ); 260 | name = "FastSocket OSX Tests"; 261 | productName = "FastSocket OSXTests"; 262 | productReference = 3754B23D188C81C30052728A /* FastSocket-OSX-Tests.xctest */; 263 | productType = "com.apple.product-type.bundle.unit-test"; 264 | }; 265 | /* End PBXNativeTarget section */ 266 | 267 | /* Begin PBXProject section */ 268 | 3754B1E9188C7E0F0052728A /* Project object */ = { 269 | isa = PBXProject; 270 | attributes = { 271 | LastUpgradeCheck = 0900; 272 | ORGANIZATIONNAME = "Daniel Reese"; 273 | }; 274 | buildConfigurationList = 3754B1EC188C7E0F0052728A /* Build configuration list for PBXProject "FastSocket" */; 275 | compatibilityVersion = "Xcode 3.2"; 276 | developmentRegion = English; 277 | hasScannedForEncodings = 0; 278 | knownRegions = ( 279 | en, 280 | ); 281 | mainGroup = 3754B1E8188C7E0F0052728A; 282 | productRefGroup = 3754B1F2188C7E0F0052728A /* Products */; 283 | projectDirPath = ""; 284 | projectRoot = ""; 285 | targets = ( 286 | 3754B1F0188C7E0F0052728A /* FastSocket iOS */, 287 | 3754B200188C7E0F0052728A /* FastSocket iOS Tests */, 288 | 3754B22B188C81C30052728A /* FastSocket OSX */, 289 | 3754B23C188C81C30052728A /* FastSocket OSX Tests */, 290 | ); 291 | }; 292 | /* End PBXProject section */ 293 | 294 | /* Begin PBXResourcesBuildPhase section */ 295 | 3754B1FF188C7E0F0052728A /* Resources */ = { 296 | isa = PBXResourcesBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | runOnlyForDeploymentPostprocessing = 0; 301 | }; 302 | 3754B23B188C81C30052728A /* Resources */ = { 303 | isa = PBXResourcesBuildPhase; 304 | buildActionMask = 2147483647; 305 | files = ( 306 | ); 307 | runOnlyForDeploymentPostprocessing = 0; 308 | }; 309 | /* End PBXResourcesBuildPhase section */ 310 | 311 | /* Begin PBXSourcesBuildPhase section */ 312 | 3754B1ED188C7E0F0052728A /* Sources */ = { 313 | isa = PBXSourcesBuildPhase; 314 | buildActionMask = 2147483647; 315 | files = ( 316 | 3754B256188C8B340052728A /* FastSocket.m in Sources */, 317 | 3754B258188C8B380052728A /* FastServerSocket.m in Sources */, 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | }; 321 | 3754B1FD188C7E0F0052728A /* Sources */ = { 322 | isa = PBXSourcesBuildPhase; 323 | buildActionMask = 2147483647; 324 | files = ( 325 | 3754B21E188C7E650052728A /* FastSocketTest.m in Sources */, 326 | ); 327 | runOnlyForDeploymentPostprocessing = 0; 328 | }; 329 | 3754B228188C81C30052728A /* Sources */ = { 330 | isa = PBXSourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | 3754B257188C8B340052728A /* FastSocket.m in Sources */, 334 | 3754B259188C8B390052728A /* FastServerSocket.m in Sources */, 335 | ); 336 | runOnlyForDeploymentPostprocessing = 0; 337 | }; 338 | 3754B239188C81C30052728A /* Sources */ = { 339 | isa = PBXSourcesBuildPhase; 340 | buildActionMask = 2147483647; 341 | files = ( 342 | 3754B25A188C8B3B0052728A /* FastSocketTest.m in Sources */, 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | /* End PBXSourcesBuildPhase section */ 347 | 348 | /* Begin PBXTargetDependency section */ 349 | 3754B25C188D047B0052728A /* PBXTargetDependency */ = { 350 | isa = PBXTargetDependency; 351 | target = 3754B1F0188C7E0F0052728A /* FastSocket iOS */; 352 | targetProxy = 3754B25B188D047B0052728A /* PBXContainerItemProxy */; 353 | }; 354 | 3754B25E188D04810052728A /* PBXTargetDependency */ = { 355 | isa = PBXTargetDependency; 356 | target = 3754B22B188C81C30052728A /* FastSocket OSX */; 357 | targetProxy = 3754B25D188D04810052728A /* PBXContainerItemProxy */; 358 | }; 359 | /* End PBXTargetDependency section */ 360 | 361 | /* Begin XCBuildConfiguration section */ 362 | 3754B212188C7E0F0052728A /* Debug */ = { 363 | isa = XCBuildConfiguration; 364 | buildSettings = { 365 | ALWAYS_SEARCH_USER_PATHS = NO; 366 | CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; 367 | CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; 368 | CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 380 | CLANG_WARN_EMPTY_BODY = YES; 381 | CLANG_WARN_ENUM_CONVERSION = YES; 382 | CLANG_WARN_INFINITE_RECURSION = YES; 383 | CLANG_WARN_INT_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 385 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 386 | CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; 387 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; 388 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 389 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 390 | CLANG_WARN_STRICT_PROTOTYPES = YES; 391 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 392 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 393 | CLANG_WARN_UNREACHABLE_CODE = YES; 394 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 395 | COPY_PHASE_STRIP = NO; 396 | ENABLE_STRICT_OBJC_MSGSEND = YES; 397 | ENABLE_TESTABILITY = YES; 398 | GCC_C_LANGUAGE_STANDARD = gnu99; 399 | GCC_DYNAMIC_NO_PIC = NO; 400 | GCC_NO_COMMON_BLOCKS = YES; 401 | GCC_OPTIMIZATION_LEVEL = 0; 402 | GCC_PREPROCESSOR_DEFINITIONS = ( 403 | "DEBUG=1", 404 | "$(inherited)", 405 | ); 406 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 407 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 408 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 409 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 410 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 411 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 412 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 413 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 414 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 415 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 416 | GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES; 417 | GCC_WARN_SHADOW = YES; 418 | GCC_WARN_SIGN_COMPARE = YES; 419 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 420 | GCC_WARN_UNDECLARED_SELECTOR = YES; 421 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 422 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 423 | GCC_WARN_UNUSED_FUNCTION = YES; 424 | GCC_WARN_UNUSED_LABEL = YES; 425 | GCC_WARN_UNUSED_VARIABLE = YES; 426 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 427 | MACOSX_DEPLOYMENT_TARGET = 10.8; 428 | ONLY_ACTIVE_ARCH = YES; 429 | PRODUCT_NAME = "${TARGET_NAME:rfc1034identifier}"; 430 | }; 431 | name = Debug; 432 | }; 433 | 3754B213188C7E0F0052728A /* Release */ = { 434 | isa = XCBuildConfiguration; 435 | buildSettings = { 436 | ALWAYS_SEARCH_USER_PATHS = NO; 437 | CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES; 438 | CLANG_ANALYZER_SECURITY_INSECUREAPI_RAND = YES; 439 | CLANG_ANALYZER_SECURITY_INSECUREAPI_STRCPY = YES; 440 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 441 | CLANG_CXX_LIBRARY = "libc++"; 442 | CLANG_ENABLE_MODULES = YES; 443 | CLANG_ENABLE_OBJC_ARC = YES; 444 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 445 | CLANG_WARN_BOOL_CONVERSION = YES; 446 | CLANG_WARN_COMMA = YES; 447 | CLANG_WARN_CONSTANT_CONVERSION = YES; 448 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 449 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 450 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 451 | CLANG_WARN_EMPTY_BODY = YES; 452 | CLANG_WARN_ENUM_CONVERSION = YES; 453 | CLANG_WARN_INFINITE_RECURSION = YES; 454 | CLANG_WARN_INT_CONVERSION = YES; 455 | CLANG_WARN_OBJC_IMPLICIT_ATOMIC_PROPERTIES = YES; 456 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 457 | CLANG_WARN_OBJC_MISSING_PROPERTY_SYNTHESIS = NO; 458 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES; 459 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 460 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 461 | CLANG_WARN_STRICT_PROTOTYPES = YES; 462 | CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; 463 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 464 | CLANG_WARN_UNREACHABLE_CODE = YES; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | COPY_PHASE_STRIP = YES; 467 | ENABLE_NS_ASSERTIONS = NO; 468 | ENABLE_STRICT_OBJC_MSGSEND = YES; 469 | GCC_C_LANGUAGE_STANDARD = gnu99; 470 | GCC_NO_COMMON_BLOCKS = YES; 471 | GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES; 472 | GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES; 473 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 474 | GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES; 475 | GCC_WARN_ABOUT_MISSING_NEWLINE = YES; 476 | GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; 477 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 478 | GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES; 479 | GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES; 480 | GCC_WARN_MULTIPLE_DEFINITION_TYPES_FOR_SELECTOR = YES; 481 | GCC_WARN_SHADOW = YES; 482 | GCC_WARN_SIGN_COMPARE = YES; 483 | GCC_WARN_STRICT_SELECTOR_MATCH = YES; 484 | GCC_WARN_UNDECLARED_SELECTOR = YES; 485 | GCC_WARN_UNINITIALIZED_AUTOS = YES; 486 | GCC_WARN_UNKNOWN_PRAGMAS = YES; 487 | GCC_WARN_UNUSED_FUNCTION = YES; 488 | GCC_WARN_UNUSED_LABEL = YES; 489 | GCC_WARN_UNUSED_VARIABLE = YES; 490 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 491 | MACOSX_DEPLOYMENT_TARGET = 10.8; 492 | PRODUCT_NAME = "${TARGET_NAME:rfc1034identifier}"; 493 | VALIDATE_PRODUCT = YES; 494 | }; 495 | name = Release; 496 | }; 497 | 3754B215188C7E0F0052728A /* Debug */ = { 498 | isa = XCBuildConfiguration; 499 | buildSettings = { 500 | FRAMEWORK_SEARCH_PATHS = ( 501 | "$(inherited)", 502 | "$(DEVELOPER_FRAMEWORKS_DIR)", 503 | ); 504 | OTHER_LDFLAGS = "-ObjC"; 505 | SDKROOT = iphoneos; 506 | SKIP_INSTALL = YES; 507 | }; 508 | name = Debug; 509 | }; 510 | 3754B216188C7E0F0052728A /* Release */ = { 511 | isa = XCBuildConfiguration; 512 | buildSettings = { 513 | FRAMEWORK_SEARCH_PATHS = ( 514 | "$(inherited)", 515 | "$(DEVELOPER_FRAMEWORKS_DIR)", 516 | ); 517 | OTHER_LDFLAGS = "-ObjC"; 518 | SDKROOT = iphoneos; 519 | SKIP_INSTALL = YES; 520 | }; 521 | name = Release; 522 | }; 523 | 3754B218188C7E0F0052728A /* Debug */ = { 524 | isa = XCBuildConfiguration; 525 | buildSettings = { 526 | FRAMEWORK_SEARCH_PATHS = ( 527 | "$(inherited)", 528 | "$(DEVELOPER_FRAMEWORKS_DIR)", 529 | ); 530 | INFOPLIST_FILE = "UnitTests-Info.plist"; 531 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielreese.${PRODUCT_NAME:rfc1034identifier}"; 532 | SDKROOT = iphoneos; 533 | WRAPPER_EXTENSION = xctest; 534 | }; 535 | name = Debug; 536 | }; 537 | 3754B219188C7E0F0052728A /* Release */ = { 538 | isa = XCBuildConfiguration; 539 | buildSettings = { 540 | FRAMEWORK_SEARCH_PATHS = ( 541 | "$(inherited)", 542 | "$(DEVELOPER_FRAMEWORKS_DIR)", 543 | ); 544 | INFOPLIST_FILE = "UnitTests-Info.plist"; 545 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielreese.${PRODUCT_NAME:rfc1034identifier}"; 546 | SDKROOT = iphoneos; 547 | WRAPPER_EXTENSION = xctest; 548 | }; 549 | name = Release; 550 | }; 551 | 3754B24C188C81C30052728A /* Debug */ = { 552 | isa = XCBuildConfiguration; 553 | buildSettings = { 554 | COMBINE_HIDPI_IMAGES = YES; 555 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 556 | FRAMEWORK_SEARCH_PATHS = ( 557 | "$(inherited)", 558 | "$(DEVELOPER_FRAMEWORKS_DIR)", 559 | ); 560 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 561 | SDKROOT = macosx; 562 | }; 563 | name = Debug; 564 | }; 565 | 3754B24D188C81C30052728A /* Release */ = { 566 | isa = XCBuildConfiguration; 567 | buildSettings = { 568 | COMBINE_HIDPI_IMAGES = YES; 569 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 570 | FRAMEWORK_SEARCH_PATHS = ( 571 | "$(inherited)", 572 | "$(DEVELOPER_FRAMEWORKS_DIR)", 573 | ); 574 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 575 | SDKROOT = macosx; 576 | }; 577 | name = Release; 578 | }; 579 | 3754B24F188C81C30052728A /* Debug */ = { 580 | isa = XCBuildConfiguration; 581 | buildSettings = { 582 | COMBINE_HIDPI_IMAGES = YES; 583 | FRAMEWORK_SEARCH_PATHS = ( 584 | "$(inherited)", 585 | "$(DEVELOPER_FRAMEWORKS_DIR)", 586 | ); 587 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 588 | GCC_PREPROCESSOR_DEFINITIONS = ( 589 | "DEBUG=1", 590 | "$(inherited)", 591 | ); 592 | INFOPLIST_FILE = "UnitTests-Info.plist"; 593 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielreese.${PRODUCT_NAME:rfc1034identifier}"; 594 | SDKROOT = macosx; 595 | WRAPPER_EXTENSION = xctest; 596 | }; 597 | name = Debug; 598 | }; 599 | 3754B250188C81C30052728A /* Release */ = { 600 | isa = XCBuildConfiguration; 601 | buildSettings = { 602 | COMBINE_HIDPI_IMAGES = YES; 603 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 604 | FRAMEWORK_SEARCH_PATHS = ( 605 | "$(inherited)", 606 | "$(DEVELOPER_FRAMEWORKS_DIR)", 607 | ); 608 | GCC_ENABLE_OBJC_EXCEPTIONS = YES; 609 | INFOPLIST_FILE = "UnitTests-Info.plist"; 610 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielreese.${PRODUCT_NAME:rfc1034identifier}"; 611 | SDKROOT = macosx; 612 | WRAPPER_EXTENSION = xctest; 613 | }; 614 | name = Release; 615 | }; 616 | /* End XCBuildConfiguration section */ 617 | 618 | /* Begin XCConfigurationList section */ 619 | 3754B1EC188C7E0F0052728A /* Build configuration list for PBXProject "FastSocket" */ = { 620 | isa = XCConfigurationList; 621 | buildConfigurations = ( 622 | 3754B212188C7E0F0052728A /* Debug */, 623 | 3754B213188C7E0F0052728A /* Release */, 624 | ); 625 | defaultConfigurationIsVisible = 0; 626 | defaultConfigurationName = Release; 627 | }; 628 | 3754B214188C7E0F0052728A /* Build configuration list for PBXNativeTarget "FastSocket iOS" */ = { 629 | isa = XCConfigurationList; 630 | buildConfigurations = ( 631 | 3754B215188C7E0F0052728A /* Debug */, 632 | 3754B216188C7E0F0052728A /* Release */, 633 | ); 634 | defaultConfigurationIsVisible = 0; 635 | defaultConfigurationName = Release; 636 | }; 637 | 3754B217188C7E0F0052728A /* Build configuration list for PBXNativeTarget "FastSocket iOS Tests" */ = { 638 | isa = XCConfigurationList; 639 | buildConfigurations = ( 640 | 3754B218188C7E0F0052728A /* Debug */, 641 | 3754B219188C7E0F0052728A /* Release */, 642 | ); 643 | defaultConfigurationIsVisible = 0; 644 | defaultConfigurationName = Release; 645 | }; 646 | 3754B24B188C81C30052728A /* Build configuration list for PBXNativeTarget "FastSocket OSX" */ = { 647 | isa = XCConfigurationList; 648 | buildConfigurations = ( 649 | 3754B24C188C81C30052728A /* Debug */, 650 | 3754B24D188C81C30052728A /* Release */, 651 | ); 652 | defaultConfigurationIsVisible = 0; 653 | defaultConfigurationName = Release; 654 | }; 655 | 3754B24E188C81C30052728A /* Build configuration list for PBXNativeTarget "FastSocket OSX Tests" */ = { 656 | isa = XCConfigurationList; 657 | buildConfigurations = ( 658 | 3754B24F188C81C30052728A /* Debug */, 659 | 3754B250188C81C30052728A /* Release */, 660 | ); 661 | defaultConfigurationIsVisible = 0; 662 | defaultConfigurationName = Release; 663 | }; 664 | /* End XCConfigurationList section */ 665 | }; 666 | rootObject = 3754B1E9188C7E0F0052728A /* Project object */; 667 | } 668 | -------------------------------------------------------------------------------- /FastSocket.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /FastSocketTest.m: -------------------------------------------------------------------------------- 1 | // 2 | // FastSocketTest.h 3 | // Copyright (c) 2011-2013 Daniel Reese 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 13 | // all 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 21 | // THE SOFTWARE. 22 | // 23 | 24 | #import "FastSocket.h" 25 | #import "FastServerSocket.h" 26 | #import 27 | 28 | 29 | @interface FastSocketTest : XCTestCase { 30 | FastSocket *client; 31 | FastServerSocket *server; 32 | } 33 | 34 | @end 35 | 36 | 37 | @implementation FastSocketTest 38 | 39 | - (void)setUp { 40 | server = [[FastServerSocket alloc] initWithPort:@"34567"]; 41 | client = [[FastSocket alloc] initWithHost:@"localhost" andPort:@"34567"]; 42 | } 43 | 44 | - (void)tearDown { 45 | [client close]; 46 | [server close]; 47 | } 48 | 49 | #pragma mark Tests 50 | 51 | - (void)testConnect { 52 | // No failures yet. 53 | XCTAssertNil([client lastError]); 54 | 55 | // Nothing is listening yet. 56 | XCTAssertFalse([client connect]); 57 | XCTAssertNotNil([client lastError]); 58 | 59 | // Spawn server thread. 60 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 61 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 62 | 63 | // Connection should now succeed. 64 | XCTAssertTrue([client connect]); 65 | //STAssertNil([client lastError], @"Last error should be nil"); // TODO: Not sure if this should be cleared out or just left alone. 66 | } 67 | 68 | - (void)testDoubleCloseAfterConnectionFailure { 69 | XCTAssertFalse([client connect:0]); 70 | XCTAssertTrue([client close]); 71 | XCTAssertTrue([client close]); 72 | 73 | XCTAssertFalse([client connect:1]); 74 | XCTAssertTrue([client close]); 75 | XCTAssertTrue([client close]); 76 | } 77 | 78 | - (void)testConnectWithDefaultTimeout { 79 | // Connect to a non-routable IP address. See https://stackoverflow.com/a/40459270 80 | [client close]; 81 | client = [[FastSocket alloc] initWithHost:@"example.com" andPort:@"81"]; 82 | 83 | // Connection should timeout. 84 | NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; 85 | XCTAssertFalse([client connect]); 86 | NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate]; 87 | XCTAssertNotNil([client lastError]); 88 | 89 | // Default timeout is 75 seconds on my machine. 90 | NSTimeInterval actualTime = endTime - startTime; 91 | XCTAssertTrue(actualTime >= 75.0, @"timeout was %.2f", actualTime); 92 | XCTAssertTrue(actualTime < 76.3, @"timeout was %.2f", actualTime); 93 | } 94 | 95 | - (void)testConnectWithCustomTimeout { 96 | // Connect to a non-routable IP address. See https://stackoverflow.com/a/40459270 97 | [client close]; 98 | client = [[FastSocket alloc] initWithHost:@"example.com" andPort:@"81"]; 99 | 100 | // Connection should timeout. 101 | NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; 102 | XCTAssertFalse([client connect:10]); 103 | NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate]; 104 | XCTAssertNotNil([client lastError]); 105 | 106 | // Check the duration of the timeout. 107 | NSTimeInterval actualTime = endTime - startTime; 108 | XCTAssertTrue(actualTime >= 9.9, @"timeout was %.2f", actualTime); 109 | XCTAssertTrue(actualTime < 10.1, @"timeout was %.2f", actualTime); 110 | } 111 | 112 | - (void)testIsConnected { 113 | // Spawn server thread. 114 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 115 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 116 | 117 | // Check connected state before and after connecting, and after closing. 118 | XCTAssertFalse([client isConnected]); 119 | XCTAssertTrue([client connect]); 120 | XCTAssertTrue([client isConnected]); 121 | XCTAssertTrue([client close]); 122 | XCTAssertFalse([client isConnected]); 123 | 124 | // Close server socket and verify that the client knows it's no longer connected. 125 | XCTAssertTrue([client connect]); 126 | XCTAssertTrue([client isConnected]); 127 | XCTAssertTrue([server close]); 128 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.2]]; 129 | XCTAssertFalse([client isConnected]); 130 | } 131 | 132 | - (void)testIsConnectedAfterRemoteClose { 133 | // Spawn server thread. 134 | [NSThread detachNewThreadSelector:@selector(listentAndClose:) toTarget:self withObject:@2]; 135 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 136 | 137 | // Check connected state before and after connecting, and after closing. 138 | XCTAssertFalse([client isConnected]); 139 | XCTAssertTrue([client connect]); 140 | XCTAssertTrue([client isConnected]); 141 | 142 | // Wait for server to close. 143 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]]; 144 | NSLog(@"checking connected status"); 145 | XCTAssertFalse([client isConnected]); 146 | } 147 | 148 | - (void)testTimeoutBefore { 149 | // Spawn server thread. 150 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 151 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 152 | 153 | // Set value before connect. 154 | XCTAssertTrue([client setTimeout:100]); 155 | XCTAssertTrue([client connect]); 156 | XCTAssertEqual([client timeout], 100); 157 | XCTAssertTrue([client close]); 158 | } 159 | 160 | - (void)testTimeoutAfter { 161 | // Spawn server thread. 162 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 163 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 164 | 165 | // Set value after connect. 166 | XCTAssertTrue([client connect]); 167 | XCTAssertTrue([client setTimeout:100]); 168 | XCTAssertEqual([client timeout], 100); 169 | XCTAssertTrue([client close]); 170 | } 171 | 172 | - (void)testTimeoutMultipleBefore { 173 | // Spawn server thread. 174 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 175 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 176 | 177 | // Set value twice before connect. 178 | XCTAssertTrue([client setTimeout:100]); 179 | XCTAssertTrue([client setTimeout:101]); 180 | XCTAssertTrue([client connect]); 181 | XCTAssertEqual([client timeout], 101); 182 | XCTAssertTrue([client close]); 183 | } 184 | 185 | - (void)testTimeoutMultipleAfter { 186 | // Spawn server thread. 187 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 188 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 189 | 190 | // Set value twice after connect. 191 | XCTAssertTrue([client connect]); 192 | XCTAssertTrue([client setTimeout:100]); 193 | XCTAssertTrue([client setTimeout:101]); 194 | XCTAssertEqual([client timeout], 101); 195 | XCTAssertTrue([client close]); 196 | } 197 | 198 | - (void)testTimeoutSubsecondBefore { 199 | // Spawn server thread. 200 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 201 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 202 | 203 | // Set value before connect. 204 | float timeout = arc4random_uniform(1000) / 1000.0f; 205 | XCTAssertTrue([client setTimeout:timeout]); 206 | XCTAssertTrue([client connect]); 207 | XCTAssertEqual([client timeout], timeout); 208 | XCTAssertTrue([client close]); 209 | } 210 | 211 | - (void)testTimeoutSubsecondAfter { 212 | // Spawn server thread. 213 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 214 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 215 | 216 | // Set value after connect. 217 | float timeout = arc4random_uniform(1000) / 1000.0f; 218 | XCTAssertTrue([client connect]); 219 | XCTAssertTrue([client setTimeout:timeout]); 220 | XCTAssertEqual([client timeout], timeout); 221 | XCTAssertTrue([client close]); 222 | } 223 | 224 | - (void)testClearTimeout { 225 | // Spawn server thread. 226 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 227 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 228 | 229 | // Set value before connect. 230 | XCTAssertTrue([client connect]); 231 | XCTAssertEqual([client timeout], 0.0f); 232 | XCTAssertTrue([client setTimeout:100]); 233 | XCTAssertTrue([client setTimeout:0]); 234 | XCTAssertEqual([client timeout], 0.0f); 235 | XCTAssertTrue([client close]); 236 | } 237 | 238 | - (void)testSegmentSizeBefore { 239 | // Spawn server thread. 240 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 241 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 242 | 243 | // Set value before connect. 244 | XCTAssertTrue([client setSegmentSize:10000]); 245 | XCTAssertTrue([client connect]); 246 | XCTAssertEqual([client segmentSize], 10000); 247 | XCTAssertTrue([client close]); 248 | } 249 | 250 | - (void)testSegmentSizeAfter { 251 | // Spawn server thread. 252 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 253 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 254 | 255 | // Set value after connect. 256 | XCTAssertTrue([client connect]); 257 | XCTAssertTrue([client setSegmentSize:10000]); 258 | XCTAssertEqual([client segmentSize], 10000); 259 | XCTAssertTrue([client close]); 260 | } 261 | 262 | - (void)testSegmentSizeMultipleBefore { 263 | // Spawn server thread. 264 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 265 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 266 | 267 | // Set value twice before connect. 268 | XCTAssertTrue([client setSegmentSize:10000]); 269 | XCTAssertTrue([client setSegmentSize:10001]); 270 | XCTAssertTrue([client connect]); 271 | XCTAssertEqual([client segmentSize], 10001); 272 | XCTAssertTrue([client close]); 273 | } 274 | 275 | - (void)testSegmentSizeMultipleAfter { 276 | // Spawn server thread. 277 | [NSThread detachNewThreadSelector:@selector(simpleListen:) toTarget:self withObject:nil]; 278 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 279 | 280 | // Set value twice after connect. 281 | XCTAssertTrue([client connect]); 282 | XCTAssertTrue([client setSegmentSize:10000]); 283 | XCTAssertFalse([client setSegmentSize:10001]); 284 | XCTAssertTrue([client setSegmentSize:9999]); 285 | XCTAssertEqual([client segmentSize], 9999); 286 | XCTAssertTrue([client close]); 287 | } 288 | 289 | - (void)testSendingAndReceivingBytes { 290 | // Spawn server thread. 291 | [NSThread detachNewThreadSelector:@selector(listenAndRepeat:) toTarget:self withObject:nil]; 292 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 293 | 294 | // Send byte array. 295 | long len = 10; 296 | unsigned char sent[] = {1, -2, 3, -4, 5, -6, 7, -8, 9, 0}; 297 | [client connect]; 298 | XCTAssertEqual([client sendBytes:sent count:len], len, @"send error: %@", [client lastError]); 299 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 300 | 301 | // Receive byte array. 302 | unsigned char received[len]; 303 | XCTAssertEqual([client receiveBytes:received count:len], len, @"receive error: %@", [client lastError]); 304 | 305 | // Compare results. 306 | XCTAssertEqual(memcmp(sent, received, len), 0); 307 | } 308 | 309 | - (void)testSendingAndReceivingRandomBytes { 310 | // Spawn server thread. 311 | [NSThread detachNewThreadSelector:@selector(listenAndRepeat:) toTarget:self withObject:nil]; 312 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 313 | 314 | // Create a random byte array larger than the buffer. 315 | long len = 1024 * 10 * 200; 316 | unsigned char sent[len]; 317 | for (int i = 0; i < len; ++i) { 318 | sent[i] = (unsigned char)(random() % 256); 319 | } 320 | NSLog(@"sending %li bytes", len); 321 | 322 | // Send the array. 323 | [client connect]; 324 | XCTAssertEqual([client sendBytes:sent count:len], len, @"send error: %@", [client lastError]); 325 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 326 | 327 | // Receive the array. 328 | unsigned char received[len]; 329 | XCTAssertEqual([client receiveBytes:received count:len], len, @"receive error: %@", [client lastError]); 330 | 331 | // Compare results. 332 | XCTAssertEqual(memcmp(sent, received, len), 0); 333 | } 334 | 335 | - (void)testSendingAndReceivingStrings { 336 | // Spawn server thread. 337 | [NSThread detachNewThreadSelector:@selector(listenAndRepeat:) toTarget:self withObject:nil]; 338 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 339 | [client connect]; 340 | 341 | // Send the string. 342 | NSString *original = @"This is å striñg to tëst séndîng. 😎"; 343 | NSData *data = [original dataUsingEncoding:NSUTF8StringEncoding]; 344 | long len = [data length]; 345 | XCTAssertEqual([client sendBytes:[data bytes] count:len], len, @"send error: %@", [client lastError]); 346 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 347 | 348 | // Receive the string. 349 | char bytes[len]; 350 | XCTAssertEqual([client receiveBytes:bytes count:len], len, @"receive error: %@", [client lastError]); 351 | NSString *received = [[NSString alloc] initWithBytes:bytes length:len encoding:NSUTF8StringEncoding]; 352 | 353 | // Compare results. 354 | XCTAssertEqualObjects(received, original); 355 | } 356 | 357 | - (void)testReceiveBytesWithDelay { 358 | int count = 10; 359 | 360 | // Spawn server thread. 361 | [NSThread detachNewThreadSelector:@selector(sendWithDelay:) toTarget:self withObject:@(count)]; 362 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 363 | 364 | // Receive the array. 365 | [client connect]; 366 | unsigned char received[count]; 367 | memset(received, 0, count); 368 | XCTAssertEqual([client receiveBytes:received count:count], count, @"receive error: %@", [client lastError]); 369 | XCTAssertEqual(received[count - 1], count - 1, @"incorrect result"); 370 | } 371 | 372 | - (void)testReceiveBytesWithTimeout { 373 | // Spawn server thread. 374 | [NSThread detachNewThreadSelector:@selector(listenAndRepeat:) toTarget:self withObject:nil]; 375 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 376 | 377 | // Send byte array. 378 | long len = 10; 379 | unsigned char sent[len]; 380 | memset(sent, 1, len); 381 | [client connect]; 382 | XCTAssertEqual([client sendBytes:sent count:len], len, @"send error: %@", [client lastError]); 383 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 384 | 385 | // Receive byte array. 386 | unsigned char received[len + 1]; 387 | [client setTimeout:3]; 388 | 389 | NSTimeInterval startTime = [NSDate timeIntervalSinceReferenceDate]; 390 | XCTAssertEqual([client receiveBytes:received count:(len + 1)], len, @"receive error: %@", [client lastError]); 391 | NSTimeInterval endTime = [NSDate timeIntervalSinceReferenceDate]; 392 | XCTAssertNotNil([client lastError]); 393 | 394 | // Check the duration of the timeout. 395 | NSTimeInterval actualTime = endTime - startTime; 396 | XCTAssertTrue(actualTime >= 2.9, @"timeout was %.2f", actualTime); 397 | XCTAssertTrue(actualTime < 3.1, @"timeout was %.2f", actualTime); 398 | 399 | // Compare results. 400 | XCTAssertEqual(memcmp(sent, received, len), 0); 401 | } 402 | 403 | - (void)testChecksum { 404 | int count = 10; 405 | 406 | // Spawn server thread. 407 | [NSThread detachNewThreadSelector:@selector(sendWithDelay:) toTarget:self withObject:@(count)]; 408 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 409 | 410 | // Receive the array. 411 | [client connect]; 412 | unsigned char received[count]; 413 | memset(received, 0, count); 414 | 415 | NSData *checksum = nil; 416 | long bytesReceived = [client receiveFile:@"/tmp/test.txt" length:count md5:&checksum]; 417 | XCTAssertEqual(bytesReceived, count, @"receive error: %@", [client lastError]); 418 | 419 | NSMutableString *checksumString = [NSMutableString stringWithCapacity:checksum.length * 2]; 420 | const unsigned char *bytes = checksum.bytes; 421 | for (NSUInteger i = 0; i < checksum.length; ++i) { 422 | [checksumString appendFormat:@"%02x", (unsigned int)bytes[i]]; 423 | } 424 | XCTAssertEqualObjects(checksumString, @"c56bd5480f6e5413cb62a0ad9666613a", @"incorrect checksum"); 425 | } 426 | 427 | #pragma mark Helpers 428 | 429 | - (void)simpleListen:(id)obj { 430 | @autoreleasepool { 431 | [server listen]; // Incoming connections just queue up. 432 | } 433 | } 434 | 435 | - (void)listentAndClose:(NSNumber *)delay { 436 | @autoreleasepool { 437 | NSLog(@"started listening"); 438 | [server listen]; 439 | 440 | // Wait before closing. 441 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:delay.doubleValue]]; 442 | NSLog(@"closing connection"); 443 | [server close]; 444 | } 445 | } 446 | 447 | - (void)listenAndRepeat:(id)obj { 448 | @autoreleasepool { 449 | NSLog(@"started listening"); 450 | [server listen]; 451 | 452 | FastSocket *incoming = [server accept]; 453 | if (!incoming) { 454 | NSLog(@"accept error: %@", [server lastError]); 455 | return; 456 | } 457 | 458 | // Read some bytes then echo them back. 459 | int bufSize = 2048; 460 | unsigned char buf[bufSize]; 461 | long count = 0; 462 | do { 463 | // Read bytes. 464 | count = [incoming receiveBytes:buf limit:bufSize]; 465 | 466 | // Write bytes. 467 | long remaining = count; 468 | while (remaining > 0) { 469 | count = [incoming sendBytes:buf count:remaining]; 470 | remaining -= count; 471 | } 472 | 473 | // Allow other threads to work. 474 | [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 475 | } while (count > 0); 476 | 477 | XCTAssertEqual(count, 0, @"error: %@", [incoming lastError]); 478 | } 479 | } 480 | 481 | - (void)sendWithDelay:(NSNumber *)count { 482 | @autoreleasepool { 483 | NSLog(@"started listening"); 484 | [server listen]; 485 | 486 | FastSocket *incoming = [server accept]; 487 | if (!incoming) { 488 | NSLog(@"accept error: %@", [server lastError]); 489 | return; 490 | } 491 | 492 | // Send each byte 0.5 sec apart. 493 | long send = 0; 494 | unsigned char buf[1]; 495 | for (char i = 0; i < count.intValue; ++i) { 496 | buf[0] = i; 497 | send += [incoming sendBytes:buf count:1]; 498 | 499 | [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.5]]; 500 | } 501 | 502 | XCTAssertEqual(send, 0, @"error: %@", [incoming lastError]); 503 | } 504 | } 505 | 506 | @end 507 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011-2017 Daniel Reese 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | FastSocket 2 | =============== 3 | 4 | Description 5 | --------------- 6 | 7 | A high-throughput, synchronous Objective-C wrapper around BSD sockets for iOS and macOS. 8 | Send and receive raw bytes over a socket as fast as possible. Includes methods 9 | for transferring files while optionally computing a checksum for verification. 10 | Swift compatible. 11 | 12 | Use this class if fast network communication is what you need. If you want to 13 | do something else while your network operations finish, then an asynchronous 14 | API might be better. 15 | 16 | For more information, please visit the [project homepage](http://github.com/dreese/FastSocket). 17 | FastSocket is also available as a [CocoaPod](http://cocoapods.org/?q=fastsocket). 18 | 19 | Download 20 | --------------- 21 | 22 | Download the [latest release](https://github.com/dreese/FastSocket/releases) of FastSocket or try the [nightly version](https://github.com/dreese/FastSocket/archive/master.zip). 23 | 24 | Examples 25 | --------------- 26 | 27 | Create and connect a client socket. 28 | 29 | FastSocket *client = [[FastSocket alloc] initWithHost:@"localhost" andPort:@"34567"]; 30 | [client connect]; 31 | 32 | Send a file. 33 | 34 | long sent = [client sendFile:@"/tmp/filetosend.txt"]; 35 | 36 | Receive a file of a given length. 37 | 38 | long received = [client receiveFile:@"/tmp/newlyreceivedfile.txt" length:1024]; 39 | 40 | Send a string. 41 | 42 | NSData *data = [@"test" dataUsingEncoding:NSUTF8StringEncoding]; 43 | long count = [client sendBytes:[data bytes] count:[data length]]; 44 | 45 | Receive a string. 46 | 47 | char bytes[expectedLength]; 48 | [client receiveBytes:bytes count:expectedLength]; 49 | NSString *received = [[NSString alloc] initWithBytes:bytes length:expectedLength encoding:NSUTF8StringEncoding]; 50 | 51 | Send raw bytes. 52 | 53 | char data[] = {42}; 54 | long sent = [client sendBytes:data count:1]; 55 | 56 | Receive available raw bytes up to the given limit. 57 | 58 | char data[42]; 59 | long received = [client receiveBytes:data limit:42]; 60 | 61 | Receive the exact number of raw bytes given. 62 | 63 | char data[1000]; 64 | long received = [client receiveBytes:data count:1000]; 65 | 66 | Close the connection. 67 | 68 | [client close]; 69 | 70 | Please check out the unit tests for more examples of how to use these classes. 71 | 72 | Creator 73 | --------------- 74 | 75 | [Daniel Reese](http://www.danielreese.com/) 76 | [@dreese](http://twitter.com/dreese) 77 | 78 | License 79 | --------------- 80 | 81 | FastSocket is available under the [MIT license](http://opensource.org/licenses/MIT). 82 | -------------------------------------------------------------------------------- /UnitTests-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundlePackageType 14 | BNDL 15 | CFBundleShortVersionString 16 | 1.0 17 | CFBundleSignature 18 | ???? 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | --------------------------------------------------------------------------------