Reputation: 1301
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
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