В настоящее время я работаю над приложением, которое требуется для связи с сервером. Я собираюсь передать пакеты двух разных размеров, небольшой пакет для небольших данных (например, sql-запросы) и большой пакет для файлов (или фрагментов файлов, когда файлы превышают 1024 байта). Эти пакеты выглядят так:
Маленький пакет:
typedef struct small_packet {
int msgtype:8;
int size:16;
int following:24;
char data1[64];
char data2[64];
} 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;
Я написал основные части сервера в C++, используя select(). Теперь сервер принимает входящие соединения и возвращает список игр, когда клиент запрашивает его. Я проверил это с простым клиентом, написанным на C.
Требования к протоколу для связи с сервером выглядят следующим образом:
*
* 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
*
Теперь вот сложная часть; какой подход к программированию сокетов для платформы iOS я должен взять? Сейчас у меня есть класс коммуникатора класса CFRead/WriteStreamRef & NSInput/OutputStream, который имеет функцию потока, которая переключает eventCodes (например, NSStreamEventHasBytesAvailable). Это, похоже, не слишком хорошо работает. Либо я делаю что-то неправильно, либо у меня возникают проблемы с медленным ответом на запросы или блокирование чтения. В любом случае, прямо сейчас я получаю ответ 0x09 при запросе списка игр. Остальное либо никогда не принимается клиентом, либо отбрасывается, потому что программа прошла мимо чтения (я не знаю, как это сделать).
Я думаю о переключении на замечательный API CocoaAsyncSocket, но переписывание моего кода не очень заманчиво.
Есть ли способ написать простой TCP-клиент для iPhone, который не блокирует и получает данные, которые могут быть переданы структуре C-стиля? Должен ли я переопределять свои структуры или даже использовать что-то еще для данных? Мне бы понравились примеры или хорошие ссылки (и поверьте мне, я gooooooooogled!).
Любая помощь очень ценится!
Вы можете прочитать это краткое введение, используя CocoaAsyncSocket.
Вы можете легко установить соединение:
GCDAsyncSocket *socket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
подключитесь к вашему серверу:
NSError *err = nil;
if (![socket connectToHost:@"yourserverip" onPort:80 error:&err]) // Asynchronous!
{
// If there was an error, it likely something like "already connected" or "no delegate set"
NSLog(@"I goofed: %@", err);
}
Теперь поместите ваши структуры в NSData:
NSData *myData = [NSData dataWithBytes:&myFilePacket length:sizeof(myFilePacket)];
и, наконец, отправьте свои данные:
[socket writeData:myData withTimeout:-1 tag:1];
CocoaAsyncSocket также предлагает множество полезных методов обратного вызова, таких как:
- (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag
или для входящих пакетов вы можете вернуть NSData в struct:
- (void)socket:(GCDAsyncSocket *)sender didReadData:(NSData *)data withTag:(long)tag
{
filePacket packet;
[data getBytes:&packet length:sizeof(packet)];
}
и так далее... Посмотрите на файлы заголовков...
Да, это так просто.
GCDAsyncSocket *socket
как свойство или переменную экземпляра. В противном случае он может быть уничтожен после отправки ваших данных.