カテゴリー別アーカイブ: IO・ネットワーク

IPv6

※v8.0からサポートされました。

通信プロトコル IPv6 がサポートされました。

 ソース

{curl 8.0 applet}
{curl-file-attributes character-encoding = “utf8”}

{import * from CURL.IO.SOCKET}

{let v4:IPv4Address = {IPv4Address 0xC0A80101}} ||192.168.1.1
{let v6:IPv6Address = {IPv6Address.from-SocketIPv4Address 0xC0A80101}}
{VBox
    {v4.to-String},
    {v6.to-String}
}

 

実行結果v6.jpg

 

 

HTTPOnly Cookie のサポート

 ※v8.0からサポートされました。

Microsoft Windows® においてHttpOnly属性を持つCookieをサポートしています。 特権アプレットはHttpOnly属性を持つCookieにアクセスすることが出来ます。

 

{curl 8.0 applet}
{curl-file-attributes character-encoding = “utf8”}

{let message:VBox={VBox}}
{let cookies:{Array-of HttpCookie} =  {get-http-cookies {url “http://localhost:8888/“} , include-http-only? = true} }
{do
    {for v:HttpCookie in cookies do
        {message.add {v.to-String}}
    }
}
{value
    message
}

include-http-only?: このパラメータが true で特権アプレットの場合は、 HttpOnly Cookie を含む全ての Cookie が返されます。このパラメータが true で、非特権アプレットの場合は、セキュリティ例外がスローされます。

 

 

オブジェクトのシリアル化

オブジェクトのシリアル化(直列化)とは,オブジェクトの内部状態をバイトストリームに変換し,そのバイトストリームから再び元と同じ内容を持ったオブジェクトを再現することができる機能のことです。ただし、すべてのデータクラスをシリアル化できるのではなく、serializableとして宣言する必要があります。このような機能はJavaやActionScript(AMF0、AMF3)にもあります。

早速使い方を説明していきます。まずは、CURL.IO.SERIALIZEパッケージをインポートします。

{import * from CURL.IO.SERIALIZE}

次に対象となるデータクラスを宣言します。このクラスはserializableとして宣言します。 

{define-class public serializable AAA
  field name:#String
  field value:#String
 
  {constructor {default name:#String = null, value:#String = null}
    set self.name = name
    set self.value = value
  }
  {method public {getNameAndValue}:#String
    {return self.name & ” ” & self.value}
  }
}

シリアライズするには、SerializeOutputStreamのwrite-oneメソッドを利用します。以下の例では、ファイル(test.byte)にシリアライズしたデータ(AAA)を保存しています。 

{with-open-streams
    out:SerializeOutputStream = {SerializeOutputStream 
                                    {write-open-byte {url “test.byte”}}}
 do
    {out.write-one {AAA name = “OKADA”, value = “123”}}
}

逆にデシリアライズ(バイトストリームから元のオブジェクトを生成)するには、deserializeプロシージャを利用します。ここでは上記で作成したファイルを読み込み、オブジェクトを生成しています。 

{with-open-streams
    in:SerializeInputStream = {SerializeInputStream 
                                  {read-open-byte {url “test.byte”}}}
 do
    let read-obj:AAA = {deserialize in, AAA}
    {dump {read-obj.getNameAndValue}}
}

実行結果

{read-obj.getNameAndValue}=OKADA 123

 

圧縮・解凍

Curlで圧縮及び解凍する方法を記載します。Curlでは圧縮フォーマットにgzipとzlibをサポートしております。これを利用するには、CURL.IO.ZSTREAMパッケージをインポートします。

{import * from CURL.IO.ZSTREAM}

この中のDeflateByteOutputStreamにて圧縮ストリームを生成し、InflateByteInputStreamにて解凍ストリームを生成することができます。以下に圧縮ファイルへの書き出しのサンプルコードを記載します。 

{define-proc {compress
                 in-url:Url,
                 out-url:Url,
                 compression-format:CompressionFormat =
                     CompressionFormat.gzip || or zlib
             }:void
   
    {with-open-streams
        bos = {DeflateByteOutputStream
                  {write-open-byte out-url},
                  compression-format = compression-format}
     do
        {with-open-streams
            bis = {read-open-byte in-url}
         do
            {while true do
                let (data:{Array-of byte}, num-read:int) = {bis.read n = 1024}
                {if num-read <= 0 then
                    {break}
                }
                let start:int = 0
                {while num-read > 0 do
                    let written:int =
                        {bos.write
                            data,
                            start = start,
                            n = num-read
                        }
                    {if written <= 0 then
                        {break}
                    }
                    {inc start, written}
                    {dec num-read, written}
                }
            }
        }
    }
}

上記のサンプルプロシージャを用いて、以下のようにテキストファイルを圧縮し、圧縮ファイルに書き出すことができます。

{compress {url “a.txt”}, {url “a.gz”}}

また、上記で圧縮したファイルを以下のサンプルプロシージャで解凍します。

{define-proc {uncompress
                 in-url:Url,
                 out-url:Url,
                 compression-format:CompressionFormat =
                     CompressionFormat.gzip || or zlib
             }:void

    {with-open-streams
        bis = {InflateByteInputStream
                  {read-open-byte in-url},
                  compression-format = compression-format
              }
     do
        {with-open-streams
            bos = {write-open-byte out-url}
         do
            {while true do
                let (data:{Array-of byte}, num-read:int) = {bis.read n = 1024}
                {if num-read <= 0 then
                    {break}
                }
                {if-non-null data then
                    {bos.write {non-null data}, n = num-read}
                }
            }
        }
    }
}

以下のようにサンプルプロシージャを実行し、解凍ファイルを取得します。 

{uncompress {url “a.gz”}, {url “ab.txt”}}

これらにより、HTTP圧縮・解凍の仕組みを実装することも可能です。

サンプルコード

圧縮・解凍サンプルコード

 

コンテンツの読込み・書き込み

外部リソースについて

Curlアプリケーションでは外部リソースから情報を取得したり外部の送信先に情報を送信する必要があるケースがほどんとです。一般的に以下のような場所でデータがあります。

  • ファイル
  • ディスク
  • ネットワーク上
  • メモリ
  • 別のプログラムあるいはプロセス

Curlはありとあらゆるファイル(テキスト、バイナリ、イメージ)・Webページなどのリソースに対しデータの読込・書込を行うことができます。
ファイルシステム内にある場合はファイルの削除や名前の変更など、一部データリソースを操作することができ
ます。

 

テキストファイルの読込

ほとんど多くの場合データはテキスト形式で保持されてます。
テキストファイルを読み取るにはTextInputStreamオブジェクトを作成する必要があります。TextInputStreamオブジェクトはリソースから一連のデータを取り表すストリームクラスの1つです。ちなみにローカルディスク上のファイルの読み取りと、ネットワークまたは他のソース上のデータのストリームからの読み取りは同じです。ただし、ネットワークリソースと対話(Httpのpostメソッドやgetメソッドなどを使用して)する場合はCurlからリクエストデータおよび返却データを受け渡しする必要があるので少し変わってきます。そちらについては「逆引きリファレンス-サーバー連携」を参照してください。
以下にネットワーク上(あるいはローカルファイルシステム)のテキストファイルを読み込むサンプルをご紹介します。
 

{curl 5.0, 6.0 applet}

{pre {read-from {url “business.txt”}}}

上記のサンプルはread-fromプロシージャを使用したサンプルで、read-fromプロシージャは第一引数のUrlを指定するだけでそのコンテンツを文字列として読込み、StringBufという文字列のオブジェクトを返却します。
上記の例ではpreマクロを使用して読込み結果を表示しています。

 

テキストファイルの一行づつの読み取り

read-fromプロシージャはコンテンツのすべての内容を文字列オブジェクト(StringBuf)に読込みますが、コンテンツがCSVデータのような場合は1行づつ読み込んで処理することが多いと思います。
以下に1行づつ読み込むサンプルを紹介します。

{curl 5.0, 6.0 applet}

{value
   let lines:StringArray = {StringArray}
  
   let location:Url = {url “business.txt”}

   {with-open-streams in = {read-open location} do
       {until in.end-of-stream? do
           {lines.append {{in.read-one-line}.to-String}}
       }
   }

   let display:VBox =  
       {VBox
           background = “beige”,
           border-style = “sunken”,
           border-width = 2pt,
           border-color = “navy”,
           margin = 4pt
       }
   {for line key k in lines do
       {if k==0 then
           {display.add {bold {underline {value line}}}}
        else
           {display.add line}
       }
   }
  
   display
}

上記のサンプルは少し複雑ですが以下にポイントを説明します。

  1. 文字列の配列を作る(これはコンテンツから読み込んだ1行づつの内容を保持するためです。)
  2.  read-openプロシージャを使用してTextInputStreamオブジェクトを生成する。
  3. TextInputStreamクラスのread-one-lineメソッドを使用して一行ずつ読込み、最終行(EOF)まで1の文字列の配列に格納していく。

上記のステップでシーケンシャルファイルを一行ずつ読み込むことができます。

 

イメージの読込み

イメージを簡単に読み込むにはimageテキストプロシージャを使用する方法があります。
以下にImageテキストプロシージャを使用したサンプルをご紹介します。

{image
    source = {url “sample-image.gif”},
||–    下の幅(width)と高さ(height)を設定すると指定したサイズになります。
||–    指定してない場合はイメージのサイズが適用されます。
||–    width = 500pixel,
||–    height = 500pixel,
    tooltip = {Tooltip “画像の説明を記述したりします。”}
}
サポートしているイメージファイル形式は以下の通りです。
  • GIF
  • JPG
  • BMP 4、8、24、または32 ビット深度
  • PPM
  • PNG 8 ビット深度の透過度をサポート

TIFF 複数イメージのファイルの最初のイメージのみ。なお、追加のイメージにアクセスするには、ImageDataクラスを使用する必要があります。

 

書込について

書込みの手順も基本的に読込と同じになります。使用するクラスはTextOutputStreamクラスを使用します。
詳しくは「Curl開発者ガイド→外部リソースとの対話→ファイルやその他のリソースへのアクセス→テキストファイルの読み取りと書き込み →テキストファイルへの書き込み」を参照してください。

  

その他の形式へのアクセスおよび方法

上記で紹介したテキストおよびイメージデータ以外にも様々なデータにアクセスすることができます。例えばバイナリデータや圧縮データです。
また同期読み取りだけでなくAjaxなどで注目された非同期読み取りももちろん可能です。

 

サンプルコードのダウンロード

データの読込みサンプル

 

関連ヘルプドキュメント

パーシスタントデータ

Web アプリケーションでは、クライアント マシンに少量のデータを格納するために HTTP cookie を使用して、ユーザー名またはID、セッション IDなどの設定情報を格納しています。Curl アプレットもHTTP cookie を使用できます。さらにCurl API ではクライアント側 パーシスタントデータという小さなサイズのデータを格納する機能を提供しています。

では、具体的にサンプルを見ていきましょう。

プライベートパーシスタントデータ

これは、1つのアプレットのみからアクセス可能なパーシスタントデータです。これらを扱うためにまずは管理のためのリポジトリをpersistent-dataというプロシージャにより作成する必要があります。この際に有効期限等を設定することもできます。

{persistent-data
    “my persistent data.”,
    duration = 90days,
    max-size = 1024
}

リポジトリ作成後、以下のようにパーシスタントデータを取得(set-persistent-data)やセット(get-persistent-dataプロシージャ)することができます。

    let date:#String = {get-persistent-data
                           “last-visit”,
                           error-if-missing? = false}
    {output date}
    || set persistent data and commit.
    {set-persistent-data “last-visit”, {format “%s”, {DateTime}}}
    {commit-persistent-data} 

扱える型としては、null、プリミティブ形、String、FastArray-of、Array-of、HashTable-ofなどがあります。(詳細はCurlヘルプを参照ください。)set-persistent-data後、値をコミットするためにcommit-persistent-dataプロシージャを実行する必要があります。注意事項として、アプレットは最大で20までのリポジトリと256Kまでのデータしか扱うことができません。

共有パーシスタントデータ

パーシスタントデータ識別子(PDI)ファイルの persistent-data を呼び出すことで、複数のアプレットが共有できるパーシスタント データ リポジトリを作成することができます。これを利用するには、共有のscurlを作成し、その中でpersistent-dataプロシージャを呼び出す必要があります。

|| create repository
{persistent-data
    “shared persistent data”,
    duration = 90days,
    max-size = 1024

一方、各アプレットでは、パーシスタントデータを利用する前にget-persistent-data-repositoryプロシージャを用いてリポジトリ(Repositoryオブジェクト)を取得する必要があります。この際、上記の共有scurlのパスを指定する必要があります。
その後、プライベートと同様にget-persistent-data/set-persistent-dataを用いてパーシスタントデータを上記で取得したRepositoryオブジェクトを指定して扱うことができます。 

{let repo:Repository =
    {get-persistent-data-repository
        pdi-name = “http://localhost/share.scurl
    }
}
{do
    let date:#String =
        {get-persistent-data
            “last-visit”,
            repository = repo,
            error-if-missing? = false}
   
    {output date}
   
    || set persistent data and commit.
    {set-persistent-data “last-visit”,
        {format “%s”, {DateTime}},
        repository = repo
    }

サンプルコードのダウンロード

パーシスタントデータのサンプル

 

ソケット通信

ソケットとは通信を行うアプリケーションの仮想的なインターフェースのことです。サーバは指定したポート番号でクライアントからの接続を待ち、クライアントはIPアドレスもしくはホスト名とポート番号を指定してサーバに接続します。接続が成功するとサーバ、クライアントともにソケットが生成され、そのソケットに対してデータ送受信を行うことができます。Curlでは、ソケットサーバ及びクライアントを実装することができます。

TCPサーバ

以下は、TCPサーバのコードサンプルです。

{let socket-server:AcceptorTCPSocket = {AcceptorTCPSocket}}
{server-socket.bind
    local-port = port,
    acceptable-handler =
        {on e:AcceptableSocketEvent do
            def socket = {server-socket.accept}
            {try 
                || receive
                {with-open-streams 
                    input-stream = {socket.to-InputStream} 
                 do
                    {print {input-stream.read allow-short-read? = true}}

                    || send 
                    {with-open-streams 
                        output-stream = {socket.to-OutputStream} 
                     do
                        {output-stream.write {make-bytes “back!”}}
                    }
                }
              finally
                {socket.close}
             }
       }

AcceptorTCPSocketクラスをnewすることによりサーバソケットを生成し、bindメソッドを実行することで、ローカルアドレスの指定したポートにバインドします。ここでbindメソッドの引数としてクライアントから受信した際に発生させるイベントAcceptableSocketEventをacceptable-handler引数にセットします。ここでは、受信した場合にクライアントから送信されたストリームデータを文字列として表示し、結果をOutputStreamとして返しています。

TCPクライアント

以下は、TCPクライアントのコードサンプルです。

let socket:DataTCPSocket =
    {DataTCPSocket remote-port = port, remote-name = hostname}
{try
    {socket.connect}
    || send
    {with-open-streams 
        output-stream = {socket.to-OutputStream} 
     do
        {output-stream.write {make-bytes “send!”}}

        || receive
        {with-open-streams
            input-stream = {socket.to-InputStream} 
         do
            {print {input-stream.read allow-short-read? = true}}
        }
    }
 finally
    {socket.close}
}

DataTCPSocketクラスを用いてクライアントソケットを生成し、connectメソッドにてサーバに接続します。サーバにデータを送信するには、ソケットのto-OutputStreamメソッドでOutputStreamを生成し、データを書き込みます(writeメソッド)。また、サンプルでは、サーバからのレスポンスをソケットのto-InputStreamメソッドによりInputStreamを受け取り、コンソールに出力しています。ソケットを閉じるにはcloseメソッドを利用します。

また、CurlではUDPサーバ及びクライアントも取り扱うことができますし、非同期通信も可能となっています。詳細はCurlのヘルプをご確認ください。

コードサンプルのダウンロード

ソケット通信のサンプル

 

非同期通信と同期通信

Ajaxは(Asynchronous JavasScript + XML)という名前の通り、XMLHttpRequestというクラスを利用しAsynchronous(非同期通信)を実現できるという点が取り上げられ流行しています。もちろんCurlでも非同期通信を実現できるので、実装方法を紹介していきたいと思います。

同期通信

以下に、サーバへアクセスし、応答結果をアプレット上に表示するというサンプルコードを紹介していきます。

{curl 6.0 applet}

{let out:Frame = {Frame}} || 表示用
{value
    def remote-url = {url “http://www.curlap.com“}
    def http-file = {remote-url.instantiate-File} asa HttpFile
    {with-open-streams tis:HttpTextInputStream =
        {http-file.http-read-open} || 同期通信
      do
        let buf:StringBuf = {StringBuf}
        {tis.read-one-string buf = buf}
        {out.add {buf.to-String}}
    }
    out
}

サーバのURLからHttpFileオブジェクトを生成します。そのHttpFileオブジェクトのread-openメソッドを利用しサーバへリクエストします。サーバが返した結果(ここではHTML)をアプレットに表示しています。

非同期通信

非同期通信のサンプルコードも同様に結果をアプレットに表示します。非同期ではリクエストした結果を受け取った際に実行するためのコールバックプロシージャを用意します。以下のサンプルでは、callback-procプロシージャを用意し、非同期通信処理の終了後にこのプロシージャに結果を引数として渡して実行されるようにしています。

{curl 6.0 applet}

{let out:Frame = {Frame}}
{value
    let callback-proc:{proc-type {#Exception, #String}:void}
        = {proc {ex:#Exception, value:#String}:void
    {if-non-null ex then
        {output ex.message}
        {return}
    }
    {out.add value}
}

def remote-url = {url “http://www.curlap.com“}
def http-file = {remote-url.instantiate-File} asa HttpFile

let worker:AsyncWorker =
    {http-file.http-async-read-open
        {on e:AsyncFileOpenEvent do
            {if-non-null ex = e.exception then
                {callback-proc ex, null}
                {return}
             else
                {if-non-null tis = e.stream asa #TextInputStream then
                     let buf:StringBuf = {StringBuf}
                     set worker =
                          {tis.async-read-string
                              buf = buf,
                              {on e:AsyncStreamReadEvent do
                                  {callback-proc null, {buf.to-String}}
                                  {tis.close}
                              }
                          }
                }
            }
        }
    }

    out
}

HttpFileオブジェクトを生成するところまでは同期通信と同様ですが、非同期通信ではHttpFileオブジェクトのasyn-read-openメソッドを利用し、サーバにリクエストします。非同期通信の場合は結果を受け取るとAsyncFileOpenEventが発生し、このイベントのstreamゲッターから結果のストリームデータを取得することとなります。また、非同期処理は例外をキャッチできないため、例外もAsyncFileOpenEventのexceptionゲッターから取得します。さらにストリームデータを非同期に読み込むために、TextInputStreamにはasync-read-stringメソッドが用意されています。

コードサンプルのダウンロード

通信のコードサンプル

 

ローカルファイルアクセス

ブラウザ上のWebアプリケーションではローカルファイルへのアクセスが制限されていますが、Curlでは簡単にアクセスすることができます。(但し、セキュリティ面を考慮しており、特権の設定が必要となります。)

では、具体的にサンプルを見ていきましょう。

ファイルの読み込み

read-fromプロシージャを利用し、簡単にファイルを読み込むことができます。

def str = {read-from {url “test1.txt”}}

ファイルの書き込み

write-toプロシージャを利用し、簡単にファイルへ書き込みを行うことができます。

{write-to {url “test1.txt”}, “書き込み成功!”}

ファイルのコピー・名前変更

以下のサンプルコードのようにコピーや名前変更、削除も1行で記述することができます。

{copy {url “test1.txt”}, {url “test2.txt”}, error-if-exists? = false}

|| rename
{rename {url “test2.txt”}, {url “test3.txt”}}

|| delete
{delete {url “test3.txt”}}

ディレクトリ作成

ディレクトリの作成も以下のように簡単にできます。

let dir:Directory =
{create-Directory {url “dir1”}, error-if-exists? = false}

コードサンプルのダウンロード

ローカルファイルアクセスのコードサンプル