In this article, you will learn how to communicate with a serverusing TCP/IP. You will also learn how to build a simple chatapplication using the concepts covered in one of my earlierarticles.
For the sample project discussed in this article, you will useXcode and create a new View-based Application project and name itas Network.
Using Streams for Network Communications
The easiest way to communicate over the network using sockets isto use the NSStream class. The NSStream class is an abstract classrepresenting streams, where you can use it to read and write data.It can be used on memory, files, or networks. Using the NSStreamclass, you can communicate with a server simply by writing data toit and receive data from it by reading from an NSStream object.
On the Mac OS X, you can establish a connection to a server viathe use of an NSHost and NSStream objects, like this:
NSInputStream *iStream;
NSOutputStream *oStream;
uint portNo = 500;
NSURL *website = [NSURL URLWithString:urlStr];
NSHost *host = [NSHost hostWithName:[website host]];
[NSStream getStreamsToHost:host
port:portNo
inputStream:&iStream
outputStream:&oStream];
As you observed, the NSStream class has a class method named
getStreamsToHost:port:inputStream:outputStream:, whichcreates an input stream and an output stream to a server where youcan read and write data to it. However, the problem is that the
getStreamsToHost:port:inputStream:outputStream: method isnot supported on the iPhone OS. Hence, the above code will not workin your iPhone application.
To resolve this problem, you can add a category to the existingNSStream class to replace the functionality provided by thegetStreamsToHost:port:inputStream:outputStream: method. Todo so, right-click on the Classes group in Xcode and add a new fileand name it as NSStreamAdditions.m. In theNSStreamAdditions.h file, add in the following:
#import
@interface NSStream (MyAdditions)
+ (void)getStreamsToHostNamed:(NSString *)hostName
port:(NSInteger)port
inputStream:(NSInputStream **)inputStreamPtr
outputStream:(NSOutputStream **)outputStreamPtr;
@end
In the
NSStreamAdditions.m file, add in the following(
see
Listing 1).
The above code adds the class method namedgetStreamsToHostNamed:port:inputStream:outputStream: tothe NSStream class. You can now use this method in your iPhoneapplication to connect to a server using TCP.
Author's Note: The code for the category outlined hereare based on Apple’s Technical Q&A1652. |
In the NetworkViewController.m file, insert thefollowing statements in bold:
#import "NetworkViewController.h"
#import "NSStreamAdditions.h"
@implementation NetworkViewController
NSMutableData *data;
NSInputStream *iStream;
NSOutputStream *oStream;
Define the
connectToServerUsingStre
-(void) connectToServerUsingStre am:(NSString *)urlStr
portNo: (uint) portNo
{
if (![urlStr isEqualToString:@""])
{
NSURL *website = [NSURL URLWithString:urlStr];
if (!website) {
NSLog(@"%@ is not a valid URL");
return;
}
else
{
[NSStream getStreamsToHostNamed:urlStr
port:portNo
inputStream:&iStream
outputStream:&oStream];
[iStream retain];
[oStream retain];
[iStream setDelegate:self];
[oStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
[iStream open];
}
}
}
You scheduled both the input and output streams to receive eventson a run loop. Doing so prevents your code from blocking when thereis no data available on the stream. The delegates for both streamobjects are also set to self as you will implement the method forreceiving data on the stream in this same class.
Using CFNetwork for Network Communication
Another way to establish a connection to a server using TCP isthrough the CFNetwork framework. The CFNetwork is a framework inthe Core Services Framework (C libraries), which providesabstractions for network protocols, such as HTTP, FTP, and BSDsockets.
To see how to use the various classes in the CFNetworkframework, add the following statements in bold to theNetworkViewController.m file:
#import "NetworkViewController.h"
#import "NSStreamAdditions.h"
#import
@implementation NetworkViewController
NSMutableData *data;
NSInputStream *iStream;
NSOutputStream *oStream;
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
Define the following
connectToServerUsingCFStYou first use the CFStreamCreatePairWithSo
Sending Data
To send data to a server, you simply use the NSOutputStreamobject, like this:
-(void) writeToServer:(const uint8_t *) buf {
[oStream write:buf maxLength:strlen((char*)buf)];
}
The above method writes an array of unsigned integer bytes to theserver.
Reading Data
When data are received from the server, thestream:handleEvent: method will be fired. Hence, you willread all incoming data in this method. Implement this method asshown below:
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode
{
switch(eventCode)
{
case NSStreamEventHasBytesAva ilable:
{
if (data == nil) {
data = [[NSMutableData alloc] init];
}
uint8_t buf[1024];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:1024];
if(len) {
[data appendBytes:(const void *)buf length:len];
int bytesRead;
bytesRead += len;
}
else
{
NSLog(@"No data.");
}
NSString *str = [[NSString alloc] initWithData:data
encoding:NSUTF8StringEncoding];
NSLog(str);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"From server"
message:str
delegate:self
cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
[alert release];
[str release];
[data release];
data = nil;
}
break;
}
}
This method contains two parameters -- an NSStream instance, and anNSStreamEvent constant. The NSStreamEvent constant can be one ofthe following:
- NSStreamEventNone -- No event has occurred.
- NSStreamEventOpenComplet
ed -- The open has completedsuccessfully. - NSStreamEventHasBytesAva
ilable -- The stream has bytes to beread. - NSStreamEventHasSpaceAva
ilable -- The stream can accept bytesfor writing. - NSStreamEventErrorOccurr
ed -- An error has occurred on thestream. - NSStreamEventEndEncounte
red -- The end of the stream has beenreached.
The stream:handleEvent: method is also a good place tocheck for connection error. For example, if theconnectToServerUsingStre
Disconnecting
To disconnect from the server, define the disconnect method asshown below:
-(void) disconnect
{
[iStream close];
[oStream close];
}
|
- (void)dealloc {
[self disconnect];
[iStream release];
[oStream release];
if (readStream)
CFRelease(readStream);
if (writeStream) CFRelease(writeStream);
[super dealloc];
}
Testing the Application
Figure 1.Populate: Populate the View window with views. | | Figure 2.Verify: Verify the connections on the File’s Owneritem. | | Figure 3.View: Add more views. |
You are now ready to put all the pieces together and test thecode against a server. In the NetworkViewController.hfile, declare the following outlet and action:
#import
@interface NetworkViewController : UIViewController {
IBOutlet UITextField *txtMessage;
}
@property (nonatomic, retain) UITextField *txtMessage;
-(IBAction) btnSend: (id) sender;
@end
Double-click on the
NetworkViewController.xib file to editit in Interface Builder. In the View window, populate it with thefollowing views (
see also
Figure 1):
- Text Field
- Round Rect Button
- Control-click on the File’s Owner item and drag and drop itover the Text Field view. Select txtMessage.
- Control-click on the Round Rect Button view and drag and dropit over the File’s Owner item. Select btnSend:.
Back in the NetworkViewController.m file, add thefollowing lines of code in bold to the viewDidLoadmethod:
- (void)viewDidLoad {
[self connectToServerUsingStre am:@"192.168.1.102" portNo:500];
//---OR---
//[self connectToServerUsingCFSt ream:@"192.168.1.102" portNo:500];
[super viewDidLoad];
}
The above code assumes you are connecting to a server of IP address192.168.1.102, at port 500. You will see how to write the servercode shortly.
Implement the btnSend: method as follows:
-(IBAction) btnSend: (id) sender
{
const uint8_t *str =
(uint8_t *) [txtMessage.text cStringUsingEncoding:NSASCIIStringEncoding];
[self writeToServer:str];
txtMessage.text = @"";
}
Release the txtMessage outlet in the
dealloc method:
- (void)dealloc
{
[txtMessage release];
[self disconnect];
[iStream release];
[oStream release];
if (readStream) CFRelease(readStream);
if (writeStream) CFRelease(writeStream);
[super dealloc];
}
Building the Server
Up till this point, you have built a client on the iPhone thatis ready to send some text over to a server. So what about theserver? To test this application, I have built a very simpleconsole server using C#. Here is the code for theProgram.cs file (see Listing 3).
The server program performs the following:
- It assumes that the IP address of the server is 192.168.1.102.When testing on your end, replace this IP address with the IPaddress of your computer running this server application.
- It sends back to the client whatever data it receives.
- Once the data is received, the server no longer listens forincoming data. In order for the client to send data to it again,the client needs to reconnect to the server.
Author's Note: Download the accompanying source code forall the examples illustrated in this article. |
Figure 4. Change isgood: Change the font size of the Text View view. |
With the server code explained, you can now test your iPhoneapplication. Type some text into the Text Field view and click theSend button. If the connection is established, you should see theAlert View displaying the data received.
A More Interesting Example
In one of my earlier articles for DevX.com, I wrote about how tobuild your own instant messenger application (see “Home-brew Your OwnInstant Messenger App with Visual Studio .NET">using .NET. In that article, I wrote about how to write a serverand a client application to allow chat messages to be sent betweenmultiple users. Using the code in that article, you could actuallymodify your iPhone application as a chat client and communicatewith other users on other platforms.
Using the same project created earlier, add the following viewsto the View window (see also Figure 3):
- Label
- Text Field
- Round Rect Button
- Text View
In the NetworkViewController.h file, add the followingstatements in bold:
#import
@interface NetworkViewController : UIViewController {
IBOutlet UITextField *txtMessage;
IBOutlet UITextField *txtNickName;
IBOutlet UITextView *txtMessages;
}
@property (nonatomic, retain) UITextField *txtMessage;
@property (nonatomic, retain) UITextField *txtNickName;
@property (nonatomic, retain) UITextView *txtMessages;
-(IBAction) btnSend:(id) sender;
Figure 5. Connections:Verify the connections. |
-(IBAction) btnLogin:(id) sender; @endPerform the following actions:
- Control-click on the File’s Owner item and drag and drop itover the top Text Field view. Select txtNickName.
- Control-click on the File’s Owner item and drag and drop itover the top Text View view. Select txtMessages.
- Control-click on the Round Rect Button view and drag and dropit over the File’s Owner item. Select btnLogin:.
Figure 6. Chat: Startchatting on your iPhone. |
Author's Note: For your convenience, I have provided theC# version of the chat server in the download package for thisarticle. |
Summary
In this article, you have seen how to communicate with anotherserver using TCP/IP. Knowing how to communicate with the outsideworld allows you to build very interesting applications. In thenext article, I will talk about Bluetooth programming on theiPhone.