iPhone TCP/IP Socket Server/Client Program - iphone

I have read a lot of questions regarding this subject on this website however they didn't quiet answer my question. If you can't be ### about my goal or background skip to the question.
My Goal
Is to build a server that can run on Mac OS X 10.4+ and later, port it to Windows XP/Vista (no idea how to do that yet, but that's a problem for later).
Then let the iPhone be the client that is able to see the computer names that are running the server (through WiFi). The user of the iPhone can then select the computer name to connect to the server on that computer.
After that they can send simple text messages to each other. For example, the iPhone sends 'Knock Knock' and the server responds 'Who is there?'. Or a simple client: 'Ping', server responds 'Pong' will do just fine.
Background
I have worked with sockets in the past, but only in Visual Basic 6 with the WINSOCKET.dll it was very easy to create a TCP/IP server.
server.host = localhost;
server.port = 12203;
server.listen();
With the client I only needed to do the following to connect.
client.connect(localhost, 12203);
There were some callbacks available like connect, close, dataArrival, etc. which I could use to do anything I want.
Perhaps for the iPhone there are libraries written for it, but is it that hard to create this simple application yourself? After doing some research I understand that I have to look in the area of CFNetwork, CFHost, CFSocket, CFStream.
Question
Is there anyone that could guide me to a tutorial or post the code where you have two buttons on the iPhone. [ Start Server ] and [ Connect to Server] where the first will start a TCP/IP server on a certain port and the second connects to it.
After a connection has been made maybe also the code to send a simple 'Ping'-message to the server after the server receives this responds with a 'Pong'-message to the client.
That would really be helpful. But maybe I am asking for to much here.

this tutorial to create a chat sample app works very well and is pretty straightforward (any iphone noob, like me, can make it work, EVEN IN SIMULATOR MODE it connects to external socket server).
i adapted it to talk my socket server and it works like a charm. this is test code, so there's no real concern with loose ends. it only sends one message (your logon id) and receives an answer back, which it shows in the console.
//
// ViewController.m
// zdelSocketTest01a
//
//
#import "ViewController.h"
#implementation ViewController
#synthesize inputNameField;
#synthesize joinView;
- (void)initNetworkCommunication {
uint portNo = 5555;
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"227.3.4.56", portNo, &readStream, &writeStream);
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self initNetworkCommunication];
messages = [[NSMutableArray alloc] init];
}
- (void)viewDidUnload
{
[self setInputNameField:nil];
[self setJoinView:nil];
[self setJoinView:nil];
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (IBAction)joinChat:(id)sender {
NSString *response = [NSString stringWithFormat:#"logon,%#", inputNameField.text];
NSData *data = [[NSData alloc] initWithData:[response dataUsingEncoding:NSASCIIStringEncoding]];
[outputStream write:[data bytes] maxLength:[data length]];
}
/*
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent);
}
*/
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
typedef enum {
NSStreamEventNone = 0,
NSStreamEventOpenCompleted = 1 << 0,
NSStreamEventHasBytesAvailable = 1 << 1,
NSStreamEventHasSpaceAvailable = 1 << 2,
NSStreamEventErrorOccurred = 1 << 3,
NSStreamEventEndEncountered = 1 << 4
};
uint8_t buffer[1024];
int len;
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened now");
break;
case NSStreamEventHasBytesAvailable:
NSLog(#"has bytes");
if (theStream == inputStream) {
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
}
}
}
} else {
NSLog(#"it is NOT theStream == inputStream");
}
break;
case NSStreamEventHasSpaceAvailable:
NSLog(#"Stream has space available now");
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
break;
default:
NSLog(#"Unknown event %i", streamEvent);
}
}
/*
- (void) messageReceived:(NSString *)message {
[messages addObject:message];
[self.tView reloadData];
}
*/
#end
your ViewController.h file would contain
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <NSStreamDelegate>
#property (weak, nonatomic) IBOutlet UITextField *inputNameField;
#property (weak, nonatomic) IBOutlet UIView *joinView;
- (IBAction)joinChat:(id)sender;
#end
NSInputStream *inputStream;
NSOutputStream *outputStream;
NSMutableArray * messages;
NOOBS ONLY: you must link your button and text field by pressing CONTROL and dragging the object into the code window. when you do that, the properties above will automatically be created. check this video tutorial if you are stumped
NOOBS ONLY 2: this socket will output in the CONSOLE PANE of XCODE. on the upper right hand corner of your xcode window, click HIDE OR SHOW THE DEBUG AREA (ask for help if necessary).
built and tested (simulator and device) on a macbook with 2GB memory, using xcode 4.2 for snow leopard.

I recommend the the following:
Cocoa Async Socket
There's also a basic example project on the site to get you started. I've had good success working with that framework.

I would expect you would want your server to already be started, and then you would only need a "Connect to Server" button, and then your "Ping". Otherwise, you need a separate process on your server box which responds to the "Start Server" message and starts the server.

Related

Record and send/stream sound from iOS device to a server continuously

I am developing an iOS app that has a button with a microphone on it (along with other features). When the user presses the microphone, it gets highlighted and the app should now start recording sound from the device´s microphone and send to a server (a server dedicated to the app, developed by people that I know, so I can affect its design).
I am looking for the simplest yet sturdiest approach to do this, i.e. I have no need to develop a complicated streaming solution or VoIP functionality, unless it is as simple to do as anything else.
The main problem is that we have no idea for how long the user will be recording sound, but we want to make sure that sounds are sent to the server continuously, we do not wish to wait until the user has finished recording. It is okay if the data arrives to the server in chunks however we do not wish to miss any information that the user may be recording, so one chunk must continue where the previous one ended and so on.
Our first thought was to create "chunks" of sound clips of for example 10 seconds and send them continuously to the server. Is there any streaming solution that is better/simpler that I am missing out on?
My question is, what would be the most simple but still reliable approach on solving this task on iOS?
Is there a way to extract chunks of sound from a running recording by AVAudioRecorder, without actually stopping the recording?
look at this
in this tutorial, the sound recorded will be saved at soundFileURL, then you will just have to create an nsdata with that content, and then send it to your server.
hope this helped.
EDIT :
I just created a version that contain 3 buttons, REC, SEND and Stop :
REC : will start recording into a file.
SEND : will save what was recorded on that file in a NSData, and send it to a server, then will restart recording.
and STOP : will stop recording.
here is the code :
in your .h file :
#import <UIKit/UIKit.h>
#import <AVFoundation/AVFoundation.h>
#interface ViewController : UIViewController <AVAudioRecorderDelegate>
#property (nonatomic, retain) AVAudioRecorder *audioRecorder;
#property (nonatomic, retain) IBOutlet UIButton *recordButton;
#property (nonatomic, retain) IBOutlet UIButton *stopButton;
#property (nonatomic, retain) IBOutlet UIButton *sendButton;
#property BOOL stoped;
- (IBAction)startRec:(id)sender;
- (IBAction)sendToServer:(id)sender;
- (IBAction)stop:(id)sender;
#end
and in the .m file :
#import "ViewController.h"
#implementation ViewController
#synthesize audioRecorder;
#synthesize recordButton,sendButton,stopButton;
#synthesize stoped;
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
#pragma mark - View lifecycle
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
sendButton.enabled = NO;
stopButton.enabled = NO;
stoped = YES;
NSArray *dirPaths;
NSString *docsDir;
dirPaths = NSSearchPathForDirectoriesInDomains(
NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *soundFilePath = [docsDir
stringByAppendingPathComponent:#"tempsound.caf"];
NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];
NSDictionary *recordSettings = [NSDictionary
dictionaryWithObjectsAndKeys:
[NSNumber numberWithInt:AVAudioQualityMin],
AVEncoderAudioQualityKey,
[NSNumber numberWithInt:16],
AVEncoderBitRateKey,
[NSNumber numberWithInt: 2],
AVNumberOfChannelsKey,
[NSNumber numberWithFloat:44100.0],
AVSampleRateKey,
nil];
NSError *error = nil;
audioRecorder = [[AVAudioRecorder alloc]
initWithURL:soundFileURL
settings:recordSettings
error:&error];
audioRecorder.delegate = self;
if (error)
{
NSLog(#"error: %#", [error localizedDescription]);
} else {
[audioRecorder prepareToRecord];
}
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation != UIInterfaceOrientationPortraitUpsideDown);
}
- (BOOL) sendAudioToServer :(NSData *)data {
NSData *d = [NSData dataWithData:data];
//now you'll just have to send that NSData to your server
return YES;
}
-(void)audioRecorderDidFinishRecording:(AVAudioRecorder *)recorder successfully:(BOOL)flag
{
NSLog(#"stoped");
if (!stoped) {
NSData *data = [NSData dataWithContentsOfURL:recorder.url];
[self sendAudioToServer:data];
[recorder record];
NSLog(#"stoped sent and restarted");
}
}
- (IBAction)startRec:(id)sender {
if (!audioRecorder.recording)
{
sendButton.enabled = YES;
stopButton.enabled = YES;
[audioRecorder record];
}
}
- (IBAction)sendToServer:(id)sender {
stoped = NO;
[audioRecorder stop];
}
- (IBAction)stop:(id)sender {
stopButton.enabled = NO;
sendButton.enabled = NO;
recordButton.enabled = YES;
stoped = YES;
if (audioRecorder.recording)
{
[audioRecorder stop];
}
}
#end
Good Luck.
It might actually be easier to have fixed-size chunks, instead of fixed-time. Then you can have two buffers, one that you currently fill with sample data. When the active buffer is full, then switch to fill the other buffer, while sending a signal to a sender-thread that takes the first buffer and sends it to the server.
You can of course use fixed-time instead, but then you need to make sure that the buffer is large enough to keep all samples, or use a dynamic buffer that can increase in size when needed. The double-buffering and sending thread can still be the same though.

NSStreams Crashing Program!

All,
I've run it down to this point by commenting, breakpoints, etc. The program crashes at the marked code.
-(void) initNetworkCommunication
{
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)#"192.168.17.1", 2004, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];//WHY MUST YOU CRASH HERE
[outputStream open];//WHY MUST YOU CRASH HERE ALSO!!?!?
NSLog(#"She be opened, sir!");
}
It doesn't crash if I comment out both of those, but it crashes if I comment out either one (so i.e. they are both causing the program to crash). There is no information that gets posted in the debugger either. All it does is send me to main.m and show me
"Thread 1: Program received signal: "EXC_BAD_ACCESS".
Thanks for the help in advance!
Edit: Here is my delegate method, but it doesn't even present the second active line in the log.
- (void)stream:(NSStream *)theStream handleEvent:(NSStreamEvent)streamEvent {
NSLog(#"stream event %i", streamEvent); //this doesn't post in the log when stream opened...
switch (streamEvent) {
case NSStreamEventOpenCompleted:
NSLog(#"Stream opened");
break;
case NSStreamEventHasBytesAvailable:
if (theStream == inputStream) {
uint8_t buffer[1024];
int len;
while ([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0) {
NSString *output = [[NSString alloc] initWithBytes:buffer length:len encoding:NSASCIIStringEncoding];
if (nil != output) {
NSLog(#"server said: %#", output);
//[self messageReceived:output];
}
}
}
}
break;
case NSStreamEventErrorOccurred:
NSLog(#"Can not connect to the host!");
break;
case NSStreamEventEndEncountered:
[theStream close];
[theStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
//[theStream release];
theStream = nil;
break;
default:
NSLog(#"Unknown event");
}
}
What is happening is that the instance of the delegate class is being deallocated (causing EXC_BAD_ACCESS in the run loop) either because you didn't retain it, or you are using ARC (pretty likely) and you do not have a reference to it.
The solution is to either call retain on the delegate class, approximately like so:
SomeStreamDelegate *theDelegate = [[SomeStreamDelegate alloc] init];
[theDelegate retain];
Or if you do have ARC enabled, make an instance variable in the class where you alloc the delegate, and store your instance of connection there. That way ARC will not dealloc it, because the instance var counts as a reference.
If you are using ARC, cast the streams like this :
inputStream = (__bridge NSInputStream *)readStream;
outputStream = (__bridge NSOutputStream *)writeStream;
This should prevent the crash. And keep in mind that if your streams are owned by a separate thread than the main thread that means the run loop needs to be called manually using run method after opening the streams.
When I placed this in my View Controller (and not in a separate class) it worked perfectly.
I had a similar issue where my app would crash in the -handleEvent callback with a huge streamEvent number. I resolved it by making sure I initialize the NSStream objects (input and output) AND open a connection to the server in the -init method of the NetworkClient object which my VC plans to use.
Try this out once,
NSInputStream * inputStream = objc_unretainedObject(readStream);
May be a casting issue

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

writeData on a syncsocket always blocks on iPhone

I use the asyncsocket sample as a starting point to learn more about wlan communication on iPhone.
On the Mac I start a sample server opening port 0. This works, since I can write data with a test client running on the mac.
On the iPhone I think I managed to connect since "streams connected" returns YES.
Then I would like to send data with a syncsocket: (EDITED VERSION WITH COMPLETE CODE)
import "InterfaceTestAppDelegate.h"
import "InterfaceTestViewController.h"
import "AsyncSocket.h"
import "SyncSocket.h"
#implementation InterfaceTestAppDelegate
#synthesize window;
#synthesize viewController;
(void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)remoteHost port:(UInt16)remotePort
{
NSLog(#"Socket is connected!");
NSLog(#"Remote Address: %#:%hu", remoteHost, remotePort);
NSString *localHost = [sock localHost];
UInt16 localPort = [sock localPort];
NSLog(#"Local Address: %#:%hu", localHost, localPort);
}
(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSLog(#"application:didFinishLaunchingWithOptions:");
/*
asyncSocket = [[AsyncSocket alloc] initWithDelegate:self];
NSError *err = nil;
if (![asyncSocket connectToHost: #"192.168.0.30" onPort: 1234 error: &err])
{
NSLog(#"Error connecting: %#", err);
}
NSData *data = [#"testxyz" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"trace 1");
[asyncSocket writeData:data withTimeout:10 tag:0];
NSLog(#"trace 2");
*/
syncSocket = [[SyncSocket alloc] initWithTimeout: 10];
syncSocket.nsLog = YES;
if (![syncSocket connectToHost: #"192.168.0.30" onPort: 12345])
{
NSLog(#"Error connecting syncSocket:");
}
NSData *data = [#"testxyz" dataUsingEncoding:NSUTF8StringEncoding];
NSLog(#"syncSocket trace 1");
[syncSocket writeData:data];
NSLog(#"syncSocket trace 2");
[window addSubview:viewController.view];
[window makeKeyAndVisible];
return YES;
}
It never continues to send the data, the writeData always blocks.
The IP 192.168.0.30 is my Mac's IP. I just used any port 12345 now as you suggested above.
But I don't really know what I have to do on the Mac to receive??
As you can see I actually use syncsocket, then it blocks.
I also tried asyncSocket, then I get the message in the asyncsocket class: writeStream Can NOT Accept Bytes
Maybe its that I don't setup the Mac correctly,ie what app do I need to run on the Mac to test?
Many thank!
For what it's worth, this is specifically how you typically read in some data using AsyncSocket:
-(void)onSocket:(AsyncSocket *)sock
didReadData:(NSData*)data withTag:(long)tag
{
[data getBytes:&getMe length:sizeof(CommProt)];
// now, you must roll in the next read...
[sock readDataToLength:sizeof(CommProt) withTimeout:-1 tag:0];
// CommProt is your communications protocol, so sizeof(CommProt)
// is how much to read at a chunk.
// you can now simply access the fields of getMe,
// for example getMe.x, getMe.y, getMe.latestValue etc etc.
// hope it helps!
}
Of course, you would have previously rolled in the first "primer" read command:
You do that when you connect to a host, hence:
-(void)onSocket:(AsyncSocket *)sock
didConnectToHost:(NSString *)host port:(UInt16)port
{
if ( yourAppSaysItsOkToConnectAtThisMoment == NO )
{
[sock disconnect]; // (so easy, AsyncSockets is a masterpiece)
return;
}
// .. blah blah
// the critical 'primer' read command
[sock readDataToLength:sizeof(CommProt) withTimeout:-1 tag:0];
// .. blah blah
}
Don't forget you must roll in the next read in two places, (a) when you first connect and (b) of course, after each read!
In the example your communications protocol would look like this ...
typedef struct _CommProt // v.3
{
BOOL pressExplosionButton;
BOOL pressFireworksButton;
float usersSteering;
float usersTemperature;
float usersAltitude;
float usersAngle;
}
CommProt;
Variable like "getMe" in the example would simply look like this:
CommProt getMe;
CommProt sendMe;
If you are struggling to understand this type of communications protocol, also try this long answer:
Tablet(iPad/Android)-Server Communication Protocol
AsyncSocket is incredibly beautiful, it was written by the mysterious Justin Voss who seemed to drop off the internet after giving it to the world - it's one of the best libraries ever written, it's a masterpiece.
Hope it helps.

Cocoa-Touch framework for speaking to a TCP socket? [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 6 years ago.
Improve this question
I have a daemon running on a server that's latched onto a TCP/IP port. I'm looking to see if there's currently any support iPhone/Cocoa-touch frameworks that gives a nice OO wrapper for speaking to the daemon over an IP socket. I need to be able to interactively query the daemon with commands and retrieve back information.
If there isn't any OO wrappers for such a task, what's the next best bet?
http://code.google.com/p/cocoaasyncsocket/
This is what you want.
Here is some sample code from the previously mentioned AsyncSocket code that I modified into a class called SocketCommunicationManager.
A few things to note:
Our messages are being delimited with newline characters (\n) so when reading data from the socket I had to make sure to use the right constant from the AsyncSocket class (LFData in our case). AsyncSocket also provides CRLFData, CRData, and ZeroData as predefined message delimiters.
I set up the SocketCommunicationManager to always wait for an incoming message after I received and acted on a previous one. To accomplish that I used the (void)readDataToData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag method. This method will wait until data is written to the socket, read up until the specified delimiter, and then call the delegate method (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag;
The SocketCommunicationManager uses NSNotificationCenter to publish any messages received from the socket. These messages are named kNotification and the message is put into the userInfo dictionary using the kNotificationMessage key.
Everything read from the socket is wrapped in an NSData object, so you'll have to decode that data after it is received.
Here's the code:
#import <Foundation/Foundation.h>
extern NSString * const kNotification;
extern NSString * const kNotificationMessage;
#class AsyncSocket;
#interface SocketCommunicationManager : NSObject {
AsyncSocket *socket;
BOOL isRunning;
NSNotificationCenter* notificationCenter;
}
#property (readwrite, assign) BOOL isRunning;
- (void)connectToHost:(NSString *)hostName onPort:(int)port;
- (void)sendMessage:(NSString *)message;
- (void)disconnect;
#end
#import "SocketCommunicationManager.h"
#import "AsyncSocket.h"
NSString * const kNotification = #"kNotification";
NSString * const kNotificationMessage = #"kNotificationMessage";
#implementation SocketCommunicationManager
#synthesize isRunning;
- (id) init {
if (!(self = [super init]))
return nil;
socket = [[AsyncSocket alloc] initWithDelegate:self];
[self setIsRunning:NO];
notificationCenter = [NSNotificationCenter defaultCenter];
return self;
}
- (void)connectToHost:(NSString *)hostName onPort:(int)port {
if (![self isRunning]) {
if (port < 0 || port > 65535)
port = 0;
NSError *error = nil;
if (![socket connectToHost:hostName onPort:port error:&error]) {
NSLog(#"Error connecting to server: %#", error);
return;
}
[self setIsRunning:YES];
} else {
[socket disconnect];
[self setIsRunning:false];
}
}
- (void)disconnect {
[socket disconnect];
}
- (void)dealloc {
[super dealloc];
[socket disconnect];
[socket dealloc];
}
- (void)sendMessage:(NSString *)message {
NSString *terminatedMessage = [message stringByAppendingString:#"\r\n"];
NSData *terminatedMessageData = [terminatedMessage dataUsingEncoding:NSASCIIStringEncoding];
[socket writeData:terminatedMessageData withTimeout:-1 tag:0];
}
#pragma mark AsyncSocket Delegate
- (void)onSocket:(AsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port {
NSLog(#"Connected to server %#:%hu", host, port);
[sock readDataToData:[AsyncSocket LFData] withTimeout:-1 tag:0];
}
- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag {
NSData *truncatedData = [data subdataWithRange:NSMakeRange(0, [data length] - 1)];
NSString *message = [[[NSString alloc] initWithData:truncatedData encoding:NSASCIIStringEncoding] autorelease];
if (message)
NSLog(#"%#", message);
else
NSLog(#"Error converting received data into UTF-8 String");
NSDictionary *userInfo = [NSDictionary dictionaryWithObject:message forKey:kNotificationMessage];
[notificationCenter postNotificationName:kNotification object:self userInfo:userInfo];
[sock readDataToData:[AsyncSocket LFData] withTimeout:-1 tag:0];
}
- (void)onSocket:(AsyncSocket *)sock didWriteDataWithTag:(long)tag {
[sock readDataToData:[AsyncSocket LFData] withTimeout:-1 tag:0];
}
- (void)onSocket:(AsyncSocket *)sock willDisconnectWithError:(NSError *)err {
NSLog(#"Client Disconnected: %#:%hu", [sock connectedHost], [sock connectedPort]);
}
#end
Roughly speaking, going up the stack you have:
BSD sockets
CFSocket
CFReadStream/CFWriteStream/NSInputStream/NSOutputStream
CFHTTPStream
NSURLConnection
Sounds like you want CFSocket, or possibly CFStream.
Did you check out the BSD Sockets in Cocoa-Touch's networking guide?
As Genericrich points out, the Cocoa Async Socket framework is the way to go. This has been around for a while and seen a good deal of use. http://code.google.com/p/cocoaasyncsocket/
As pointed out by Genericrich, the AsyncSocket class is just wonderful for dealing with sockets.
http://code.google.com/p/cocoaasyncsocket/