作成日

28 September 2010

その他の要件

  • Flex SDK 4.1
  • Adobe AIR SDK 2.0
  • Flash Builder 4
2010年6月9日にAdobe AIR 2がリリースされました。約2年半振りのメジャーバージョンアップとなり、多くの新機能追加と機能強化が行われています。本連載(全9回予定)では、 それら機能について詳しく説明するとともに、サンプルアプリを用いながらその使い方を解説していきます。

第1回 Adobe AIR 2をはじめよう
第2回 サーバーソケット/ データグラムソケットなどのネットワーク系機能
第3回 Safari 4.0.3 と同等の WebKit を搭載
第4回 プリンタ情報の取得や印刷ダイアログの制御
第5回 OSネイティブな機能との連携
第6回 OSネイティブとの統合
第7回 グローバルエラーハンドラー/IMEテキスト入力の機能強化

本記事では、Adobe AIR 2から導入されたサーバーソケットやセキュアクライアントソケットなどのネットワーク系機能について、サンプルアプリケーションをもとに解説します。

本記事のサンプル:AIR2master02sample.zip

新機能について

Adobe AIR 2 では、以下のネットワーク周辺機能が新しく追加されました。

  • ネットワークインターフェイス情報の提供
  • データグラムソケット(UDP)
  • サーバーソケット
  • DNSクライアント
  • セキュアソケット(TLS/SSL)

本チュートリアルでは、EchoSampleとPop3MailCheckerの2つのサンプルアプリケーションをもとに、これらの新機能を解説していきます。ネットワークインターフェイス、データグラムソケット、サーバーソケットについてはEchoSampleにおいて、DNSクライアント、セキュアソケットについてはPop3MailCheckerにおいて実装方法を紹介します。

サンプルアプリケーションについて

EchoSample

EchoSampleはUDPとTCPのそれぞれでEchoプロトコルのクライアントとサーバーを実装したものです。サーバーのバインド先候補のピックアップにネットワークインターフェイス情報を使っています。UDPはクライアント・サーバー双方ともデータグラムソケットの機能を、TCPサーバーはサーバーソケットの機能を利用して実現しています。

Pop3MailChecker

Pop3MailCheckerはPOP3プロトコルでメールサーバーに接続認証し、サーバー上にあるメール数を取得して表示します。ホストチェックでは、DNSクライアントのルックアップ機能を使ってホストが実在するか確認しています。POP3 over SSLはセキュアソケットを使ってサーバーに接続することで実現しています。

ネットワークインターフェイス情報の取得

ホストのネットワークインターフェイスの情報がNetworkInfoクラスをルートとして取得できるようになっています。サーバー機能のバインド先のIPアドレスやNICのMACアドレスを取得できます。

以下は、EchoSampleにおけるサーバーのエンドポイントIPアドレスのリスト取得の処理です。

public static function getBindableAddresses():Array { var addresses:Array = new Array(); var netinfo:NetworkInfo = NetworkInfo.networkInfo; var netifs:Vector.<NetworkInterface> = netinfo.findInterfaces(); for each (var netif:NetworkInterface in netifs) { for each (var addr:InterfaceAddress in netif.addresses) { if (addr.ipVersion == IPVersion.IPV4) { addresses.push(addr.address); } } } return addresses; }

NetworkInfo.networkInfoで取得できるNetworkInfoインスタンスのfindInteracesメソッドを呼ぶことで、インターフェイスのリストをNetworkInterfaceクラスのベクターで取得できます。さらに各インターフェイスには多種のIPアドレスが割り当てられているため、InterfaceAddress クラスのipVersionプロパティからIPv4に限定して取得しています。

データグラムソケット

これまでのSocketクラスではTCPのクライアントソケットしか使えませんでしたが、新しいDatagramSocketクラスによってUDPを扱えるようになりました。こちらはクライアント、サーバーどちらにも対応しています。

データグラムソケットによる送信処理

EchoSampleのUDPクライアント(UdpClient)実装の抜粋から送信処理の方法をみていきましょう。

var _socket:DatagramSocket = new DatagramSocket(); _socket.addEventListener(DatagramSocketDataEvent.DATA, onData); _socket.send(data, 0, data.length, <送信先IPアドレス>, <送信先ポート>); _socket.receive();

送信にはsendメソッドを呼び出してByteArrayのdataを送信します。sendの引数で送信先のIPアドレスとポートを指定できます。TCPソケットのようにconnectメソッドを先に呼ぶ場合は、送信先の指定は不要です。送信先はあくまでもIPアドレスであり、Socketのconnectメソッドとは異なり、ホスト名は指定できないので注意してください。

なお、応答パケットを受信したいときはreceiveメソッドを呼んでおきます。受信は非同期で処理され、DatagramSocketDataEventのDATAイベントによりevent.dataで受信データを取得できます。

データグラムソケットによるサーバーの実装

EchoSampleのUDPサーバー(UdpServer)実装の抜粋からサーバー処理の方法をみていきましょう。

private var _svrsock:DatagramSocket; _svrsock = new DatagramSocket(); _svrsock.addEventListener(DatagramSocketDataEvent.DATA, onData); _svrsock.bind(<バインドするポート>, <バインドするIPアドレス>); _svrsock.receive();

DatagramSocketDataEventのDATAイベントのハンドル追加と、bindメソッドでバインド先のポートとIPアドレスを指定します。receiveメソッドを呼ぶと受信を開始します。

_svrsock.close();

サーバーの終了にはcloseメソッドを呼び出します。

サーバーソケット

