Bump project version to 212.1.
[macgdbp.git] / dev / CFNetworkTest / AppDelegate.m
1 //
2 // AppDelegate.m
3 // CFNetworkTest
4 //
5 // Created by Robert Sesek on 2/15/10.
6 // Copyright 2010 Blue Static. All rights reserved.
7 //
8
9 #import <sys/socket.h>
10 #import <netinet/in.h>
11
12 #import "AppDelegate.h"
13
14 // AppDelegate (Private) ///////////////////////////////////////////////////////
15
16 #define BUFFER_SIZE 1024
17
18 @interface AppDelegate (Private)
19 - (void)newDataToRead;
20 - (void)streamErrorOccured:(NSError*)error;
21 - (void)disconnected;
22 @end
23
24 // CFNetwork Callbacks /////////////////////////////////////////////////////////
25 #pragma mark CFNetwork Callbacks
26
27 void ReadStreamCallback(CFReadStreamRef stream, CFStreamEventType eventType, void* appDelegateRaw)
28 {
29 NSLog(@"ReadStreamCallback()");
30 AppDelegate* appDelegate = (AppDelegate*)appDelegateRaw;
31 switch (eventType)
32 {
33 case kCFStreamEventHasBytesAvailable:
34 [appDelegate newDataToRead];
35 break;
36
37 case kCFStreamEventErrorOccurred:
38 {
39 CFErrorRef error = CFReadStreamCopyError(stream);
40 CFReadStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
41 CFReadStreamClose(stream);
42 CFRelease(stream);
43 [appDelegate streamErrorOccured:[(NSError*)error autorelease]];
44 break;
45 }
46
47 case kCFStreamEventEndEncountered:
48 CFReadStreamUnscheduleFromRunLoop(stream, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes);
49 CFReadStreamClose(stream);
50 CFRelease(stream);
51 [appDelegate disconnected];
52 break;
53 };
54 }
55
56 void WriteStreamCallback(CFWriteStreamRef stream, CFStreamEventType eventType, void* appDelegateRaw)
57 {
58 NSLog(@"WriteStreamCallback()");
59 AppDelegate* appDelegate = (AppDelegate*)appDelegateRaw;
60 switch (eventType)
61 {
62 case kCFStreamEventErrorOccurred:
63 {
64 CFErrorRef error = CFWriteStreamCopyError(stream);
65 CFWriteStreamUnscheduleFromRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
66 CFWriteStreamClose(stream);
67 CFRelease(stream);
68 [appDelegate streamErrorOccured:[(NSError*)error autorelease]];
69 break;
70 }
71
72 case kCFStreamEventEndEncountered:
73 CFWriteStreamUnscheduleFromRunLoop(stream, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes);
74 CFWriteStreamClose(stream);
75 CFRelease(stream);
76 [appDelegate disconnected];
77 break;
78 }
79 }
80
81 void SocketAcceptCallback(CFSocketRef socket, CFSocketCallBackType callbackType, CFDataRef address, const void* data, void* appDelegateRaw)
82 {
83 assert(callbackType == kCFSocketAcceptCallBack);
84 NSLog(@"SocketAcceptCallback()");
85
86 AppDelegate* appDelegate = (AppDelegate*)appDelegateRaw;
87
88 // Create the streams on the socket.
89 CFStreamCreatePairWithSocket(kCFAllocatorDefault,
90 *(CFSocketNativeHandle*)data, // Socket handle.
91 &appDelegate->readStream_, // Read stream in-pointer.
92 &appDelegate->writeStream_); // Write stream in-pointer.
93
94 // Create struct to register callbacks for the stream.
95 CFStreamClientContext context;
96 context.version = 0;
97 context.info = appDelegate;
98 context.retain = NULL;
99 context.release = NULL;
100 context.copyDescription = NULL;
101
102 // Set the client of the read stream.
103 CFOptionFlags readFlags =
104 kCFStreamEventOpenCompleted |
105 kCFStreamEventHasBytesAvailable |
106 kCFStreamEventErrorOccurred |
107 kCFStreamEventEndEncountered;
108 if (CFReadStreamSetClient(appDelegate->readStream_, readFlags, ReadStreamCallback, &context))
109 // Schedule in run loop to do asynchronous communication with the engine.
110 CFReadStreamScheduleWithRunLoop(appDelegate->readStream_, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes);
111 else
112 return;
113
114 NSLog(@"Read stream scheduled");
115
116 // Open the stream now that it's scheduled on the run loop.
117 if (!CFReadStreamOpen(appDelegate->readStream_))
118 {
119 CFStreamError error = CFReadStreamGetError(appDelegate->readStream_);
120 NSLog(@"error! %@", error);
121 return;
122 }
123
124 NSLog(@"Read stream opened");
125
126 // Set the client of the write stream.
127 CFOptionFlags writeFlags =
128 kCFStreamEventOpenCompleted |
129 kCFStreamEventErrorOccurred |
130 kCFStreamEventEndEncountered;
131 if (CFWriteStreamSetClient(appDelegate->writeStream_, writeFlags, WriteStreamCallback, &context))
132 // Schedule it in the run loop to receive error information.
133 CFWriteStreamScheduleWithRunLoop(appDelegate->writeStream_, [[NSRunLoop currentRunLoop] getCFRunLoop], kCFRunLoopCommonModes);
134 else
135 return;
136
137 NSLog(@"Write stream scheduled");
138
139 // Open the write stream.
140 if (!CFWriteStreamOpen(appDelegate->writeStream_))
141 {
142 CFStreamError error = CFWriteStreamGetError(appDelegate->writeStream_);
143 NSLog(@"error! %@", error);
144 return;
145 }
146
147 NSLog(@"Write stream opened");
148 }
149
150 // SocketRLTestAppDelegate /////////////////////////////////////////////////////
151
152 @implementation AppDelegate
153
154 @synthesize window = window_;
155 @synthesize commandField = commandField_;
156 @synthesize resultView = resultView_;
157 @synthesize currentPacket = currentPacket_;
158
159 - (void)applicationDidFinishLaunching:(NSNotification*)aNotification
160 {
161 // Pass ourselves to the callback so we don't have to use ugly globals.
162 CFSocketContext context;
163 context.version = 0;
164 context.info = self;
165 context.retain = NULL;
166 context.release = NULL;
167 context.copyDescription = NULL;
168
169 // Create the address structure.
170 struct sockaddr_in address;
171 memset(&address, 0, sizeof(address));
172 address.sin_len = sizeof(address);
173 address.sin_family = AF_INET;
174 address.sin_port = htons(9000);
175 address.sin_addr.s_addr = htonl(INADDR_ANY);
176
177 // Create the socket signature.
178 CFSocketSignature signature;
179 signature.protocolFamily = PF_INET;
180 signature.socketType = SOCK_STREAM;
181 signature.protocol = IPPROTO_TCP;
182 signature.address = (CFDataRef)[NSData dataWithBytes:&address length:sizeof(address)];
183
184 socket_ = CFSocketCreateWithSocketSignature(kCFAllocatorDefault,
185 &signature, // Socket signature.
186 kCFSocketAcceptCallBack, // Callback types.
187 SocketAcceptCallback, // Callout function pointer.
188 &context); // Context to pass to callout.
189 if (!socket_)
190 {
191 NSLog(@"socket error");
192 return;
193 }
194
195 // Allow old, yet-to-be recycled sockets to be reused.
196 BOOL yes = YES;
197 setsockopt(CFSocketGetNative(socket_), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(BOOL));
198
199 // Schedule the socket on the run loop.
200 CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket_, 0);
201 CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop], source, kCFRunLoopCommonModes);
202 CFRelease(source);
203 }
204
205 - (void)dealloc
206 {
207 // The socket goes down, so do the streams, which clean themselves up.
208 CFSocketInvalidate(socket_);
209 CFRelease(socket_);
210 self.currentPacket = nil;
211 [super dealloc];
212 }
213
214 - (IBAction)send:(id)sender
215 {
216 BOOL done = NO;
217
218 char* string = (char*)[[self.commandField stringValue] UTF8String];
219 int stringLength = strlen(string);
220
221 // Busy wait while writing. BAADD. Should background this operation.
222 while (!done)
223 {
224 if (CFWriteStreamCanAcceptBytes(writeStream_))
225 {
226 // Include the NULL byte in the string when we write.
227 int bytesWritten = CFWriteStreamWrite(writeStream_, (UInt8*)string, stringLength + 1);
228 if (bytesWritten < 0)
229 {
230 NSLog(@"write error");
231 }
232 // Incomplete write.
233 else if (bytesWritten < strlen(string))
234 {
235 // Adjust the buffer and wait for another chance to write.
236 stringLength -= bytesWritten;
237 memmove(string, string + bytesWritten, stringLength);
238 }
239 else
240 {
241 done = YES;
242 }
243 }
244 }
245 }
246
247 // AppDelegate (Private) ///////////////////////////////////////////////////////
248 #pragma mark Private
249
250 - (void)newDataToRead
251 {
252 UInt8 buffer[BUFFER_SIZE];
253 CFIndex bytesRead = CFReadStreamRead(readStream_, buffer, BUFFER_SIZE);
254 const char* charBuffer = (const char*)buffer;
255
256 // We haven't finished reading a packet, so just read more data in.
257 if (packetIndex_ < lastPacketSize_)
258 {
259 [currentPacket_ appendFormat:@"%s", buffer];
260 packetIndex_ += bytesRead;
261 }
262 // Time to read a new packet.
263 else
264 {
265 // Read the message header: the size.
266 lastPacketSize_ = atoi(charBuffer);
267 packetIndex_ = bytesRead - strlen(charBuffer);
268 self.currentPacket = [NSMutableString stringWithFormat:@"%s", buffer + strlen(charBuffer) + 1];
269 }
270
271 // We have finished reading the packet.
272 if (packetIndex_ >= lastPacketSize_)
273 {
274 lastPacketSize_ = 0;
275 packetIndex_ = 0;
276
277 // Update the displays.
278 [self.resultView setString:currentPacket_];
279
280 // Test if we can convert it into an NSXMLDocument.
281 NSError* error = nil;
282 NSXMLDocument* xmlTest = [[NSXMLDocument alloc] initWithXMLString:currentPacket_ options:NSXMLDocumentTidyXML error:&error];
283 if (error)
284 NSLog(@"FAILED XML TEST: %@", error);
285 [xmlTest release];
286 }
287 }
288
289 - (void)streamErrorOccured:(NSError*)error
290 {
291 NSLog(@"stream error: %@", error);
292 }
293
294 - (void)disconnected
295 {
296 }
297
298 @end