OS X: как смотреть события чтения сокетов с помощью NSRunLoop?

Я пишу приложение Cocoa. В приложении есть сокет, и всякий раз, когда сокет становится доступным для чтения, я хочу читать данные из сокета, обрабатывать данные и соответствующим образом обновлять пользовательский интерфейс. Я хочу интегрировать проверку события чтения в основной цикл, т.е. я хочу присоединить сокет к основному циклу и заставить основной цикл вызывать обратный вызов всякий раз, когда этот сокет становится доступным для чтения.

Я написал тестовое приложение, но оно почему-то не работает:

#include <stdio.h>
#include <Foundation/NSAutoReleasePool.h>
#include <Foundation/NSRunLoop.h>
#include <Foundation/NSPort.h>

@interface MyDelegate : NSObject <NSPortDelegate> {
}
- (void)handlePortMessage:(NSPortMessage *)portMessage;
@end

@implementation MyDelegate
- (void)handlePortMessage:(NSPortMessage *)portMessage {
    printf("Haiz\n");
}
@end

int
main() {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSSocketPort *server = [NSSocketPort alloc];
    MyDelegate *foo = [MyDelegate alloc];
    [server initWithTCPPort: 1234];
    [server setDelegate: foo];
    [[NSRunLoop mainRunLoop] addPort: server forMode: NSDefaultRunLoopMode];
    [[NSRunLoop mainRunLoop] run];
    [pool release];
    return 0;
}

Предполагается, что приложение прослушивает локальный порт 1234, и всякий раз, когда кто-то подключается к серверу или отправляет данные на сервер, приложение должно печатать «Haiz» на консоли. Однако приложение вообще ничего не делает. Сокет создан, и я могу подключиться по телнету к порту 1234, но приложение ничего не выводит на консоль.

Что я делаю неправильно?


person Hongli    schedule 16.02.2010    source источник
comment
Среди прочего, отправка alloc в класс, но не отправка init в экземпляр. Вы полностью забываете об этом с вашим объектом MyDelegate, который хорошо демонстрирует, почему вы всегда должны держать alloc и init вместе в одном и том же выражении сообщения: MyDelegate *foo = [[[MyDelegate alloc] init] autorelease]; Также обратите внимание, что вы должны освобождать объекты, которые вы выделяете. Просмотрите правила управления памятью: developer.apple. com/mac/library/documentation/Общие/   -  person Peter Hosey    schedule 16.02.2010


Ответы (2)


Из документации:

Объект NSSocketPort можно использовать в качестве конечной точки для подключений к распределенным объектам.

Это не то, что вы делаете здесь.

Вам нужен либо NSFileHandle вокруг дескриптора файла сокета из API сокетов BSD или CFSocket. Это позволит вам поместить сокет в цикл выполнения.

person Peter Hosey    schedule 16.02.2010
comment
NSFileHandle не является NSPort, и для него нет CFRunLoopSource, поэтому я не могу подключить его к основному циклу. - person Hongli; 16.02.2010
comment
Я знаю, что NSFileHandle не является NSPort. NSPorts предназначены для IPC. Ваш метод делегата не вызывается, потому что вы никогда не получаете сообщение о порте на этом сокете. Что касается присоединения к циклу выполнения, либо отправьте дескриптору файла сообщение acceptConnectionInBackgroundAndNotify, либо вместо этого используйте CFSocket. - person Peter Hosey; 16.02.2010
comment
У меня сложилось впечатление, что FoundationKit — это просто объективная оболочка C для CoreFoundation, но теперь кажется, что они совершенно разные, и что есть некоторые вещи, которые я могу делать с CoreFoundation, но не с FoundationKit? Причина, по которой я пытаюсь цепляться за FoundationKit, заключается в том, что я на самом деле пишу приложение на MacRuby, а не на Objective C, и у него есть некоторые проблемы с указателями функций, которые ожидают API-интерфейсы CoreFoundation. - person Hongli; 16.02.2010
comment
Foundation старше, чем Core Foundation. Некоторые классы Foundation теперь являются оболочками для классов CF или соединены с ними по бесплатному телефону, но не все, и есть некоторые вещи, которые вы можете делать с каждым из них, но не можете делать с другим. - person Peter Hosey; 16.02.2010

Вы хотите использовать NSSocketPort так, как делаете, но затем создайте NSFileHandle для приема соединений в сокете. Вы можете получать обратные вызовы в основном потоке, как и ожидали, сначала для новых подключений, а затем для данных об этих подключениях. Используйте этот Статья О'Рейли и просто игнорируйте HTTP.

person Kevin Conner    schedule 26.05.2012