This relationship between the HTTP stream and the CFSocketStream is not part of the API. The fact that the kCFStreamPropertySocketNativeHandle ever works is just an accident of the implementation. I don't think you should rely on this in your code. Why do you need access to the underlying socket?
Dear Quinn,
Thank you very much for showing me the errors in my thinking. I mostly think in terms of Cocoa so when I saw that CFReadStreamRef was toll-free bridged to NSInputStream I erroneously thought that NSStream, on which NSInputStream is based, was somehow also related to CFStream. That would have allowed me to use kCFStreamPropertySocketNativeHandle, which seemed to work sometimes but I see was just a fluke of an implementation detail. :-)
Let me give you why I was wanting the underlying socket and maybe you can give an alternate method for what I am trying to achieve. What I was trying to do was fairly clever I thought, unfortunately it's not going to work given my flawed thinking. So as I pointed out in the previous email I am using persistent HTTP connections so I am working with CFReadStreamCreateForHTTPRequest() with the appropriate properties set on the CFReadStream to achieve that. The problem is that I might have several HTTP connections in flight at any given time. If all my CFReadStreams finish at the same approximate time the question comes to the next batch of requests, which CFReadStream is this request being enqueued on and which CFReadStream can I close?
My solution was to create a simple class that basically holds on to CFReadStreams storing them in a dictionary using the native socket as the key to the dictionary entries. When adding a new socket to the pool object it checks to see if that native socket is in the pool and if it is closes the old CFReadStream and releases it. There is also a timer that tells the 'pool boy' to come and clean the pool every now and again. As you can see from the code snippet below the native socket also comes in handily during the clean up process to determine if the CFReadStream ever has a chance of being used again. If the native socket has been closed then we can remove it from the pool.
NSData *nativeSocketData = nil; while (nativeSocketData = [keysEnumerator nextObject]) { CFSocketNativeHandle theSocket = 0; [nativeSocketData getBytes:&theSocket]; char msg; ssize_t response = recv(theSocket, &msg, 1, MSG_PEEK); if (response == 0) { NSLog(@"socket closed"); // The socket has been closed from the other side. Close the // stream and add the stream to be removed from the pool. CFReadStreamRef foundPersistentStream = (CFReadStreamRef)[_activePersistentReadStreams objectForKey:((NSData *)nativeSocketData)]; if (foundPersistentStream != nil) { NSLog(@"closing stream"); CFReadStreamClose(foundPersistentStream); [streamsToRemove addObject:nativeSocketData]; } } } OK, that was my flawed solution for dealing with multiple persistent connections... I guess my question now turns into... what is the best way to deal with multiple persistent connections in flight and keeping track of them?
Sincerely, Michael Ledford |