Подход TCP на платформе iOS

0

В настоящее время я работаю над приложением, которое требуется для связи с сервером. Я собираюсь передать пакеты двух разных размеров, небольшой пакет для небольших данных (например, 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!).

Любая помощь очень ценится!

  • 0
    Используйте GCDAsyncSocket.
Теги:
sockets
tcp

1 ответ

1
Лучший ответ

Вы можете прочитать это краткое введение, используя 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)];
}

и так далее... Посмотрите на файлы заголовков...

Да, это так просто.

  • 0
    Спасибо, я переписал части моей серверной связи, используя AsyncSocket. Сервер, кажется, принимает клиента, и он возвращает пакеты по запросу, но мой клиент не читает пакеты, отправленные сервером. Мне кажется, что - (void) сокет: (GCDAsyncSocket *) отправитель didReadData: (NSData *) данные с тегом: (длинная) функция делегата тега никогда не вызывается. Странно ... О, вернемся к отладке. Еще раз спасибо :)
  • 0
    Вы можете объявить GCDAsyncSocket *socket как свойство или переменную экземпляра. В противном случае он может быть уничтожен после отправки ваших данных.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню