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.