using NSInputStream/NSOutputStream to communicate over TCP (iPhone) - iphone

Is it possible to do TCP communications using NSInputStream/NSOutputStream on iPhone? The example apple gives in their documentation uses [NSStream getStreamsToHost] and that isn't supported on iPhone. I have seen other posts which use CFStream to set up the socket and then bridge to NSStream, is that the only supported way?
Based on the documentation it seems something like this should work in theory:
//input stream
NSURL *url = [[NSURL alloc] initWithString:#"10.252.1.1:8080"];
iStream = [NSInputStream inputStreamWithURL:url];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = [NSOutputStream outputStreamWithURL:url append:true];
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[oStream open];
However there are two problems with this:
1) If I do just the iStream part I never see any events called on my delegate.
2) The outputStreamWithURL fails with a cryptic "EXC_BAD_ACCESS" error message which comes from within CFWriteStreamSetProperty

This Apple article explains how to implement getStreamsStreamsToHost on iOS
Using NSStreams For A TCP Connection Without NSHost

Related

Socket Programming - iPhone - connection getting disconnected automatically

I am trying to implement a chat client in iPhone APP. I'm using the below code to open the socket connection. But my stream connection is automatically getting disconnected frequently. What will be the issue? How can I maintain the connection until it is closed manually through the code?
- (void) initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"111.122.123.124", (UInt32)123, &readStream, &writeStream);
inputStream = (__bridge_transfer NSInputStream *) readStream;
outputStream = (__bridge_transfer 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 you are not using a websocket - not sending any data it will automatically disconnect from a server side usually.
Standard configuration is around 1 minute for that - until 60 second of idle time it will disconnect a client from a server by sending valid end connection info.
Check configuration for your server to higher value or send a heartbeat packet each 30 seconds to keep connection alive.

Wait before returning value iPhone

I found some classes on the internet to establish a tcp connection. The link is in here. I want to customize a class and so far I am able to establish a connection send data and receive data which is great. Once I have created and imported the classes used by the first link that I provided I am able to establish a connection using the following method:
-(void) connectToServerUsingCFStream:(NSString *) urlStr portNo: (uint) portNo {
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault,
(CFStringRef) urlStr,
portNo,
&readStream,
&writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream,
kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
iStream = (NSInputStream *)readStream;
[iStream retain];
[iStream setDelegate:self];
[iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[iStream open];
oStream = (NSOutputStream *)writeStream;
[oStream retain];
[oStream setDelegate:self];
[oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[oStream open];
}
}
Since I am going to use this class a lot on my application I am creating my own class and I want to create a -(BOOL) connect{} method. I want to return yes is the connection is establish and no otherwise. The problem is that the way I am able to tell if I establish a connection is by creating a connection attempting to send data and then on the server side I have created a method that whenever I receive that I send a string back. If in the next 2 seconds I receive data I know that the connection was establish.
so in my connection method I want to wait 2 seconds and then return a value depending if the BOOL variable didReciveData = YES.
Since you use a NSOutputStream a better approach could be to check the return value of [oStream write:]: if it returns -1 no data has been sent, so there's no connection.
However, if you want to wait two seconds you can use NSTimer to create a timeout. If you receive a response before the timer fires you can invalidate the timer, otherwise the timer will call the related method that will notify the end of the two seconds.
You could try pausing the main run loop.
- (void)test
{
NSLog(#"Test starting.");
BOOL wasSuccessful = [self connect];
NSLog(#"Success: %d", wasSuccessful);
}
- (BOOL)connect
{
// try to connect here, make sure to get a callback on success/failure
// fake callback
[self performSelector:#selector(callback:) withObject:[NSNumber numberWithBool:NO] afterDelay:2.0];
// wait for callback
CFRunLoopRun();
return self.success;
}
- (void)callback:(NSNumber *)successful
{
self.success = [successful boolValue];
CFRunLoopStop(CFRunLoopGetCurrent()); // now we want -connect to return!
}

iphone :client-server communication not occuring

i had made the following programming for client server programming but it is not working. the server is not able to receive the request for connection setup.plz help.
#import "clientserverprogramViewController.h"
#import "secondview.h"
#import <CoreFoundation/CFSocket.h>
#include <sys/socket.h>
#include <netinet/in.h>
NSInputStream *iStream;
NSOutputStream *oStream;
#implementation clientserverprogramViewController
#synthesize name,filepath,display;
-(IBAction) print {
NSString *urlStr = serverIP;]
[display setText : urlStr];
if (![urlStr isEqualToString:#""]) {
NSURL *website = [NSURL URLWithString:urlStr];
if (!website) {
NSLog(#"%# is not a valid URL");
return;
}
NSHost *host = [NSHost hostWithName:[website host]];
[NSStream getStreamsToHost:host port:3000 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];
[iStream open];
[oStream open];
}
}
-(IBAction) settings {
[self presentModalViewController:nextview animated: YES];
}
-(IBAction) cancel {
exit(0);
}
- (void)dealloc {
[super dealloc];
}
#end
You only open streams and don't do anything with them. It's like picking up a phone and not dialing a number. Use NSStreamDelegate protocol to implement data transmission code.
Update:
You have these lines that set the delegate for streams:
[iStream setDelegate:self];
[oStream setDelegate:self];
Now implement methods that are defined in NSStreamDelegate protocol in your own class (AFAIK - there's only one of them). See how to receive/send data from there.
Is there a specific reason you're using streams?
What about using NSURLConnection? Here's a piece of code from a project of mine. Both are in KANetworkManager. KANetworkTransactionType is simply a enum that helps me know how to parse the response.
+ (void) createAndStartUrlConnection:(NSMutableURLRequest *)request type:(KANetworkTransactionType)type target:(id)target callback:(SEL)callback;
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSDictionary *requestDict = [NSDictionary dictionaryWithObjectsAndKeys:request, #"request", [NSNumber numberWithInt:type], #"type", target, #"target", [NSValue valueWithPointer:callback], #"callback", nil];
[KANetworkManager performSelectorInBackground:#selector(makeNetworkCall:) withObject:requestDict];
}
I'm able to made a synchronous network call because I always call this method on its own thread. It's a simpler way to achieve asynchronous network communications without dealing with delegates (although the delegate method provides some benefits). Your parseResponse method would need to be specific to whatever your web service it sending back. parseResponse would notify the callback method. Let me know if you have additional questions regarding this.
+ (void) makeNetworkCall:(NSDictionary *)params
{
// We assume this method won't be called from the main thread, so we need our own NSAutoreleasePool.
NSAutoreleasePool *autoreleasePool = [[NSAutoreleasePool alloc] init];
NSMutableURLRequest *request = [params objectForKey:#"request"];
KANetworkTransactionType type = [(NSNumber *)[params objectForKey:#"type"] intValue];
id target = [params objectForKey:#"target"];
SEL callback = (SEL)[[params objectForKey:#"callback"] pointerValue];
NSURLResponse *response;
NSError *err;
// We make a synchronous request assuming we're on a background thread.
NSData *data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
if (data.length > 0)
{
[self parseResponse:data type:type target:target callback:callback];
}
else
{
NSLog(#"Error occured during network call. %#", err);
}
[autoreleasePool drain];
}
JB gates,
In your code you inform iStream and oStream that your clientserverprogramViewController object is to be the delegate for each. However, a proper delegate needs actual implementation. Your class needs to implement this method:
– stream:handleEvent:
The details are documented here:
Reading From Input Streams
Writing To Output Streams
Also, your code will not work on a real iPhone. There is an updated Core Foundation API for creating the socket pair, details here.
Update
Just wondering if this is not a software issue but maybe the server is behind a firewall. Please give details what the server is, ie webserver, netcat, or simple TCP socket, etc.
Peter

How to Maintain VOIP socket connection in background?

My App Requirement: I should maintain a socket connection to trigger local notification on server push without using Push Notification(APNs) for some reasons. So I am using the VOIP background capability of iPhone to maintain socket connection.
1. I have configured a stream for VOIP in order to persist socket connection to run in background, so what Timeout value should I set?
Will the socket connection terminates once the timeout expires?
How do I make my application to listen to the socket all the time.?
Client stream configuration is as follows,
NSString *urlStr = #"http://192.168.0.108";
NSURL *website = [NSURL URLWithString:urlStr];
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 1234, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
NSInputStream *inputStream = (NSInputStream *)readStream;
NSOutputStream *outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[outputStream setDelegate:self];
[outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
2. Should I reconnect the stream in the handler applicationDidEnterBackground:
[[UIApplication sharedApplication] setKeepAliveTimeout:86400 handler:^(void)
{
if (inputStream)
[inputStream close];
if (outputStream)
[outputStream close];
urlStr = #"http://192.168.0.108";
website = [NSURL URLWithString:urlStr];
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)[website host], 1234, &readStream, &writeStream);
CFReadStreamSetProperty(readStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
CFWriteStreamSetProperty(writeStream, kCFStreamNetworkServiceType, kCFStreamNetworkServiceTypeVoIP);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[inputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[outputStream setDelegate:self];
[outputStream setProperty:NSStreamNetworkServiceTypeVoIP forKey:NSStreamNetworkServiceType] ;
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}];
3. Say my server restarts and the app is in background, how do I ensure the connection?
If the Wi-Fi connection in my iPhone or if I terminate the server app the connection will be closed, so what measures should I take to make my app work as per expectation?
You also need to ensure that you have set the in your pList file
<key>UIBackgroundModes</key>
<array>
<string>voip</string>
</array>
The socket will be managed by the iOS while your application is in the background. Your application will receive the CPU time as soon there is a data available in the socket. So in the runLoop I am checking ht
In my case the signaling protocol is working in a separate thread, so I am spinning the runLoop my self
// Start runloop
while (!m_needStop)
{
CFRunLoopRun();
}
And stopping it when needed:
m_needStop = true;
{
QAutoLock l(m_runLoopGuard);
if ( m_runLoop != NULL )
CFRunLoopStop(m_runLoop);
}
For the sockets in runLoop I have setup the handler functions before scheduling them into the runLoop:
int nFlags = kCFStreamEventOpenCompleted | kCFStreamEventHasBytesAvailable | kCFStreamEventCanAcceptBytes | kCFStreamEventErrorOccurred | kCFStreamEventEndEncountered;
CFStreamClientContext context;
context.info = this;
context.version = 0;
context.release = NULL;
context.retain = NULL;
context.copyDescription = NULL;
if ( !CFReadStreamSetClient(m_readStream, nFlags, NotificationProtocolHandler::ReadStreamCallback, &context) )
{
ReleaseStreams();
return false;
}
if ( !CFWriteStreamSetClient(m_writeStream, nFlags, NotificationProtocolHandler::WriteStreamCallback, &context) )
{
ReleaseStreams();
return false;
}
These are the functions which will be called when your socket will has some info for you and even if your application in the background:
void NotificationProtocolHandler::ReadStreamCallback(CFReadStreamRef stream,
CFStreamEventType eventType,
void *clientCallBackInfo)
{
NotificationProtocolHandler* handler = (NotificationProtocolHandler*)clientCallBackInfo;
switch (eventType)
{
case kCFStreamEventOpenCompleted:
break;
case kCFStreamEventHasBytesAvailable:
handler->ProcessInput();
break;
case kCFStreamEventErrorOccurred:
handler->ProcessConnectionError();
break;
case kCFStreamEventEndEncountered:
handler->ProcessConnectionError();
break;
default:
break; // do nothing
}
}
void NotificationProtocolHandler::WriteStreamCallback(CFWriteStreamRef stream,
CFStreamEventType eventType,
void *clientCallBackInfo)
{
NotificationProtocolHandler* handler = (NotificationProtocolHandler*)clientCallBackInfo;
switch (eventType)
{
case kCFStreamEventOpenCompleted:
handler->ProcessOutputConnect();
break;
case kCFStreamEventCanAcceptBytes:
handler->ProcessReadyToWrite();
break;
case kCFStreamEventErrorOccurred:
handler->ProcessConnectionError();
break;
case kCFStreamEventEndEncountered:
handler->ProcessConnectionError();
break;
default:
break; // do nothing
}
}
To make server aware that client is still alive we send the ping command to server every 10 minutes so the KeepAlive handler is set to 600. You can use other values to save the battery but it will make worse the detection of the disconnects on the client and server side. And will increase the time between the disconnect and reconnect.
BOOL scheduled = [app setKeepAliveTimeout:pingTimeout handler:^{ // Schedule processing after some time interval
SchedulePing(0);
}
Where SchedulePing(0) will be executed as following:
StartLongBGTask();
if ( avoidFinishBgTask != NULL )
*avoidFinishBgTask = true;
m_pingTimer = CreateTimer(pingTimeout, PingTimerCallback); // result is ignored
And StartLongBGTask is a
m_bgTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler: ^{
[[UIApplication sharedApplication] endBackgroundTask:m_bgTask];
m_bgTask = UIBackgroundTaskInvalid;
}];
This is needed to make sure that application will not be suspended before sending the ping and waiting the reply on ping from the server. Also if socket is already disconnected it could happen that reconnect is needed which will take some time and needs proccess to be running in the background.
But make sure to free the background tasks properly when you don't longer need them. Other wise application will be killed by the system when bg timeout will be exceeded.
Apple have provided details about this on official documentation.You can find it here https://developer.apple.com/library/ios/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/AdvancedAppTricks/AdvancedAppTricks.html
As per the documentation
There are several requirements for implementing a VoIP app:
1.Add the UIBackgroundModes key to your app’s Info.plist file. Set the value of this key to an array that includes the voip string.
2.Configure one of the app’s sockets for VoIP usage.
3.Before moving to the background, call the setKeepAliveTimeout:handler: method to install a handler to be executed periodically. Your app can use this handler to maintain its service connection.
4.Configure your audio session to handle transitions to and from active use.
5.To ensure a better user experience on iPhone, use the Core Telephony framework to adjust your behavior in relation to cell-based phone calls; see Core Telephony Framework Reference.
6.To ensure good performance for your VoIP app, use the System Configuration framework to detect network changes and allow your app to sleep as much as possible.
Including the voip value in the UIBackgroundModes key lets the system know that it should allow the app to run in the background as needed to manage its network sockets. This key also permits your app to play background audio (although including the audio value for the UIBackgroundModes key is still encouraged). An app with this key is also relaunched in the background immediately after system boot to ensure that the VoIP services are always available. For more information about the UIBackgroundModes key, see Information Property List Key Reference.

Is there a simpler example available for performing serial communication using EAAccessory?

I am writing an iPhone application that needs to send and receive data over the serial connection. I have been studying Apple's EADemo found here: EAAccessory reference
The problem is that I am finding this example too complex to take in. Is there a simpler example available for how to send and receive to and from a connected accessory over a serial connection?
I'm looking for something like sending four integer values to the accessory, and then sending them back to the iPhone using a const char buffer.
Will cost you $5 on Amazon, but the examples are easy: EAAccessory ebook
If you use MFi Programming, I think that's very simple.
First, you must setup connection, in this step you need know protocol string of external accessory. Open session with this protocol string. When open session use codes:
_session = [[EASession alloc] initWithAccessory:accessory forProtocol:_iAPProtocolString];
if (_session)
{
[[_session inputStream] setDelegate:self];
[[_session inputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[[_session inputStream] open];
[[_session outputStream] setDelegate:self];
[[_session outputStream] scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[[_session outputStream] open];
}
Then, you can write data to external accessory like this:
uint8_t buff[4];
buff[0] = 0xE0;
buff[1] = 0x10;
buff[2] = 0x00;
buff[3] = 0x1A;
bytesWritten = [[_session outputStream] write:[_writeData bytes] maxLength:[_writeData length]];