CodeZine(コードジン)

特集ページ一覧

iPhoneネットワークプログラミング

TCP/IPを使用してサーバと通信する方法

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2010/03/10 14:00

 外の世界と通信する方法を会得すると、非常に面白いアプリを作ることができます。本稿では、iPhoneプログラミングにおいて、TCP/IPによって他のサーバーと通信する方法や、簡単なチャットアプリの作り方を学びます。

目次

はじめに

 iPhoneプログラミングに関する筆者の前回の記事では、iPhoneアプリケーション内からWebサービスを利用(consume)し、そこから返されるXMLデータを解析する方法について考察しました。Webサービスは今とても流行っているものの、Webサービスを利用するために必要とされるペイロードはかなり大きく、わずかなデータを取り出したいだけの場合は無駄が多いように感じられます。問題は、SOAPパケット自体が多くのバイト数を消費することです。そこで、これに代わる方法としてソケットを利用することを考えます。ソケットを使えば、余分なXMLペイロードなしに情報をやり取りすることが可能です。また、サーバとの接続が確立した状態を維持できるので、アプリケーションを非同期で動かして、送られてきたデータを必要なときだけ受け取るようなことも可能です。

 本稿では、TCP/IPを使用してサーバと通信する方法を学びます。また、筆者が以前書いた記事の中で述べたアイデアを使って、簡単なチャットアプリを作ります。

 本稿のサンプルプロジェクトでは、Xcodeを使用し、新規のView-based ApplicationプロジェクトをNetworkという名前で作成します。

ストリームによるネットワーク通信

 ネットワーク上でソケットを使用して通信するときはNSStreamクラスを使うのが簡単です。NSStreamはストリームを表す抽象クラスで、これを使ってデータを読み書きできます。このクラスはメモリ、ファイル、ネットワークに対して有効です。NSStreamクラスを使うと、NSStreamオブジェクトに対してデータを読み書きするだけでサーバと通信できます。

 Mac OS Xでサーバとの接続を確立するには、NSHostオブジェクトとNSStreamオブジェクトを次のように使います。

NSInputStream *iStream;
NSOutputStream *oStream;
uint portNo = 500;

NSURL *website = [NSURL URLWithString:urlStr];
NSHost *host = [NSHost hostWithName:[website host]];
[NSStream getStreamsToHost:host 
                      port:portNo 
               inputStream:&iStream
              outputStream:&oStream];

 ご存じのように、NSStreamクラスにはgetStreamsToHost:port:inputStream:outputStream:というクラスメソッドがあり、これでサーバに対して入力ストリームと出力ストリームを作成してデータを読み書きできます。しかし、問題はgetStreamsToHost:port:inputStream:outputStream:メソッドがiPhone OSでサポートされていないことです。そのため、上記のコードはiPhoneアプリでは動きません。

 この問題は、既存のNSStreamクラスにカテゴリを追加してgetStreamsToHost:port:inputStream:outputStream:メソッドによる機能を置き換えれば解決できます。具体的には、Xcode内でClassesグループを右クリックし、新規のファイルをNSStreamAdditions.mという名前で追加し、NSStreamAdditions.hファイルに次のコードを追加します。

#import 

@interface NSStream (MyAdditions)

+ (void)getStreamsToHostNamed:(NSString *)hostName 
                         port:(NSInteger)port 
                  inputStream:(NSInputStream **)inputStreamPtr 
                 outputStream:(NSOutputStream **)outputStreamPtr;

@end

 また、NSStreamAdditions.mファイルに、リスト1のコードを追加します。

 上のコードにより、getStreamsToHostNamed:port:inputStream:outputStream:というクラスメソッドがNSStreamクラスに追加され、iPhoneアプリからこのメソッドを使ってサーバとのTCP接続を行えるようになります。

※筆者注

 ここに示したカテゴリのコードは、AppleのTechnical Q&A1652を手本にしています。

リスト1
#import "NSStreamAdditions.h"

@implementation NSStream (MyAdditions)

+ (void)getStreamsToHostNamed:(NSString *)hostName 
                         port:(NSInteger)port 
                  inputStream:(NSInputStream **)inputStreamPtr 
                 outputStream:(NSOutputStream **)outputStreamPtr
{
    CFReadStreamRef     readStream;
    CFWriteStreamRef    writeStream;
    
    assert(hostName != nil);
    assert( (port > 0) && (port < 65536) );
    assert( (inputStreamPtr != NULL) || (outputStreamPtr != NULL) );
    
    readStream = NULL;
    writeStream = NULL;
    
    CFStreamCreatePairWithSocketToHost(
                                       NULL, 
                                       (CFStringRef) hostName, 
                                       port, 
                                       ((inputStreamPtr  != nil) ? &readStream : NULL),
                                       ((outputStreamPtr != nil) ? &writeStream : NULL)
                                       );
    
    if (inputStreamPtr != NULL) {
        *inputStreamPtr  = [NSMakeCollectable(readStream) autorelease];
    }
    if (outputStreamPtr != NULL) {
        *outputStreamPtr = [NSMakeCollectable(writeStream) autorelease];
    }
}

@end

 NetworkViewController.mファイルに、以下のステートメントを挿入します。

#import "NetworkViewController.h"

#import "NSStreamAdditions.h"

@implementation NetworkViewController

NSMutableData *data;

NSInputStream *iStream;
NSOutputStream *oStream;

 connectToServerUsingStream:portNo:メソッドを定義します。これでサーバに接続して、入力ストリームと出力ストリームのオブジェクトを作成できます。

-(void) connectToServerUsingStream:(NSString *)urlStr 
                            portNo: (uint) portNo {

    if (![urlStr isEqualToString:@""]) {
        NSURL *website = [NSURL URLWithString:urlStr];
        if (!website) {
            NSLog(@"%@ is not a valid URL");
            return;
        } else {
            [NSStream getStreamsToHostNamed:urlStr 
                                       port:portNo 
                                inputStream:&iStream
                               outputStream:&oStream];            
            [iStream retain];
            [oStream retain];
            
            [iStream setDelegate:self];
            [oStream setDelegate:self];
            
            [iStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                               forMode:NSDefaultRunLoopMode];
            [oStream scheduleInRunLoop:[NSRunLoop currentRunLoop]
                               forMode:NSDefaultRunLoopMode];
            
            [oStream open];
            [iStream open];            
        }
}    

}

 入力ストリームと出力ストリームの両方が、実行ループ上でイベントを受け取るようにスケジュールしてあります。これで、ストリームにデータがなくなってもコードが中断されなくなります。また、両方のストリームオブジェクトのデリゲート(delegate)をselfに設定しているのは、ストリーム上のデータを受け取るメソッドをこの同じクラスに実装するからです。


  • LINEで送る
  • このエントリーをはてなブックマークに追加

あなたにオススメ

著者プロフィール

  • japan.internet.com(ジャパンインターネットコム)

    japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.com や EarthWeb.c...

  • Wei-Meng Lee(Wei-Meng Lee)

    Microsoft MVP受賞者。Microsoft社の最新テクノロジー実地研修を専門とするDeveloper Learning Solutions社を創設。.NETとワイヤレステクノロジーの開発者、指導者として知られる。国際的なカンファレンスでたびたび講演し、.NET、XML、ワイヤレステクノロジ...

バックナンバー

連載:japan.internet.com翻訳記事

もっと読む

All contents copyright © 2005-2021 Shoeisha Co., Ltd. All rights reserved. ver.1.5