3 * Copyright (c) 2007 - 2011, Blue Static <http://www.bluestatic.org>
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 #import "NetworkCallbackController.h"
19 #import <sys/socket.h>
20 #import <netinet/in.h>
22 #import "NetworkConnection.h"
23 #import "NetworkConnectionPrivate.h"
25 NetworkCallbackController::NetworkCallbackController(NetworkConnection* connection)
28 connection_(connection),
29 runLoop_(CFRunLoopGetCurrent())
33 void NetworkCallbackController::OpenConnection(NSUInteger port)
35 // Pass ourselves to the callback so we don't have to use ugly globals.
36 CFSocketContext context = { 0 };
39 // Create the address structure.
40 struct sockaddr_in address;
41 memset(&address, 0, sizeof(address));
42 address.sin_len = sizeof(address);
43 address.sin_family = AF_INET;
44 address.sin_port = htons(port);
45 address.sin_addr.s_addr = htonl(INADDR_ANY);
47 // Create the socket signature.
48 CFSocketSignature signature;
49 signature.protocolFamily = PF_INET;
50 signature.socketType = SOCK_STREAM;
51 signature.protocol = IPPROTO_TCP;
52 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
55 socket_ = CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
56 &signature, // Socket signature.
57 kCFSocketAcceptCallBack, // Callback types.
58 &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer.
59 &context); // Context to pass to callout.
61 [connection_ errorEncountered:@"Could not open socket."];
66 // Allow old, yet-to-be recycled sockets to be reused.
68 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
69 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL));
71 // Schedule the socket on the run loop.
72 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 0);
73 CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes);
77 void NetworkCallbackController::CloseConnection()
80 CFSocketInvalidate(socket_);
84 UnscheduleReadStream();
85 UnscheduleWriteStream();
88 BOOL NetworkCallbackController::WriteStreamCanAcceptBytes()
90 return writeStream_ && CFWriteStreamCanAcceptBytes(writeStream_);
93 BOOL NetworkCallbackController::WriteString(NSString* string)
97 char* cString = const_cast<char*>([string UTF8String]);
98 size_t stringLength = strlen(cString);
100 // Busy wait while writing. BAADD. Should background this operation.
102 if (WriteStreamCanAcceptBytes()) {
103 // Include the NULL byte in the string when we write.
104 CFIndex bytesWritten = CFWriteStreamWrite(writeStream_, (UInt8*)cString, stringLength + 1);
105 if (bytesWritten < 0) {
106 CFErrorRef error = CFWriteStreamCopyError(writeStream_);
111 else if (bytesWritten < static_cast<CFIndex>(strlen(cString))) {
112 // Adjust the buffer and wait for another chance to write.
113 stringLength -= bytesWritten;
114 memmove(string, string + bytesWritten, stringLength);
125 // Static Methods //////////////////////////////////////////////////////////////
127 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
128 CFSocketCallBackType callbackType,
133 assert(callbackType == kCFSocketAcceptCallBack);
134 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
137 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
138 CFStreamEventType eventType,
141 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
144 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
145 CFStreamEventType eventType,
148 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
152 // Private Instance Methods ////////////////////////////////////////////////////
154 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
158 // Create the streams on the socket.
159 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
160 *(CFSocketNativeHandle*)data, // Socket handle.
161 &readStream_, // Read stream in-pointer.
162 &writeStream_); // Write stream in-pointer.
164 // Create struct to register callbacks for the stream.
165 CFStreamClientContext context = { 0 };
168 // Set the client of the read stream.
169 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
170 kCFStreamEventHasBytesAvailable |
171 kCFStreamEventErrorOccurred |
172 kCFStreamEventEndEncountered;
173 if (CFReadStreamSetClient(readStream_, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
174 // Schedule in run loop to do asynchronous communication with the engine.
175 CFReadStreamScheduleWithRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
179 // Open the stream now that it's scheduled on the run loop.
180 if (!CFReadStreamOpen(readStream_)) {
181 ReportError(CFReadStreamCopyError(readStream_));
185 // Set the client of the write stream.
186 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
187 kCFStreamEventCanAcceptBytes |
188 kCFStreamEventErrorOccurred |
189 kCFStreamEventEndEncountered;
190 if (CFWriteStreamSetClient(writeStream_, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
191 // Schedule it in the run loop to receive error information.
192 CFWriteStreamScheduleWithRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
196 // Open the write stream.
197 if (!CFWriteStreamOpen(writeStream_)) {
198 ReportError(CFWriteStreamCopyError(writeStream_));
202 [connection_ socketDidAccept];
205 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
206 CFStreamEventType eventType)
210 case kCFStreamEventHasBytesAvailable:
212 [connection_ readStreamHasData:stream];
215 case kCFStreamEventErrorOccurred:
216 ReportError(CFReadStreamCopyError(stream));
217 UnscheduleReadStream();
220 case kCFStreamEventEndEncountered:
221 UnscheduleReadStream();
222 [connection_ socketDisconnected];
227 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
228 CFStreamEventType eventType)
232 case kCFStreamEventCanAcceptBytes:
233 [connection_ sendQueuedWrites];
236 case kCFStreamEventErrorOccurred:
237 ReportError(CFWriteStreamCopyError(stream));
238 UnscheduleWriteStream();
241 case kCFStreamEventEndEncountered:
242 UnscheduleReadStream();
243 [connection_ socketDisconnected];
248 void NetworkCallbackController::UnscheduleReadStream()
252 CFReadStreamUnscheduleFromRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
253 CFReadStreamClose(readStream_);
254 CFRelease(readStream_);
258 void NetworkCallbackController::UnscheduleWriteStream()
262 CFWriteStreamUnscheduleFromRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
263 CFWriteStreamClose(writeStream_);
264 CFRelease(writeStream_);
268 void NetworkCallbackController::ReportError(CFErrorRef error)
270 [connection_ errorEncountered:[(NSError*)error description]];