Как получить, чтобы didReadData в GCDAsyncSocket выполнялся в текущем RunLoop? - PullRequest
7 голосов
/ 03 ноября 2011

Я пытаюсь получить простой пример работы с GCDAsyncSocket, и обнаруживаю, что мне не хватает определенного понимания и надеюсь, что вы, прекрасные люди, можете помочь объяснить это.

Я настроил GCDAsyncSocketвещи ниже:

dispatch_queue_t mainQueue = dispatch_get_main_queue();
asyncSocket = [[GCDAsyncSocket alloc] initWithDelegate:self delegateQueue:mainQueue];

NSString *host = @"192.168.169.132";
uint16_t port = 2112;

DDLogInfo(@"Connecting to \"%@\" on port %hu...", host, port);
self.viewController.label.text = @"Connecting...";

NSError *error = nil;
if (![asyncSocket connectToHost:host onPort:port withTimeout:5.0 error:&error])
{
    DDLogError(@"Error connecting: %@", error);
    self.viewController.label.text = @"Oops";
}
else
{
    DDLogVerbose(@"Connecting...");
}


- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    DDLogInfo(@"socket:%p didConnectToHost:%@ port:%hu", sock, host, port);
    self.viewController.label.text = @"Connected";

    // We're just going to send a test string to the server.

    NSString *myStr = @"testing...123...\r\n";
    NSData *myData = [myStr dataUsingEncoding:NSUTF8StringEncoding];

    [asyncSocket writeData:myData withTimeout:5.0 tag:0];
}

И можно увидеть, как мое приложение сервера тестирования сокетов получает строку

"testing ... 123 ... \ r \ n"

Но когда мне затем мой сервер тестирования сокетов отправил строку назад, я наивно ожидал, что didReadData делегат выполнит

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

холодная суровая реальность заставила меня понять, что пока я не вызову

[asyncSocket readDataWithTimeout:5.0 tag:0];

... делегат didReadData не будет вызван.

OK,Все в порядке.Я понял.

Читая документацию , еще больше ясно говорится, что

AsyncSocket - библиотека сокетов TCP на основе RunLoop.

Так что теперь я смотрю на эту RunLoop вещь, которая, на мой взгляд, похожа на цикл сообщений в Microsoft Windows .Поскольку iOS является архитектурой, управляемой событиями / сообщениями (точно так же, как Win32), тогда основной поток по умолчанию, в котором я сейчас нахожусь, очевидно, имеет собственный цикл сообщений для обработки событий.

Теперь я путаюсь с тем, что iOS RunLoop кажетсякак какой-то отдельный объект, с которым нужно работать, чтобы заставить GCDAsyncSocket работать должным образом.

Когда он заявляет, что по умолчанию установлен режим цикла запуска NSDefaultRunLoopMode, который находится в основном потоке.

Confusedпока что?

Итак, под Win32 мой код обработки событий коммюнике выглядел бы так:

while( sCOMport.hCOMport != INVALID_HANDLE_VALUE )  // ...while the COM port is open...
{
    // Wait for an event to occur on the port.
    WaitCommEvent( sCOMport.hCOMport, &dwCommStatus, NULL );

Конечно, он был бы в своем собственном потоке (еще не попал туда с помощью GCDAsyncSocket),но это будет своего рода "RunLoop".

Как мне сделать то же самое, используя GCDAsyncSocket , чтобы я не застрял в каком-то цикле опроса, заполняя очередь [asyncSocket readDataWithTimeout] вызывает?

Мне кажется, нам нужны лучшие примеры использования этой библиотеки.

Ответы [ 2 ]

18 голосов
/ 03 ноября 2011

Хорошо, у меня это получилось как-то.

Дайте мне знать, если это противоречит определенным «лучшим практикам».

- (void)socket:(GCDAsyncSocket *)sock didConnectToHost:(NSString *)host port:(UInt16)port
{
    // setup as normal...

    // then ...

    // Instigate the first read
    [asyncSocket readDataWithTimeout:-1 tag:0];

.
.
}

Тогда ... когда данные поступают ...

- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
{
    // Do whatever you need to do with the data ...
.
.
.
    // and at the end ...
.
.
    // Always keep a 'read' in the queue.
    [asyncSocket readDataWithTimeout:-1 tag:0];
}

Это даст вам операцию RunLoop без использования таймеров или других конструкций. И может быть включен в его собственный поток. (это все еще TBD)

1 голос
/ 31 марта 2014

Я знаю, что это старый вопрос и уже есть принятый ответ, но вот мое решение, которое я уже использовал в одном из своих приложений:

после подключения к хосту запустите следующую очередь отправки:

dispatch_queue_t alwaysReadQueue = dispatch_queue_create("com.cocoaasyncsocket.alwaysReadQueue", NULL);

dispatch_async(alwaysReadQueue, ^{
    while(![socket isDisconnected]) {
        [NSThread sleepForTimeInterval:5];
        [socket readDataWithTimeout:-1 tag:0];
    }
});

Вы могли бы использовать одну и ту же очередь для отправки запросов сердцебиения, просто чтобы поддерживать соединение.

...