« もっとCore Foundation / CFStreamこそがtoll-freeだよ | ホーム | Core Foundation Hacks / iPhoneでも同じように動くのか? »

2011年5月16日

もっとCore Foundation / サーバソケットとCFStreamと私

で。

CFStream APIについて触れたかったのはこっち。CFStreamはネットワーク部分はいわゆるTCP/IPソケットと同じなので、connectとacceptによって通信路が確立される。

前のエントリで触れた内容ではクライアント側の実装、すなわちconnect側だったわけだが、もちろんサーバソケット(accept側)の実装もできる。ちょっと手間はかかるけれどもこんな感じ。

static void
_server_socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
    CFSocketNativeHandle handle = *(CFSocketNativeHandle*)data;
    
    NSLog(@"accepted. (s = %p, handle = %d)", s, handle);
    
    CFReadStreamRef readStream = NULL;
    CFWriteStreamRef writeStream = NULL;
    
    CFStreamCreatePairWithSocket(kCFAllocatorDefault, handle, &readStream, &writeStream);
    
    if (_setup_read_stream(readStream) != 0) {
        CFRelease(readStream);
        readStream = NULL;
    }
    if (_setup_write_stream(writeStream) != 0) {
        CFRelease(writeStream);
        writeStream = NULL;
    }
    
    NSLog(@"readStream = %p, writeStream = %p", readStream, writeStream);
}

static void
_do_server()
{
    CFSocketContext context = { 0, NULL, NULL, NULL, NULL };
    CFSocketRef socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, 0, kCFSocketAcceptCallBack, _server_socket_callback, &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);
}
  1. CFSocketCreate()でCFSocketオブジェクトを生成し、
  2. CFSocketSetAddress()でバインディングアドレスを指定、
  3. CFSocketCreateRunLoopSource()およびCFRunLoopAddSource()でRun LoopにCFSocketオブジェクトを登録、
  4. 最初のCFSocketCreate()で登録したコールバック(_server_socket_callback)にacceptされたソケットディスクリプタ(=CFSocketNativeHandle)が渡り、
  5. 4.のソケットディスクリプタからCFStreamCreatePairWithSocket()より、CFReadStream/CFWriteStreamを生成。

これ以降は前エントリの扱いと同じ。

んで。

このパターンで生成されたCFReadStream/CFWriteStreamは、CFReadStreamClose()/CFWriteStreamClose()してもTCP接続は閉じないという現象を確認してしまいました。これそういうもん?と色々調べたところ、CFReadStream/CFWriteStreamオブジェクトのプロパティkCFStreamPropertyShouldCloseNativeSocketをTRUE(kCFBooleanTrue)にするとちゃんと閉じてくれるようになった。

static void
_server_socket_callback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address, const void* data, void* info)
{
    switch (type) {
        case kCFSocketAcceptCallBack: {
            CFSocketNativeHandle handle = *(CFSocketNativeHandle*)data;
            
            NSLog(@"accepted. (s = %p, handle=%d)", s, handle);
            
            CFReadStreamRef readStream = NULL;
            CFWriteStreamRef writeStream = NULL;
            
            CFStreamCreatePairWithSocket(kCFAllocatorDefault, handle, &readStream, &writeStream);
            
            CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
            
            if (_setup_read_stream(readStream) != 0) {
                CFRelease(readStream);
                readStream = NULL;
            }
            if (_setup_write_stream(writeStream) != 0) {
                CFRelease(writeStream);
                writeStream = NULL;
            }

            NSLog(@"readStream = %p, writeStream = %p", readStream, writeStream);
        }
            break;
    }
}

トラックバック(0)

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

コメントする

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

このブログ記事について

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

ひとつ前のブログ記事は「もっとCore Foundation / CFStreamこそがtoll-freeだよ」です。

次のブログ記事は「Core Foundation Hacks / iPhoneでも同じように動くのか?」です。

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