RSU

[Monday, July 6, 2009]

iPhone Programming: Creating Network Socket Connections

My current project involves creating an iPhone application that communicates with another application (hosted on a PC). Ideally, I wanted to do this via Bluetooth, but like a lot of other (useful) stuff, Apple's API doesnt expose it for any communication other than peer-to-peer (iphone to iphone) and audio gateway. So using that was out of question, though my personal side project is to write a program for PC that fools the iphone into thinking that its talking to one of its own. So for now I am using the WiFi/Network connection.

My goal: Create a simple app, that connect to a TCP host, sends "Hello World" and closes the connection.

Being a complete beginner in iPhone development, my first step was check out the Apple's official Networking guides. I found and followed Cocoa Streams how-to. This document is filed under iPhone Reference, and can be accessed from the developer webpage. After hours of struggling with the seemingly simple code, I realized that iPhone OS does not support +getStreamsToHost:. I have filed a bug regarding this document's classification. After some chitchatting and more documentations, I found the correct way to create a connection is to use the CFNetwork framework, which is well explained by Apple here.

Following the sample code, I created an app that uses CFStreams to create a socket connection.



-(IBAction) connect_button {

CFWriteStreamRef writeStream = NULL;

CFStringRef host = CFSTR("10.212.97.159 ");
UInt32 port = 22701;

CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, NULL, &writeStream);

CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);

if(!CFWriteStreamOpen(writeStream)) {
NSLog(@"Error Opening Socket");
}
else{
UInt8 buf[] = "Hello WOrld!";
int bytesWritten = CFWriteStreamWrite(writeStream, buf, strlen((char*)buf));
NSLog(@"Written: %d", bytesWritten);

if (bytesWritten < 0) {
CFStreamError error = CFWriteStreamGetError(writeStream);
/** How do I print out description? All i get is -1 here. i.e What is perror() equivalent? **/
}

}

CFWriteStreamClose(writeStream);

}


Very simple and familiar looking code, so this should definitely work. Right? Well no it did not. I kept getting an error, which is stored in CFStreamError type. Problem is I had no way of interpreting that error. The CFError type is very well documented, but CFStreamError has a lot of loose ends. I spent quite a time trying to figure out what the error meant so that I could debug my application.

Update: The above code works! I still do not know how to print out the description of CFStreamError, but I found the error when I rewrote the code. The culprit is Line 5. Do you see it? :). Its the whitespace after the IP. Damn! That little error wasted atleast a few hours of my coding time.

Finally, I gave up on apple's way of networking and decided to do it the classic C-style way. I found an amazing library called the SmallSockets that uses the linux style of sockets under the hood, wrapped inside a Obj-C class. Downloaded it, added the headers file in my project and Voila!, it worked! All under 5 minutes!


-(IBAction) connect_button {
Socket *socket;
int port = 22701;
NSString *host = @"10.212.97.159";

socket = [Socket socket];
@try{
[socket connectToHostName:host port:port];
[socket writeString:@"Hello World!"];
//** Connection was successful **//
//[socket retain]; // Must retain if want to use out of this action block.
}
@catch (NSException* exception) {
NSString *errMsg = [NSString stringWithFormat:@"%@",[exception reason]];
NSLog(errMsg);
socket = nil;
}

//** Disconnect **//
[socket close];
}


Though my project is now up and running with 2-way network comm, I still want to get back to the CFNetwork and figure out where I went wrong, and try to implement a bare bone hello world app, the apple way.

5 comments:

Arsenikal | July 29, 2009 at 8:40 AM
This comment has been removed by the author.
Unknown | November 18, 2009 at 8:40 AM

i am getting 2 errors that read:

"error: 'Socket' undeclared (first use in function)"
"error: 'socket' undeclared (first use in function)"

any thoughts?

Sidhant Gupta | December 3, 2009 at 12:31 AM

You need to include the SmallSockets libraries in your code. Basically #include or import. For that the library needs to be added to the project.

If you can post some code, I might be able to help.

Thanks!

Anonymous | January 5, 2011 at 9:21 PM

in your first code, you need to release writeStream

Sidhant Gupta | January 5, 2011 at 9:23 PM

Thank you for pointing that out!

Post a Comment