« もっとCore Foundation / サーバソケットとCFStreamと私 | ホーム | Core Foundationを知る / CFHostでエラーハンドリング »

2011年5月17日

Core Foundation Hacks / iPhoneでも同じように動くのか?

さて、ここまでやってきた内容はMacOSX上の話にてございました(しかもSnow Leopard)。これがiOS上でも同じように挙動するかどうかも追っておきたい。

一応、MacOSX/iOS間で(ある程度)共通の環境基盤ということで用意されている以上、挙動がMacとiPhone/iPadとで食い違ったりしてたら嫌じゃんと。適宜こうした差異があるんだかないんだかというのは自分の目で確かめておきたいものでありまして。

下記の実装によって、MacOSXでの動作と同様にデフォルトではaccept()されたサーバ側ソケットがCFReadStreamClose()/CFWriteStreamClose()の呼び出しの際に同時にはcloseされず、kCFStreamPropertyShouldCloseNativeSocketプロパティをTRUE(kCFBooleanTrue)に設定することで同時closeとなるよう挙動を変更できることが確認できました。よろしければ皆様もご確認くださいませ。

#import <UIKit/UIKit.h>

@interface iPhoneSocketServerAppDelegate : NSObject <UIApplicationDelegate, NSStreamDelegate> {
    CFSocketRef _socket;
}

@property (nonatomic, retain) IBOutlet UIWindow *window;

@property (nonatomic, retain) IBOutlet UINavigationController *navigationController;

@end

#import "iPhoneSocketServerAppDelegate.h"

#include <sys/socket.h>
#include <arpa/inet.h>


@implementation iPhoneSocketServerAppDelegate


- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode
{
    NSLog(@"stream = %@, eventCode = %u", aStream, eventCode);
    
    switch (eventCode) {
        case NSStreamEventOpenCompleted:
            NSLog(@"open completed");
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [aStream release];
            break;
            
        case NSStreamEventHasBytesAvailable:
            [aStream close];
            [aStream removeFromRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
            [aStream release];
            break;
    }
}

- (void)setupInputStream:(NSInputStream*)instream
{
    [instream setDelegate:self];
    [instream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [instream open];
}

- (void)setupOutputStream:(NSOutputStream*)outstream
{
    [outstream setDelegate:self];
    [outstream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    [outstream open];
}


static void
_server_socket_callback_2(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
    CFSocketNativeHandle handle = *(CFSocketNativeHandle*)data;
    iPhoneSocketServerAppDelegate* appdele = (iPhoneSocketServerAppDelegate*)info;
    
    NSLog(@"accepted. (s = %p)", s);
    NSLog(@"handle = %d", handle);
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, handle, &readStream, &writeStream);
    
    CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
    CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
    
    [appdele setupInputStream:(NSInputStream*)readStream];
    [appdele setupOutputStream:(NSOutputStream*)writeStream];
    
    NSLog(@"readStream = %p, writeStream = %p", readStream, writeStream);
}

- (void)doServer
{
    CFSocketContext context = { 0, self, NULL, NULL, NULL };
    CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, _server_socket_callback_2, &context);
    
    int yes = 1;
    setsockopt(CFSocketGetNative(socket), SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));
    
    struct sockaddr_in sin;
    memset(&sin, 0, sizeof(sin));
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = htons(8888);
    sin.sin_len = sizeof(struct sockaddr_in);
    
    CFDataRef data = CFDataCreate(kCFAllocatorDefault, (UInt8*)&sin, sizeof(sin));
    CFSocketSetAddress(socket, data);
    CFRelease(data);
    
    CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socket, 0);
    CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
    CFRelease(source);
    
    NSLog(@"socket = %p", socket);
}

@synthesize window=_window;

@synthesize navigationController=_navigationController;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    // Add the navigation controller's view to the window and display.
    self.window.rootViewController = self.navigationController;
    [self.window makeKeyAndVisible];
    
    [self doServer];
    return YES;
}

- (void)dealloc
{
    [_window release];
    [_navigationController release];
    
    CFSocketInvalidate(_socket);
    CFRelease(_socket);
    _socket = nil;
    [super dealloc];
}

トラックバック(0)

トラックバックURL: http://foursics.jp/cgi-bin/mt/mt-tb.cgi/341

コメントする

OpenID対応しています OpenIDについて

このブログ記事について

このページは、Hironobu Kouraが2011年5月17日 01:08に書いたブログ記事です。

ひとつ前のブログ記事は「もっとCore Foundation / サーバソケットとCFStreamと私」です。

次のブログ記事は「Core Foundationを知る / CFHostでエラーハンドリング」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。