TCP Approach on the iOS platform

I am currently working on an app which needs to communicate with a server. I am going to transfer packets of two different sizes, a small packet for small data (such as sql requests), and a big packet for files (or pieces of files, when the files exceed 1024bytes). These packets look like this:

Small packet:

typedef struct small_packet {
    int msgtype:8;
    int size:16;
    int following:24;
    char data1[64];
    char data2[64];
} packet;

Large packet:

typedef struct file_packet {
    int msgtype:8; //For partial file packet this should be 0x02
    int size:16;
    int following:24;
    char data1[64];
    char fileBuffer[1024];
} filePacket;

I have written the basic parts of the server in C++ using select(). The server now accepts incoming connections, and returns a list of games when a client requests it. I have tested this with a simple client written in C.

My protocol requirements for server communication look like this:

 *
 * Message types:
 * 0x01: Login message. Data1 is username, data2 is pw hash
 * 0x02: Login accepted/failed. Data1 is 1 for accept or 0 for not accepted
 * 0x03: New user. Data1 is username, data2 is pw hash
 * 0x04: New user accepted/failed. Data1 is 1 for accept and 0 for error
 * 0x05: Friend list request. Data1 is username and data2 should be 0
 * 0x06: Friend list header. Data1 is username and data2 should be 0
 * 0x07: Friend list packet. Data1 is username, data2 is 0
 * 0x08: Game list request. Data1 is username, data2 is 0
 * 0x09: Game list header. Data1 is username, data2 is game id
 * 0x0A: Game list packet. Following is gameID, data1 is randomness (0 or 1, first bit), data2 is players (divided by semicolon)
 * 0x0B: Friend add. Data1 is username, data2 is friend username
 * 0x0C: Random game add. Data1 is username, data2 is 0
 * 0x0D: Friend game add. Data1 is username, filebuffer holds friends' usernames, divided by semicolons
 * 0x10: File add. Data1 is username, data2 is game id
 * 0x11: File request. Data1 is username, data2 is game id
 * 0x12: File header. Data1 is turn number, data2 is game id
 * 0x13: File part. Data1 is 0, filebuffer is filepart
 *

Now here comes the tricky part; what approach to socket programming for the iOS platform should I take? Right now I have a CFRead/WriteStreamRef & NSInput/OutputStream socket communicator class which has a stream function that switches over eventCodes (such as NSStreamEventHasBytesAvailable). This doesn't seem to work too well. Either I am doing something wrong, or I have trouble with either requests being answered too slowly, or blocking reads. Either way, right now I only get the 0x09 reply when requesting a game list. The rest is either never received by the client, or dropped because the program has moved past the reading (I don't know how).

I am thinking of switching to the wonderful CocoaAsyncSocket API, but rewriting my code isn't very tempting.

Is there a way to write a simple TCP client for the iPhone which does not block, and receives data which may be cast to a C-style struct? Do I have to redefine my structs, or even use something else for the data? I'd love examples, or good links (and trust me, I've gooooooooogled!).

Any help is very appreciated!

Upvotes: 0

Views: 338

Answers (1)

crizztus
crizztus

Reputation: 307

You might read this short intro on using CocoaAsyncSocket.

You can easily establish a connection by:

GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];

connect to your server:

NSError *err = nil;
if (![socket connectToHost:@"yourserverip" onPort:80 error:&err]) // Asynchronous!
{
// If there was an error, it's likely something like "already connected" or "no delegate set"
NSLog(@"I goofed: %@", err);
}

Now put your struct(s) to NSData:

NSData *myData = [NSData dataWithBytes:&myFilePacket length:sizeof(myFilePacket)];

and finally send your data:

[socket writeData:myData withTimeout:-1 tag:1];

CocoaAsyncSocket also offers a lot of useful callback methods like:

- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag

or for incoming packets you can cast NSData back to struct:

- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
  filePacket packet;
  [data getBytes:&packet length:sizeof(packet)];
}

and so on... Have a look at the header files...

Yes, it is as simple as that.

Upvotes: 1

Related Questions