TCPソケットにおいてもServerSocketによってサーバー機能の実装が可能になりました。EchoSampleのTCPサーバー(TcpServer)実装の抜粋からサーバー処理の方法を順にみていきましょう。

サーバー着信の開始

エンドポイントをバインドして着信を開始するには次のようにします。

private var _svrsock:ServerSocket; _svrsock = new ServerSocket(); _svrsock.addEventListener(ServerSocketConnectEvent.CONNECT, onConnect); _svrsock.bind(<バインドするポート>, <バインドするIPアドレス>); _svrsock.listen();

UDPとは異なり、TCPはコネクション型の通信になるので、いきなりデータではなく、接続ごとのソケットを処理します。そのため、ServerSocketConnectEventのCONNECTイベントをハンドルすることでクライアントからの接続を検出します。bindメソッドでバインド先を指定しlistenメソッドで着信を開始します。

クライアントソケット接続時の処理

ServerSocketConnectEventのCONNECTイベントを使えば、接続してきたクライアントソケットを取得できます。

private function onConnect(event:ServerSocketConnectEvent):void { var socket:Socket = event.socket; socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData); socket.addEventListener(Event.CLOSE, onSocketClose); _connectedSockets.push(socket); }

ProgressEventのSOCKET_DATAイベントでソケットがデータを受信したときの処理を、CLOSEイベントでソケットが閉じられたときの処理をハンドルします。マルチコネクションに対応するため、これらのソケットはコレクション(サンプルでは_connectedSockets)に保持しておき、サーバー停止時にまとめて閉じた方がよいでしょう。

各ソケットのデータ受信時の処理

private function onSocketData(event:ProgressEvent):void { var socket:Socket = Socket(event.target); var data:ByteArray = new ByteArray(); socket.readBytes(data, 0, socket.bytesAvailable); … 省略 …

socketのbytesAvailableプロパティで受信されたサイズがわかるので、その分だけソケットからバイトデータを取得します。

ソケットが閉じられた時の処理

private function onSocketClose(event:Event):void { var socket:Socket = Socket(event.target); … 省略 … _connectedSockets.removeItemAt(_connectedSockets.getItemIndex(socket)); }

接続しているソケットのコレクションから閉じられたものを取り除きます。

サーバーの停止

for each (var socket:Socket in _connectedSockets) { socket.close(); } _svrsock.close();

サーバーを終了するには残った接続ソケットとサーバーソケットのcloseメソッドを呼び出します。

DNSクライアント

前述の通りデータグラムソケットのをサポートしたことで、DNSサーバーへのクエリも簡単に行えるようになりました。ユーザーが入力したホスト名から事前にIPアドレスが引けるかどうか確かめて入力チェックの実装に使うこともできます。

Pop3MailCheckerのホストチェックボタン押下時の処理をみてみましょう。

var resolver:DNSResolver = new DNSResolver(); resolver.addEventListener(ErrorEvent.ERROR, resolver_error); resolver.addEventListener(DNSResolverEvent.LOOKUP, resolver_lookup); resolver.lookup(<ホスト名>, hostTextInput.text, ARecord); private function resolver_lookup(event:DNSResolverEvent):void { var address:String; for each (var record:ResourceRecord in event.resourceRecords) { if (record is ARecord) { // DNSIPv4アドレスの取得 address = ARecord(record).address; } } … 省略 … private function resolver_error(event:ErrorEvent):void { // DNSエラー }

DNSResolverのインスタンスに対してErrorEventのERRORイベントでエラー処理を、DNSResolverEventのLOOKUPイベントでDNSクエリの成功時の処理をハンドルします。lookupメソッドでクエリを発行します。第2引数にレコードタイプをクラスで指定します(表1を参考)。DNSResolverEventのresourceRecordsプロパティにはクエリ結果としてResouceRecordが配列に格納されています。lookupメソッドで指定した検索レコードを走査で探して処理します。

表1

レコードクラス 用途
AAAARecord IPv6正引き用レコード
ARecord IPv4正引き用レコード
MXRecord メールエクスチェンジ用レコード
PTRRecord 逆引き用レコード
SRVRecord サービスロケーション用レコード

セキュアソケット

SSLv3およびTLSv1プロトコルに対応した暗号化通信用クライアントソケットをサポートしています。この機能はSecureSocket クラスで提供されます。なお、自作したSSL証明書など、サーバー側の証明書に問題がある場合は接続時にエラーとなります。

セキュアソケットは全てのプラットフォームでサポートされるわけではないので、SecureSocket.isSupported(静的プロパティ)で適宜確認する必要があります。
SecureSocketはSocketを継承しているため、既にあるクライアントプログラムをSSL対応させるにはインスタンスの切り替えだけでできます。

var socket:Socket = new SecureSocket();

Pop3MailCheckerサンプルではPOP3とPOP3 over SSLとプロトコルによってソケットを切り替えています。

サーバー側の証明書に問題発生時はIOErrorEventのIO_ERRORイベントで処理します。

private function socket_ioError(event:IOErrorEvent):void { if (event.target is SecureSocket) { var status:String = SecureSocket(event.target).serverCertificateStatus; … 省略 …

SecureSocketのserverCertificateStatusプロパティがCertificateStatusクラスの定数値のいずれかによって証明書エラーの詳細を判別できます。

おわりに

Adobe AIR 2から導入されたネットワーク系機能について解説しました。特にソケットのサーバー機能が提供されたことで、P2Pやクライアント/サーバー型などといった様々なネットワークアプリケーションをAdobe AIRのみで作成することが可能になりました。Adobe AIRの強みであるマルチプラットフォームと合わさって、WindowsでもMacでも動く強力なソフトの登場が期待できるでしょう。