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)
26 : listeningSocket_(NULL),
30 connection_(connection),
31 runLoop_(CFRunLoopGetCurrent())
35 void NetworkCallbackController::OpenConnection(NSUInteger port)
37 // Pass ourselves to the callback so we don't have to use ugly globals.
38 CFSocketContext context = { 0 };
41 // Create the address structure.
42 struct sockaddr_in address;
43 memset(&address, 0, sizeof(address));
44 address.sin_len = sizeof(address);
45 address.sin_family = AF_INET;
46 address.sin_port = htons(port);
47 address.sin_addr.s_addr = htonl(INADDR_ANY);
49 // Create the socket signature.
50 CFSocketSignature signature;
51 signature.protocolFamily = PF_INET;
52 signature.socketType = SOCK_STREAM;
53 signature.protocol = IPPROTO_TCP;
54 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
58 CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
59 &signature, // Socket signature.
60 kCFSocketAcceptCallBack, // Callback types.
61 &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer.
62 &context); // Context to pass to callout.
63 if (!listeningSocket_) {
64 [connection_ errorEncountered:@"Could not open socket."];
67 } while (!listeningSocket_);
69 // Allow old, yet-to-be recycled sockets to be reused.
71 setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
72 setsockopt(CFSocketGetNative(listeningSocket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL));
74 // Schedule the socket on the run loop.
75 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, listeningSocket_, 0);
76 CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes);
80 void NetworkCallbackController::CloseConnection()
84 //socketHandle_ = NULL;
86 UnscheduleReadStream();
87 UnscheduleWriteStream();
90 BOOL NetworkCallbackController::WriteStreamCanAcceptBytes()
92 return writeStream_ && CFWriteStreamCanAcceptBytes(writeStream_);
95 BOOL NetworkCallbackController::WriteString(NSString* string)
99 char* cString = const_cast<char*>([string UTF8String]);
100 size_t stringLength = strlen(cString);
102 // Busy wait while writing. BAADD. Should background this operation.
104 if (WriteStreamCanAcceptBytes()) {
105 // Include the NULL byte in the string when we write.
106 CFIndex bytesWritten = CFWriteStreamWrite(writeStream_, (UInt8*)cString, stringLength + 1);
107 if (bytesWritten < 0) {
108 CFErrorRef error = CFWriteStreamCopyError(writeStream_);
113 else if (bytesWritten < static_cast<CFIndex>(strlen(cString))) {
114 // Adjust the buffer and wait for another chance to write.
115 stringLength -= bytesWritten;
116 memmove(string, string + bytesWritten, stringLength);
127 // Static Methods //////////////////////////////////////////////////////////////
129 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
130 CFSocketCallBackType callbackType,
135 assert(callbackType == kCFSocketAcceptCallBack);
136 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
139 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
140 CFStreamEventType eventType,
143 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
146 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
147 CFStreamEventType eventType,
150 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
154 // Private Instance Methods ////////////////////////////////////////////////////
156 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
160 socketHandle_ = *(CFSocketNativeHandle*)data;
162 // Create the streams on the socket.
163 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
164 socketHandle_, // Socket handle.
165 &readStream_, // Read stream in-pointer.
166 &writeStream_); // Write stream in-pointer.
168 // Create struct to register callbacks for the stream.
169 CFStreamClientContext context = { 0 };
172 // Set the client of the read stream.
173 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
174 kCFStreamEventHasBytesAvailable |
175 kCFStreamEventErrorOccurred |
176 kCFStreamEventEndEncountered;
177 if (CFReadStreamSetClient(readStream_, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
178 // Schedule in run loop to do asynchronous communication with the engine.
179 CFReadStreamScheduleWithRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
183 // Open the stream now that it's scheduled on the run loop.
184 if (!CFReadStreamOpen(readStream_)) {
185 ReportError(CFReadStreamCopyError(readStream_));
189 // Set the client of the write stream.
190 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
191 kCFStreamEventCanAcceptBytes |
192 kCFStreamEventErrorOccurred |
193 kCFStreamEventEndEncountered;
194 if (CFWriteStreamSetClient(writeStream_, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
195 // Schedule it in the run loop to receive error information.
196 CFWriteStreamScheduleWithRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
200 // Open the write stream.
201 if (!CFWriteStreamOpen(writeStream_)) {
202 ReportError(CFWriteStreamCopyError(writeStream_));
206 [connection_ socketDidAccept];
211 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
212 CFStreamEventType eventType)
216 case kCFStreamEventHasBytesAvailable:
218 [connection_ readStreamHasData:stream];
221 case kCFStreamEventErrorOccurred:
222 ReportError(CFReadStreamCopyError(stream));
223 UnscheduleReadStream();
226 case kCFStreamEventEndEncountered:
227 UnscheduleReadStream();
229 [connection_ socketDisconnected];
234 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
235 CFStreamEventType eventType)
239 case kCFStreamEventCanAcceptBytes:
240 [connection_ sendQueuedWrites];
243 case kCFStreamEventErrorOccurred:
244 ReportError(CFWriteStreamCopyError(stream));
245 UnscheduleWriteStream();
248 case kCFStreamEventEndEncountered:
249 UnscheduleReadStream();
250 [connection_ socketDisconnected];
255 void NetworkCallbackController::CloseSocket()
257 if (listeningSocket_) {
258 CFSocketInvalidate(listeningSocket_);
259 CFRelease(listeningSocket_);
260 listeningSocket_ = NULL;
264 void NetworkCallbackController::UnscheduleReadStream()
268 CFReadStreamUnscheduleFromRunLoop(readStream_, runLoop_, kCFRunLoopCommonModes);
269 CFReadStreamClose(readStream_);
270 CFRelease(readStream_);
274 void NetworkCallbackController::UnscheduleWriteStream()
278 CFWriteStreamUnscheduleFromRunLoop(writeStream_, runLoop_, kCFRunLoopCommonModes);
279 CFWriteStreamClose(writeStream_);
280 CFRelease(writeStream_);
284 void NetworkCallbackController::ReportError(CFErrorRef error)
286 [connection_ errorEncountered:[(NSError*)error description]];