Tuesday, May 1, 2012

Socket Programing in Objective-C


Socket are interfaces that allow you to transfer data in bidirectional from app to another app. Socket has two sides. Each side is identified by a combination of two elements: the IP address and port. Socket programing is used in various situation in iOS application development like on line gaming, chat application etc.

So many guides and articles are available to make socket connection between apps but these are very limited in Objective-C and complicated to integrated them. This article discusses only how we can create a socket connection from one app to another app. After establishing connection both app can send and receive data to each other.


Setting Socket Connection
You can use the CFStream API to establish  a socket connection. 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 cast your CFStreams to NSStreams.

- (void)connection:(NSString*)serviceName forIpAddress:(NSString *)ipAddress 
  forPort:(NSString *)portNo
{
if(inputStream && outputStream)
    [self close];
NSString *urlString = [NSString stringWithFormat:@"http://%@", ipAddress];
NSURL *website = [NSURL URLWithString:urlString];
if (!website) {
NSLog(@"%@ is not a valid URL", website);
}
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], [portNo intValue], &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[self open];
}  

- (void)open
{
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}

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:). This method is described below in 'Reading From Socket Connection' section.

Reading From Socket Connection
After a NSInputStream object is sent open, you can find out about its status, whether it has bytes available to read, and the nature of any error with the following messages:
streamStatus
hasBytesAvailable
streamError

  • The returned status is an NSStreamStatus constant indicating that the stream is opening, reading, at the end of the stream, and so on. This code for reading data from socket connection listed below.

-(void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent
{
NSString *event;    
    
switch (streamEvent)
    {
case NSStreamEventNone:
event = @"NSStreamEventNone";
break;
case NSStreamEventOpenCompleted:
event = @"NSStreamEventOpenCompleted";
break;
case NSStreamEventHasBytesAvailable:
event = @"NSStreamEventHasBytesAvailable";
if (theStream == inputStream)
{
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable])
{
len = [inputStream read:buffer maxLength:1024];
if (len > 0)
{
NSMutableString *output = [[NSMutableString alloc] initWithBytes:buffer length:len encoding:NSUTF8StringEncoding];
NSLog(@"Received data--------------------%@", output);
}
}
}
break;
case NSStreamEventHasSpaceAvailable:
event = @"NSStreamEventHasSpaceAvailable";
break;
case NSStreamEventErrorOccurred:
event = @"NSStreamEventErrorOccurred"            
            
[self close];
break;
case NSStreamEventEndEncountered:
event = @"NSStreamEventEndEncountered";            
                               [self close];
break;
default:
event = @"Unknown";
break;
}
           NSLog(@"event------%@",event);
}

When an NSInputStream object reaches the end of a stream, it sends the delegate a NSStreamEventEndEncountered event in a stream:handleEvent:message. The delegate should dispose of the object by doing the mirror-opposite of what it did to prepare the object. Also we should do When the NSInputStream object experiences errors processing the stream. 

- (void)close
{
[inputStream close];
[outputStream close];
[inputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
          [outputStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
          [inputStream setDelegate:nil];
          [outputStream setDelegate:nil];
inputStream = nil;
outputStream = nil;            
}

Writing To Socket Connection
After a NSOutputStream object is sent open, you can find out about its status, whether it has space for writing data, and the nature of any error with the following messages:
streamStatus
hasSpaceAvailable
streamError
The returned status is an NSStreamStatus constant indicating that the stream is opening, writing, at the end of the stream, and so on. This code for writing data to socket connection listed below.
- (void)dataSending:(NSString*)data
{
if(outputStream)
{
if(![outputStream hasSpaceAvailable])
return;
NSData *_data=[data dataUsingEncoding:NSUTF8StringEncoding];
int data_len = [_data length];
uint8_t *readBytes = (uint8_t *)[_data bytes];
int byteIndex=0;
unsigned int len=0;
while (TRUE)
{
len = ((data_len - byteIndex >= 40960) ?
  40960 : (data_len-byteIndex));
if(len==0)
break;            
uint8_t buf[len];
(void)memcpy(buf, readBytes, len);
len = [outputStream write:(const uint8_t *)buf maxLength:len];
byteIndex += len;
readBytes += len;
}        
                     NSLog(@"Sent data----------------------%@",data);               
}
}

Video Signals to Projector for iOS4 Apps


What’s New
This is a fire-and-forget solution to mirroring the display of your iOS device (iPhone, iPod Touch, iPad) apps on an external display. It uses public APIs, so it can be used in apps published on the App Store. It supports detecting cabling (plugging and unplugging the display), orientation changes, and even offers a “tv safe mode” for displaying your app on an older analog video device.

How To Code
It's crazy easy. Just three steps:
1. Get the files AppProjector.m and AppProjector.h from this link and add them to your project.
2. In your app controller, add this to the other imports near the top: #import "AppProjector.h"
3. In your application's -applicationDidFinishLaunching: method, add this line: [[AppProjector sharedInstance] startTVOut];


Hardware Requirements
Since the launch of the iPad, Apple also offers a VGA connector cable, ostensibly iPad-only but which is what I used with an iPhone 4 while developing this code. I also tested with the composite out cable. Both cable seem to work fine. I’m not sure about iPhone 3GS and older devices, though — those devices may not support the VGA cable, although they certainly do support the composite and component cables.

While the TV output is running, if the video output cable is disconnected, the class will catch a notification, and stop sending video to the external window. Likewise, if a video output cable is connected, the class will catch that notification, and automatically start showing the video.

You don't even need to call -startTVOut -- just initialize the singleton sharedInstance, and the class will watch for cable connection notifications.

No Jailbreak API
This class uses app-store safe methods. You can ship apps that use this code. We are using an Apple-approvied safe-and-friendly method of collecting bitmaps of all visible windows, and then drawing that bitmap on the external screen, which is also displayed using Apple-approved non-private APIs.

Debugging with the Video Out Cable Attached
Obviously you can’t debug an app in Xcode without having the device connected via USB. Likewise, you can’t view the video output without having the VGA cable attached. So debugging was basically impossible.

Improvements Needed
Currently, device rotation is correctly tracked for the Portrait and Landscape Left initial starting positions. You can turn the device 90 degrees to the left or right and the view on the external monitor will rotate to match. (There’s even a nice animated rotation of the screen contents on the external video window.) However, portraitUpsideDown is not handled.

Compatibility Note
This class relies on certain APIs (i.e. [UIScreen screens]) that only exist in iOS SDK 3.2 and above.

AppProjector Class Reference

-sharedInstance
Creates a singleton instance (if it doesn’t already exist) and returns it. Starts listening to cable connection/disconnection and device orientation notifications.

-startTVOut
Creates a window on the second screen at the highest resolution it supports, and starts a timer (at the frames per second rate defined in the class file) to copy the screen contents to the window. If no screen is attached, -startTVOut will simply report a failure to the console.

-stopTVOut
Stops the periodic video-mirror timer (or thread) and releases the offscreen window.

tvSafeMode
When tvSafeMode is YES, the class will scale down the output size by 20%, so that the entire picture can fit within the visible scan area of an analog TV. If you don’t know what an analog TV is, don’t worry, you’ll probably never see one.