You can use the CFStream API to establish a socket connection and, with the stream object (or objects) created as a result, send data to and receive data from a remote host. You can also configure the connection for security.
The NSStream class does not support connecting to a remote host on iOS. CFStream does support this behavior, however, and once you have created your streams with the CFStream API, you can take advantage of the toll-free bridge between CFStream and NSStream to cast your CFStreams to NSStreams. Just call the CFStreamCreatePairWithSocketToHost
function, providing a host name and a port number, to receive both a CFReadStreamRef
and a CFWriteStreamRef
for the given host. You can then cast these objects to an NSInputStream and an NSOutputStream and proceed.
Listing 1 illustrates the use of CFStreamCreatePairWithSocketToHost
. This example shows the creation of both a CFReadStreamRef
object and a CFWriteStreamRef
object. If you want to receive only one of these objects, just specify NULL
as the parameter value for the unwanted object.
Listing 1 Setting up a network socket stream
- (IBAction)searchForSite:(id)sender |
{ |
NSString *urlStr = [sender stringValue]; |
if (![urlStr isEqualToString:@""]) { |
NSURL *website = [NSURL URLWithString:urlStr]; |
if (!website) { |
NSLog(@"%@ is not a valid URL"); |
return; |
} |
CFReadStreamRef readStream; |
CFWriteStreamRef writeStream; |
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 80, &readStream, &writeStream); |
NSInputStream *inputStream = (NSInputStream *)readStream; |
NSOutputStream *outputStream = (NSOutputStream *)writeStream; |
[inputStream setDelegate:self]; |
[outputStream setDelegate:self]; |
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; |
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; |
[inputStream open]; |
[outputStream open]; |
} |
} |
If the socket connection fails, then one or both of the requested CFReadStreamRef
and CFWriteStreamRef
objects are NULL
. Once you have cast the CFStreams to NSStreams, set the delegate, schedule the stream on a run loop, and open the stream as usual. The delegate should begin to receive stream-event messages (stream:handleEvent:
). See “Reading From Input Streams” and “Writing To Output Streams” for more information.
Before you open a stream object, you might want to set security and other features for the connection to the remote host (which might be, for example, an HTTPS server). NSStream
defines properties that affect the security of TCP/IP socket connections in two ways:
Secure Socket Layer (SSL).
A security protocol using digital certificates to provide data encryption, server authentication, message integrity, and (optionally) client authentication for TCP/IP connections.
SOCKS proxy server.
A server that sits between a client application and a real server over a TCP/IP connection. It intercepts requests to the real server and, if it cannot fulfill them from a cache of recently requested files, forwards them to the real server. SOCKS proxy servers help improve performance over a network and can also be used to filter requests.
For SSL security, NSStream
defines various security-level properties (for example, NSStreamSocketSecurityLevelSSLv2
). You set these properties by sending setProperty:forKey:
to the stream object using the key NSStreamSocketSecurityLevelKey
, as in this sample message:
[inputStream setProperty:NSStreamSocketSecurityLevelTLSv1 forKey:NSStreamSocketSecurityLevelKey]; |
You must set the property before you open the stream. Once it opens, it goes through a handshake protocol to find out what level of SSL security the other side of the connection is using. If the security level is not compatible with the specified property, the stream object generates an error event. However, if you request a negotiated security level (NSStreamSocketSecurityLevelNegotiatedSSL
), the security level becomes the highest that both sides of the connection can implement. Still, if you try to set an SSL security level when the remote host is not secure, an error is generated.
To configure a SOCKS proxy server for a connection, you need to construct a dictionary with keys of the form NSStreamSOCKSProxy
NameKey
(for example, NSStreamSOCKSProxyHostKey
). The value of each key is the SOCKS proxy setting that Name refers to. Then using setProperty:forKey:
, set the dictionary as the value of the NSStreamSOCKSProxyConfigurationKey
.
If you are opening a connection to an HTTP server (that is, a website), then you may have to initiate a transaction with that server by sending it an HTTP request. A good time to make this request is when the delegate of the NSOutputStream
object receives a NSStreamEventHasSpaceAvailable
event via a stream:handleEvent:
message. “Making an HTTP GET request” shows the delegate creating an HTTP GET request and writing it to the output stream, after which it immediately closes the stream object.
Listing 2 Making an HTTP GET request
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode { |
NSLog(@"stream:handleEvent: is invoked..."); |
switch(eventCode) { |
case NSStreamEventHasSpaceAvailable: |
{ |
if (stream == oStream) { |
NSString * str = [NSString stringWithFormat: |
@"GET / HTTP/1.0\r\n\r\n"]; |
const uint8_t * rawstring = |
(const uint8_t *)[str UTF8String]; |
[oStream write:rawstring maxLength:strlen(rawstring)]; |
[oStream close]; |
} |
break; |
} |
// continued ... |
} |
} |
Last updated: 2009-12-16