5 // Created by Robert Sesek on 2/15/10.
6 // Copyright 2010 Blue Static. All rights reserved.
10 #import <netinet/in.h>
12 #import "AppDelegate.h"
14 // AppDelegate (Private) ///////////////////////////////////////////////////////
16 #define BUFFER_SIZE 1024
18 @interface AppDelegate (Private
)
19 - (void)newDataToRead
;
20 - (void)streamErrorOccured
:(NSError
*)error
;
24 // CFNetwork Callbacks /////////////////////////////////////////////////////////
25 #pragma mark CFNetwork Callbacks
27 void ReadStreamCallback(CFReadStreamRef stream
, CFStreamEventType eventType
, void* appDelegateRaw
)
29 NSLog(@
"ReadStreamCallback()");
30 AppDelegate
* appDelegate
= (AppDelegate
*)appDelegateRaw
;
33 case kCFStreamEventHasBytesAvailable
:
34 [appDelegate newDataToRead
];
37 case kCFStreamEventErrorOccurred
:
39 CFErrorRef error
= CFReadStreamCopyError(stream
);
40 CFReadStreamUnscheduleFromRunLoop(stream
, CFRunLoopGetCurrent(), kCFRunLoopCommonModes
);
41 CFReadStreamClose(stream
);
43 [appDelegate streamErrorOccured
:[(NSError
*)error autorelease
]];
47 case kCFStreamEventEndEncountered
:
48 CFReadStreamUnscheduleFromRunLoop(stream
, [[NSRunLoop currentRunLoop
] getCFRunLoop
], kCFRunLoopCommonModes
);
49 CFReadStreamClose(stream
);
51 [appDelegate disconnected
];
56 void WriteStreamCallback(CFWriteStreamRef stream
, CFStreamEventType eventType
, void* appDelegateRaw
)
58 NSLog(@
"WriteStreamCallback()");
59 AppDelegate
* appDelegate
= (AppDelegate
*)appDelegateRaw
;
62 case kCFStreamEventErrorOccurred
:
64 CFErrorRef error
= CFWriteStreamCopyError(stream
);
65 CFWriteStreamUnscheduleFromRunLoop(stream
, CFRunLoopGetCurrent(), kCFRunLoopCommonModes
);
66 CFWriteStreamClose(stream
);
68 [appDelegate streamErrorOccured
:[(NSError
*)error autorelease
]];
72 case kCFStreamEventEndEncountered
:
73 CFWriteStreamUnscheduleFromRunLoop(stream
, [[NSRunLoop currentRunLoop
] getCFRunLoop
], kCFRunLoopCommonModes
);
74 CFWriteStreamClose(stream
);
76 [appDelegate disconnected
];
81 void SocketAcceptCallback(CFSocketRef socket
, CFSocketCallBackType callbackType
, CFDataRef address
, const void* data
, void* appDelegateRaw
)
83 assert(callbackType
== kCFSocketAcceptCallBack
);
84 NSLog(@
"SocketAcceptCallback()");
86 AppDelegate
* appDelegate
= (AppDelegate
*)appDelegateRaw
;
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.
94 // Create struct to register callbacks for the stream.
95 CFStreamClientContext context
;
97 context.info
= appDelegate
;
98 context.retain
= NULL
;
99 context.release
= NULL
;
100 context.copyDescription
= NULL
;
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
);
114 NSLog(@
"Read stream scheduled");
116 // Open the stream now that it's scheduled on the run loop.
117 if (!CFReadStreamOpen(appDelegate
->readStream_
))
119 CFStreamError error
= CFReadStreamGetError(appDelegate
->readStream_
);
120 NSLog(@
"error! %@", error
);
124 NSLog(@
"Read stream opened");
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
);
137 NSLog(@
"Write stream scheduled");
139 // Open the write stream.
140 if (!CFWriteStreamOpen(appDelegate
->writeStream_
))
142 CFStreamError error
= CFWriteStreamGetError(appDelegate
->writeStream_
);
143 NSLog(@
"error! %@", error
);
147 NSLog(@
"Write stream opened");
150 // SocketRLTestAppDelegate /////////////////////////////////////////////////////
152 @implementation AppDelegate
154 @synthesize window
= window_
;
155 @synthesize commandField
= commandField_
;
156 @synthesize resultView
= resultView_
;
157 @synthesize currentPacket
= currentPacket_
;
159 - (void)applicationDidFinishLaunching
:(NSNotification
*)aNotification
161 // Pass ourselves to the callback so we don't have to use ugly globals.
162 CFSocketContext context
;
165 context.retain
= NULL
;
166 context.release
= NULL
;
167 context.copyDescription
= NULL
;
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
);
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
)];
184 socket_
= CFSocketCreateWithSocketSignature(kCFAllocatorDefault
,
185 &signature
, // Socket signature.
186 kCFSocketAcceptCallBack
, // Callback types.
187 SocketAcceptCallback
, // Callout function pointer.
188 &context
); // Context to pass to callout.
191 NSLog(@
"socket error");
195 // Allow old, yet-to-be recycled sockets to be reused.
197 setsockopt(CFSocketGetNative(socket_
), SOL_SOCKET
, SO_REUSEADDR
, &yes
, sizeof(BOOL));
199 // Schedule the socket on the run loop.
200 CFRunLoopSourceRef source
= CFSocketCreateRunLoopSource(kCFAllocatorDefault
, socket_
, 0);
201 CFRunLoopAddSource([[NSRunLoop currentRunLoop
] getCFRunLoop
], source
, kCFRunLoopCommonModes
);
207 // The socket goes down, so do the streams, which clean themselves up.
208 CFSocketInvalidate(socket_
);
210 self.currentPacket
= nil;
214 - (IBAction
)send
:(id)sender
218 char* string
= (char*)[[self.commandField stringValue
] UTF8String
];
219 int stringLength
= strlen(string
);
221 // Busy wait while writing. BAADD. Should background this operation.
224 if (CFWriteStreamCanAcceptBytes(writeStream_
))
226 // Include the NULL byte in the string when we write.
227 int bytesWritten
= CFWriteStreamWrite(writeStream_
, (UInt8
*)string
, stringLength
+ 1);
228 if (bytesWritten
< 0)
230 NSLog(@
"write error");
233 else if (bytesWritten
< strlen(string
))
235 // Adjust the buffer and wait for another chance to write.
236 stringLength
-= bytesWritten
;
237 memmove(string
, string
+ bytesWritten
, stringLength
);
247 // AppDelegate (Private) ///////////////////////////////////////////////////////
250 - (void)newDataToRead
252 UInt8 buffer
[BUFFER_SIZE
];
253 CFIndex bytesRead
= CFReadStreamRead(readStream_
, buffer
, BUFFER_SIZE
);
254 const char* charBuffer
= (const char*)buffer
;
256 // We haven't finished reading a packet, so just read more data in.
257 if (packetIndex_
< lastPacketSize_
)
259 [currentPacket_ appendFormat
:@
"%s", buffer
];
260 packetIndex_
+= bytesRead
;
262 // Time to read a new packet.
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];
271 // We have finished reading the packet.
272 if (packetIndex_
>= lastPacketSize_
)
277 // Update the displays.
278 [self.resultView setString
:currentPacket_
];
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
];
284 NSLog(@
"FAILED XML TEST: %@", error
);
289 - (void)streamErrorOccured
:(NSError
*)error
291 NSLog(@
"stream error: %@", error
);