Chinaunix首页 | 论坛 | 博客
  • 博客访问: 182176
  • 博文数量: 60
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 385
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-19 21:43
个人简介

readonly

文章分类

全部博文(60)

文章存档

2013年(60)

我的朋友

分类: iOS平台

2013-02-23 10:28:00

n my previous article on iPhone programming, you saw how to consume Web services from within an iPhone application and how to parse the returning XML result. While Web services are very common nowadays, the payload needed再看下Wei-Meng Lee使用Streams,CFNetwork进行网络通讯的介绍,如何用最少的代码开始网络编程 for consuming Web services is pretty high, especially if you are simply fetching small amounts of data -- the SOAP packet itself takes up quite a number of bytes. A better way to communicate is to use sockets, where information could be sent and received without the additional XML payload. In addition, socket allows you to maintain a connection to the server, which allows your application to run asynchronously, and receive incoming data as and when needed.

In this article, you will learn how to communicate with a server using TCP/IP. You will also learn how to build a simple chat application using the concepts covered in one of my earlier articles.

For the sample project discussed in this article, you will use Xcode and create a new View-based Application project and name it as Network.


Using Streams for Network Communications

The easiest way to communicate over the network using sockets is to use the NSStream class. The NSStream class is an abstract class representing streams, where you can use it to read and write data. It can be used on memory, files, or networks. Using the NSStream class, you can communicate with a server simply by writing data to it and receive data from it by reading from an NSStream object.

On the Mac OS X, you can establish a connection to a server via the 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:, which creates an input stream and an output stream to a server where you can read and write data to it. However, the problem is that the getStreamsToHost:port:inputStream:outputStream: method is not supported on the iPhone OS. Hence, the above code will not work in your iPhone application.

To resolve this problem, you can add a category to the existing NSStream class to replace the functionality provided by thegetStreamsToHost:port:inputStream:outputStream: method. To do so, right-click on the Classes group in Xcode and add a new file and name it asNSStreamAdditions.m. In the NSStreamAdditions.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 ).

The above code adds the class method named getStreamsToHostNamed:port:inputStream:outputStream: to the NSStream class. You can now use this method in your iPhone application to connect to a server using TCP.


Author's Note: The code for the category outlined here are based on Apple’s Technical Q&A1652.

In the NetworkViewController.m file, insert the following statements in bold:

 #import "NetworkViewController.h"

#import "NSStreamAdditions.h"

@implementation NetworkViewController

NSMutableData *data;

NSInputStream *iStream;
NSOutputStream *oStream; 
Define the connectToServerUsingStream:portNo: method so that you can connect to a server and then create an input and output stream objects:
 -(void) connectToServerUsingStream:(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 events on a run loop. Doing so prevents your code from blocking when there is no data available on the stream. The delegates for both stream objects are also set to self as you will implement the method for receiving data on the stream in this same class.


Using CFNetwork for Network Communication

Another way to establish a connection to a server using TCP is through the CFNetwork framework. The CFNetwork is a framework in the Core Services Framework (C libraries), which provides abstractions for network protocols, such as HTTP, FTP, and BSD sockets.

To see how to use the various classes in the CFNetwork framework, add the following statements in bold to the NetworkViewController.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 connectToServerUsingCFStream:portNo: method as follows (see ).

You first use the CFStreamCreatePairWithSocketToHost() method to create a readable and writable stream connected to a server via TCP/IP. This method returns a reference to a readable stream (readStream) and a writable stream (writeStream). They are then type-casted to their respective equivalent in Objective C -- NSInputStream and NSOutputStream. You then do the same as you did previously -- set their delegates as well as their run loop.


Sending Data

To send data to a server, you simply use the NSOutputStream object, 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 the server.



Reading Data

When data are received from the server, the stream:handleEvent: method will be fired. Hence, you will read all incoming data in this method. Implement this method as shown below:

 - (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
    
    switch(eventCode) {
        case NSStreamEventHasBytesAvailable:
        {
            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 an NSStreamEvent constant. The NSStreamEvent constant can be one of the following:
  • NSStreamEventNone -- No event has occurred.


  • NSStreamEventOpenCompleted -- The open has completed successfully.


  • NSStreamEventHasBytesAvailable -- The stream has bytes to be read.


  • NSStreamEventHasSpaceAvailable -- The stream can accept bytes for writing.


  • NSStreamEventErrorOccurred -- An error has occurred on the stream.


  • NSStreamEventEndEncountered -- The end of the stream has been reached.
For reading incoming data, you would check for the NSStreamEventHasBytesAvailable constant. What you did in this method was to read from the incoming stream and then display the received data using an UIAlertView object.

The stream:handleEvent: method is also a good place to check for connection error. For example, if the connectToServerUsingStream:portNo: method fails to connect to the server, the error will be notified via the stream:handleEvent: method, with the NSStreamEvent constant set to NSStreamEventErrorOccurred.

阅读(1522) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~