Curl 言語では、TCP ソケットと UDP ソケットを使用するための API を提供しています。両方のソケットはアドレス/ポート のペアを使用してソケットの位置を独自に識別します。サーバーと交信するクライアントは、サーバーが情報を受けるアドレスとポートを知っている必要があります。Curl のソケット API は与えられたホスト名のアドレスを検索します。
Curl のAPI ではクライアントの TCP ソケット接続にセキュリティ制限を課します。「
ネットワーク アクセスの制限」を参照してください。
各オペレーションに対して同期のソケット呼び出しができますが、これはテストのみの使用にしてください。ソケット呼び出しがサーバーの応答を待っている間、アプレットの UI が無反応だからです。このセクションの順を追った説明では、非同期の呼び出しだけを説明しています。これらの呼び出しは、操作が完了、失敗、時間切れあるいはキャンセルされた時にイベント ループから
EventHandler を呼び出すイベントを送ります。非同期の操作は、各非同期メソッドから返された
AsyncWorker の
AsyncWorker.cancel を呼び出すことによってキャンセルすることができます。
IDE のドキュメントでは、TCP および UDP ソケットのための Curl の API を説明する幅広いサンプルを提供しています。サンプルの使用に関する詳細は、「
内容の充実したサンプル ファイル」を参照してください。
サンプルは次のファイル内にあります。
d:\automated-build-temp\build\win32-atom\docs\default\examples\dguide\sockets.zip
このアーカイブ ファイルには次のアプレット ファイルが含まれています。
- socket-http-client.curl は「TCP ソケット クライアント」で述べられている TCP ソケット クライアントについて説明しています。
- socket-http-server.curl は「TCP ソケット サーバー」で述べられている TCP ソケット サーバーについて説明しています。
- udp-client.curl は「UDP ソケット」で述べられている UDP ソケット クライアントについて説明しています。
- udp-server.curl は「UDP ソケット」で述べられている UDP ソケット サーバーについて説明しています。
これらのサンプルは特権が与えられたアプレットで実行しなければなりません。次の説明はアプレット ファイル内のコードについて言及しています。
サンプル アプレットの socket-http-client.curl は、同期または非同期のいずれかの TCP ソケット API を使用して Web サーバーに HTTP リクエストを行ってレスポンスを得る方法を示しています。
サンプルはまず、
Url から接続しようとする HTTP Web サーバーを識別するホスト名とポートを取得し、その情報を使用して
DataTCPSocket を作成します。この動作は
SocketHttpRequest クラスの
create-socket-and-command-bytes プロシージャで行われます。次のコードは
DataTCPSocket を返します。
{DataTCPSocket remote-name = host, remote-port = port}
{socket.async-connect
{on e:AsyncConnectSocketEvent do
{if-non-null ex = e.exception then
{cleanup-proc}
{callback ex, null, null, false}
elseif e.canceled? then
{cleanup-proc}
{callback null, null, null, true}
else
set output-stream = {socket.to-OutputStream}
set worker.worker =
{output-stream.async-write
bytes,
{on e:AsyncStreamWriteEvent do
...
}
}
}
}
}
{output-stream.async-write
bytes,
{on e:AsyncStreamWriteEvent do
{if-non-null ex = e.exception then
{cleanup-proc}
{callback ex, null, null, false}
elseif e.canceled? then
{cleanup-proc}
{callback null, null, null, true}
else
set input-stream = {socket.to-InputStream}
set worker.worker =
{input-stream.async-read
{on e:AsyncStreamReadEvent do
...
}
}
}
}
}
最後のコード フラグメントは、読み込みが成功、失敗、またはキャンセルされた時に呼び出され、
InputStream-of.async-read に渡されるイベント ハンドラを示しています。アプレットはソケットを閉じ、そのソケットのストリームを閉じ、読み込みデータまたは例外を処理します。
このアプレットはこの接続で単一の HTTP リクエストのみを行い、HTTP リクエストに含まれるヘッダーは HTTP Web サーバーに終了時に接続を閉じるよう指示し、従ってアプレットは、ソケットをクローズするサーバーから EOF を検出するまで読み込みます。接続を継続するクライアントの場合は、バイト数でどのくらいのデータが読み込まれ、
n パラメータの中に渡されたかを自分で把握するか、または、
partial? = true を設定しデータを受け取るたびにそれを
AsyncStreamReadEvent に渡して解析する必要があります。
{on e:AsyncStreamReadEvent do
{if-non-null ex = e.exception
then
{cleanup-proc}
{callback ex, null, null, false}
elseif e.canceled? then
{cleanup-proc}
{callback null, null, null, true}
else
{callback
null,
e.data asa ByteArray,
socket.local-address.address-as-String & ":" & socket.local-port,
false
}
{cleanup-proc}
}
}
サンプル アプレットの socket-http-server.curl は、同期または非同期のいずれかの TCP ソケット API を使用して簡単な HTTP 1.0 Web サーバーを実装する方法を示しています。
次のコード フラグメントは HttpServer クラスの make-server-socket プロシージャからのものです。
def server-socket =
{AcceptorTCPSocket
||-- local-port = 80
}
{server-socket.bind}
{return server-socket,
server-socket.local-port,
"http://localhost:" & server-socket.local-port & "/"
}
{server-socket.async-accept
{on e:AsyncAcceptSocketEvent do
{if e.exception != null or e.canceled? then
{if e.exception != null then
{status-callback "Failed accepting " & e.exception.message}
}
{server-socket.close}
{return}
}
def socket = e.socket
def remote-id =
socket.remote-address.address-as-String & ":" &
socket.remote-port
{status-callback "Connection from " & remote-id}
def input-stream =
{socket.to-InputStream}
def read-worker =
{input-stream.async-read
partial? = true,
append? = true,
{on e:AsyncStreamReadEvent do
...
}
}
}
}
次のコード フラグメントはこのプロセスを説明しています。
def read-worker =
{input-stream.async-read
partial? = true,
append? = true,
{on e:AsyncStreamReadEvent do
{if e.exception != null or e.canceled? then
{if e.exception != null then
{status-callback
remote-id & e.exception.message
}
}
{socket.close}
{input-stream.close}
{return}
}
{status-callback
remote-id & " read " &
(e.data asa ByteArray).size
}
def (http-command, request-path, http-version,
request-headers) =
{HttpServer.parse-request (e.data asa ByteArray)}
{if request-path != null then
{read-worker.cancel}
{status-callback
remote-id & " got command " &
http-command & " " & request-path & " " & http-version
}
def response-data =
{HttpServer.make-response
http-command,
request-path,
http-version,
request-headers
}
{status-callback
remote-id & " writing " &
response-data.size
}
def output-stream =
{socket.to-OutputStream}
{output-stream.async-write
response-data,
{on e:AsyncStreamWriteEvent do
...
}
}
elseif e.done? then
{status-callback
remote-id & " got incomplete command."
}
}
}
}
def output-stream =
{socket.to-OutputStream}
{output-stream.async-write
response-data,
{on e:AsyncStreamWriteEvent do
{if e.exception != null then
{status-callback
remote-id &
e.exception.message
}
elseif e.canceled? then
}
{socket.close}
{input-stream.close}
{output-stream.close}
{status-callback remote-id & " done."}
}
}
UDP ソケットがパケットの書き込みまたは読み込みをするにはセキュリティ上の制限があります。「
ネットワークのアクセス制限」を参照してください。
サンプル アプレットの udp-server.curl では、同期および非同期の両方の API を使用してパケットを読み込むサーバーを実装する方法を示し、パケット内のものをすべて大文字に変え、送信元に送り返します。udp-client.curl と呼ばれるクライアントのサンプルもありますが、これはサーバーにパケットを送りレスポンスを待つと言うこと以外は、サーバーのサンプルと似ています。
次のコード フラグメントは
udp-server.curl からのものです。最初のフラグメントが示すように、サンプルはまず、
UDPSocket を作成します。ローカル ポートなしに作成し、システムがそれを割り当てられるようにしますが、必要に応じてポートをリクエストすることもできます。その後
UDPSocket.bind を呼び出して、クライアントにパケットの送り先を指示できるようにあとで返すことができる、特定のローカル ポートにソケットをバインドします。また、
UDPSocket.bind はソケットに対しパケットの受信を開始させます。UDP ソケットは基本的に送られてくるパケットを待つ必要がないので、
UDPSocket.bind をスキップしパケットの書き込みを開始することができます。
def socket = {UDPSocket}
{socket.bind}
def read-worker =
{socket.async-read-packet
{on e:AsyncReadPacketSocketEvent do
{if e.exception != null or e.canceled? then
{socket.close}
else
def packet = {non-null e.packet-data}
{UDPServer.upcase-packet packet}
{packet-array.append packet}
{address-array.append {non-null e.remote-address}}
{port-array.append e.remote-port}
{status
e.remote-address.address-as-String & ":" &
e.remote-port & " read " & packet.size & " bytes"
}
{write-proc}
}
}
}
最後は、送信準備のできたパケットの1つの書き込みを開始する必要があるかどうかをみるコードを示します。既に送られたものがない場合、送る必要のあるパケット、アドレス、ポートを指定して
UDPSocket.async-write-packet を呼び出します。書き込みが完了、失敗、またはキャンセルされたときは、次の書き込みを再開するか、キャンセルされたエラーがあれば、終了します。この書き込みが進行中に、追加の受信パケットも
UDPSocket.async-read-packet 呼び出しを続けて書き込んだり、処理されたり、送信される応答パケットのリストに加えたりします。UDP ソケットでは、パケットは様々なネットワーク レイヤによってドロップされたり、送信先に届かない場合があるので注意してください。
def write-proc =
{proc {}:void
{if packet-array.size > 0 and socket.open? and
write-worker == null
then
def packet-size = packet-array[0].size
def remote-address = address-array[0]
def remote-port = port-array[0]
set write-worker =
{socket.async-write-packet
packet-array[0],
remote-address = remote-address,
remote-port = remote-port,
{on e:AsyncWritePacketSocketEvent do
{status
remote-address.address-as-String & ":" &
remote-port & " wrote " & packet-size & " bytes"
}
set write-worker = null
{if e.exception != null or e.canceled? then
{socket.close}
else
{write-proc}
}
}
}
{packet-array.remove 0}
{address-array.remove 0}
{port-array.remove 0}
}
}
バージョン8.0のAPIから、ソケット、HTTP、およびセキュリティ システムに対して IPv6 のサポートが追加されました。以前のAPIは今までと同様に動作しますが、それらに加えほとんどのコールが IPv6 アドレスを受け付けるようになりました。オペレーティング システムが IPv6 をサポートしている場合は、IPv6 アドレスはオペレーティング システムから
String または
Url の形式での受け渡しが可能になっています。IPv6 をアプリケーションで使用する場合は新しいバージョンの API が必要になりますが、ホスト名やURLを受け渡しするだけのアプリケーションであれば特別な作業は必要ありません。
ソケット API は下記のフォーマットで IPv6 アドレスを受け付けます。
- "fe80::1"
- "[fe80::2%1]"
- "[fe80::2%251]"
また
Url API は
'[' ']' でアドレスが括られているフォーマットを受け付けます。
ホスト名は数値のアドレスよりも汎用的であり、アプリケーションがホスト名もしくは文字列のみをアドレスに使用している場合は特別な修正を加えることなく IPv4/IPv6 どちらのプロトコルでもサーバ/クライアントで動作いたします。(ただし、適切なアドレス ファミリを両方がサポートしており、その設定が適切である必要があります。)
Copyright © 1998-2019 SCSK Corporation.
All rights reserved.
Curl, the Curl logo, Surge, and the Surge logo are trademarks of SCSK Corporation.
that are registered in the United States. Surge
Lab, the Surge Lab logo, and the Surge Lab Visual Layout Editor (VLE)
logo are trademarks of SCSK Corporation.