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 : connection_(connection),
27 runLoop_(CFRunLoopGetCurrent())
31 void NetworkCallbackController::OpenConnection(NSUInteger port)
33 // Pass ourselves to the callback so we don't have to use ugly globals.
34 CFSocketContext context = { 0 };
37 // Create the address structure.
38 struct sockaddr_in address;
39 memset(&address, 0, sizeof(address));
40 address.sin_len = sizeof(address);
41 address.sin_family = AF_INET;
42 address.sin_port = htons(port);
43 address.sin_addr.s_addr = htonl(INADDR_ANY);
45 // Create the socket signature.
46 CFSocketSignature signature;
47 signature.protocolFamily = PF_INET;
48 signature.socketType = SOCK_STREAM;
49 signature.protocol = IPPROTO_TCP;
50 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
53 socket_ = CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
54 &signature, // Socket signature.
55 kCFSocketAcceptCallBack, // Callback types.
56 &NetworkCallbackController::SocketAcceptCallback, // Callout function pointer.
57 &context); // Context to pass to callout.
59 [connection_ errorEncountered:@"Could not open socket."];
64 // Allow old, yet-to-be recycled sockets to be reused.
66 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
67 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(BOOL));
69 // Schedule the socket on the run loop.
70 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 0);
71 CFRunLoopAddSource(runLoop_, source, kCFRunLoopCommonModes);
75 void NetworkCallbackController::CloseConnection()
78 NSLog(@"invalidating socket %d", close(CFSocketGetNative(socket_)));
79 CFSocketInvalidate(socket_);
80 NSLog(@"socket is valid %d", CFSocketIsValid(socket_));
84 UnscheduleReadStream();
85 UnscheduleWriteStream();
88 // Static Methods //////////////////////////////////////////////////////////////
90 void NetworkCallbackController::SocketAcceptCallback(CFSocketRef socket,
91 CFSocketCallBackType callbackType,
96 assert(callbackType == kCFSocketAcceptCallBack);
97 static_cast<NetworkCallbackController*>(self)->OnSocketAccept(socket, address, data);
100 void NetworkCallbackController::ReadStreamCallback(CFReadStreamRef stream,
101 CFStreamEventType eventType,
104 static_cast<NetworkCallbackController*>(self)->OnReadStreamEvent(stream, eventType);
107 void NetworkCallbackController::WriteStreamCallback(CFWriteStreamRef stream,
108 CFStreamEventType eventType,
111 static_cast<NetworkCallbackController*>(self)->OnWriteStreamEvent(stream, eventType);
115 // Private Instance Methods ////////////////////////////////////////////////////
117 void NetworkCallbackController::OnSocketAccept(CFSocketRef socket,
121 CFReadStreamRef readStream;
122 CFWriteStreamRef writeStream;
124 // Create the streams on the socket.
125 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
126 *(CFSocketNativeHandle*)data, // Socket handle.
127 &readStream, // Read stream in-pointer.
128 &writeStream); // Write stream in-pointer.
130 // Create struct to register callbacks for the stream.
131 CFStreamClientContext context = { 0 };
134 // Set the client of the read stream.
135 CFOptionFlags readFlags = kCFStreamEventOpenCompleted |
136 kCFStreamEventHasBytesAvailable |
137 kCFStreamEventErrorOccurred |
138 kCFStreamEventEndEncountered;
139 if (CFReadStreamSetClient(readStream, readFlags, &NetworkCallbackController::ReadStreamCallback, &context))
140 // Schedule in run loop to do asynchronous communication with the engine.
141 CFReadStreamScheduleWithRunLoop(readStream, runLoop_, kCFRunLoopCommonModes);
145 // Open the stream now that it's scheduled on the run loop.
146 if (!CFReadStreamOpen(readStream)) {
147 ReportError(CFReadStreamCopyError(readStream));
151 // Set the client of the write stream.
152 CFOptionFlags writeFlags = kCFStreamEventOpenCompleted |
153 kCFStreamEventCanAcceptBytes |
154 kCFStreamEventErrorOccurred |
155 kCFStreamEventEndEncountered;
156 if (CFWriteStreamSetClient(writeStream, writeFlags, &NetworkCallbackController::WriteStreamCallback, &context))
157 // Schedule it in the run loop to receive error information.
158 CFWriteStreamScheduleWithRunLoop(writeStream, runLoop_, kCFRunLoopCommonModes);
162 // Open the write stream.
163 if (!CFWriteStreamOpen(writeStream)) {
164 ReportError(CFWriteStreamCopyError(writeStream));
168 connection_.readStream = readStream;
169 connection_.writeStream = writeStream;
170 [connection_ socketDidAccept];
173 void NetworkCallbackController::OnReadStreamEvent(CFReadStreamRef stream,
174 CFStreamEventType eventType)
178 case kCFStreamEventHasBytesAvailable:
179 if (connection_.readStream)
180 [connection_ readStreamHasData];
183 case kCFStreamEventErrorOccurred:
184 NSLog(@"%s error", __PRETTY_FUNCTION__);
185 ReportError(CFReadStreamCopyError(stream));
186 UnscheduleReadStream();
189 case kCFStreamEventEndEncountered:
190 NSLog(@"%s end", __PRETTY_FUNCTION__);
191 UnscheduleReadStream();
192 [connection_ socketDisconnected];
197 void NetworkCallbackController::OnWriteStreamEvent(CFWriteStreamRef stream,
198 CFStreamEventType eventType)
202 case kCFStreamEventCanAcceptBytes:
203 [connection_ sendQueuedWrites];
206 case kCFStreamEventErrorOccurred:
207 ReportError(CFWriteStreamCopyError(stream));
208 UnscheduleWriteStream();
211 case kCFStreamEventEndEncountered:
212 UnscheduleReadStream();
213 [connection_ socketDisconnected];
218 void NetworkCallbackController::UnscheduleReadStream()
220 CFReadStreamUnscheduleFromRunLoop(connection_.readStream, runLoop_, kCFRunLoopCommonModes);
221 CFReadStreamClose(connection_.readStream);
222 CFRelease(connection_.readStream);
223 connection_.readStream = NULL;
226 void NetworkCallbackController::UnscheduleWriteStream()
228 CFWriteStreamUnscheduleFromRunLoop(connection_.writeStream, runLoop_, kCFRunLoopCommonModes);
229 CFWriteStreamClose(connection_.writeStream);
230 CFRelease(connection_.writeStream);
231 connection_.writeStream = NULL;
234 void NetworkCallbackController::ReportError(CFErrorRef error)
236 [connection_ errorEncountered:[(NSError*)error description]];