Как получить, чтобы didReadData в GCDAsyncSocket выполнялся в текущем RunLoop?
Я пытаюсь получить простой пример работы с 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];
}
И можете увидеть мое приложение сервера тест сокета получить строку
"Тестирование...123...\ г \ п"
Но когда мой сервер тестирования сокетов отправил строку назад, я наивно ожидал выполнения делегата didReadData
- (void)socket:(GCDAsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag
И все же холодная суровая реальность заставила меня учиться этому, пока я не позвоню
[asyncSocket readDataWithTimeout:5.0 tag:0];
... делегат didReadData не будет вызван.
ОК, хорошо. Я понял
Читая документацию еще немного, он ясно заявляет, что
AsyncSocket - библиотека сокетов TCP на основе RunLoop.
Итак, теперь я смотрю на эту вещь RunLoop, которая, на мой взгляд, похожа на цикл сообщений в Microsoft Windows. Поскольку iOS является архитектурой, управляемой событиями / сообщениями (так же, как Win32), то основной поток по умолчанию, в котором я сейчас нахожусь, очевидно, имеет собственный цикл сообщений для обработки событий.
Теперь я путаюсь с тем, что iOS RunLoop выглядит как некая отдельная сущность, с которой нужно работать, чтобы заставить GCDAsyncSocket работать должным образом.
Когда он заявляет, что его установленный по умолчанию режим запуска цикла является NSDefaultRunLoopMode, который находится в основном потоке.
Смущены еще?
Таким образом, под 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 ответа
Ладно, у меня получилось как-то работать.
Дайте мне знать, если это противоречит определенным "лучшим практикам".
- (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 хотя)
Я знаю, что это старый вопрос и уже есть принятый ответ, но вот мое решение, которое я уже использовал в одном из моих приложений:
после подключения к хосту запустите следующую очередь отправки:
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];
}
});
Вы могли бы использовать ту же очередь для отправки запросов сердцебиения, чтобы поддерживать соединение.