Difference between revisions of "Category:LSL XML-RPC/ja"

From Second Life Wiki
Jump to navigation Jump to search
(Initial translation)
 
(Redirected page to Category:LSL XML-RPC通信)
Line 1: Line 1:
{{LSL Header/ja|ml=*}}{{LSLC/ja|}}{{LSLC/ja|Communications}}
#Redirect [[:Category:LSL XML-RPC通信]]
{{Wikipedia|1=XML-RPC|2=XML-RPC|lang=ja}} は外部システムへのプロシージャコール (機能呼び出し) 送信に使われるひとつの標準です。そこでは、外部システムが扱う {{Wikipedia|1=XML|2=XML|lang=ja}} データが HTTP でやり取りされます。
 
LSL は XML-RPC 要求を受け取ると、それを指定のプリムへ渡します。プリムはコネクションを確立しないかもしれませんが、応答は可能であり、サーバとの双方向通信を維持します。これらの応答は ({{LSLG/ja|Email}} や {{LSLGC/ja|HTTP}} 要求に比べると) Second Life 外との大規模なデータのやりとりを可能にしてくれるようです。
 
=== 注意:{{Anchor|NOTE:}} ===
 
XML-RPC 要求は、フロントエンド サーバが過負荷となるため、しばしばタイムアウトします。LL (Linden Lab) は定期的にサーバのハードウェアを更新し続けていますが、未だに信頼性には欠けます。(サーバが 1 つだけというボトルネックゆえに) XML-RPC の設計に拡張性が無く、そのサービスは "推奨されない" と LL の開発者は勧告してきました。彼らは HTTP ポーリング (訳注: 外部からの要求を待つのでなく、要求の有無を相手側へ一定時間おきに確認しにゆく手法。HTTP polling) を代替とするよう提案しています。XML-RPC 要求がタイムアウトした場合、そのスクリプトの remote_data イベントは発生するかもしれませんし、発生しないかもしれません。(後者の場合、スクリプトは無反応になります。)
 
=== 実装に関する重要な注意:{{Anchor|IMPORTANT IMPLEMENTATION NOTE:}} ===
 
現在の XML-RPC の実装では、フロントエンド サーバ (xmlrpc.secondlife.com) で同時にキューに入ることができる要求はただ 1 個だけです。同じデータ チャネルに新しい要求があった場合、それは既存のものを上書きします。これは XML-RPC 通信の設計にとって深刻な問題です。なぜなら XML-RPC 通信では in-world のオブジェクトが応答可能になる前に要求を受け取る可能性があるからです。加えて、{{LSLG/ja|llRemoteDataReply}} 関数の 3 秒間の遅延が問題をより深刻にします。
 
分かっている問題は以下のとおりです: XML-RPC を介して in-world のオブジェクト (何らかの処理を行なって llRemoteDataReply 関数で応答を返すスクリプトが入っている) に複数の要求を立て続けに送ると、先に送られた要求ほど ({{LSLG/ja|remote_data}} イベントを発生させなければならないのに) フロントエンド サーバで取りこぼされる可能性があります。その結果、先の要求に対して、後の要求に関する応答を返してしまいます。本来ならば先の要求に関して、外部アプリケーションに対しタイムアウトが返るところです。
 
結局のところ、XML-RPC を使って何らかの重要な処理を行なおうとするならば、各 RPC チャネルに対する全ての要求に連番をつけて管理するよう、外部のクライアント アプリケーションを設計しなければならないでしょう。すなわちこれは、次の要求を送る前に、前の要求に関する応答が帰ってくるのを待たなけれならない事を意味します。応答の受け取りを気にしないで済むならば、これは問題ではありません。なぜならキューの問題がどうであろうと、全ての要求はスクリプトへ渡されたように見えるからです。
 
なお llRemoteDataReply 関数の 3 秒間の遅延から逃れる方法はありません。例えば multiple-slave-comm-script といった手法 (訳注: {{LSLG/ja|llMessageLinked}} 関数を使い複数のスクリプトに並行処理をさせる手法のことか) は使えません。XML-RPC チャネルはスクリプト固有のものであり、オブジェクト固有のものではないからです。
 
これ以上の情報については公式フォーラムの[http://forums.secondlife.com/showthread.php?t=158437 このスレッド]と[http://forums.secondlife.com/showthread.php?p=1379244 このスレッド]を参照してください。
 
== 参考資料{{Anchor|Other Resources}} ==
* [http://www.xmlrpc.com/ XMLRPC.com]
* [http://scripts.incutio.com/xmlrpc/ Incutio's XML-RPC PHP library]
* [http://perisic.com/sl/ A simple form to launch an XMLRPC request into Second Life]
 
== 例{{Anchor|Examples}} ==
 
= php =
 
外部サーバとして XML-RPC を初期化するには、ある種の Web アプリケーションが必要です。
Web アプリケーションを作成する言語の一つが PHP です。
PHP を使い Web サーバから SL 内のスクリプトへ XML-RPC メッセージをどのように送信するかの例を以下に示します:
<php><?php
echo '<pre>';
$channel = ""; //Fill in the channel you are using (key)
$intvalue = ""; //Fill in the intvalue you are using (integer)
$strvalue = ""; //Fill in the strvalue you are using (string)
$xmldata = "<?xml version=\"1.0\"?><methodCall><methodName>llRemoteData</methodName>
<params><param><value><struct>
<member><name>Channel</name><value><string>".$channel."</string></value></member>
<member><name>IntValue</name><value><int>".$intvalue."</int></value></member>
<member><name>StringValue</name><value><string>".$strvalue."</string></value></member>
</struct></value></param></params></methodCall>";
echo sendToHost("xmlrpc.secondlife.com", "POST", "/cgi-bin/xmlrpc.cgi", $xmldata);
echo '</pre>';
function sendToHost($host,$method,$path,$data,$useragent=0)
{
$buf="";
// Supply a default method of GET if the one passed was empty
if (empty($method))
$method = 'GET';
$method = strtoupper($method);
$fp = fsockopen($host, 80, $errno, $errstr, 30);
if( !$fp )
{
$buf = "$errstr ($errno)<br />\n";
}else
{
if ($method == 'GET')
$path .= '?' . $data;
fputs($fp, "$method $path HTTP/1.1\r\n");
fputs($fp, "Host: $host\r\n");
fputs($fp, "Content-type: text/xml\r\n");
fputs($fp, "Content-length: " . strlen($data) . "\r\n");
if ($useragent)
fputs($fp, "User-Agent: MSIE\r\n");
fputs($fp, "Connection: close\r\n\r\n");
if ($method == 'POST')
fputs($fp, $data);
while (!feof($fp))
$buf .= fgets($fp,128);
fclose($fp);
}
return $buf;
}
?></php>
 
= perl =
 
Second Life 内のオブジェクトにアクセスするための perl コード、及びそれに対応する LSL のコードは以下のとおりです。perl コードは RPC::XML モジュールを使います。この perl コードはコマンドラインで実行できます。それは RPC 関連の perl オブジェクトを作成し、グリッド内に rez された SL オブジェクトへメッセージを送信します。perl コードに、SL オブジェクトの UUID を与えなければならない点に注意してください。
 
この perl コードは "Message to pass" という文字列と "2007" という数字を含んだメッセージを送信します。LSL コードは "I got it" という文字列と "2008" という数字を応答として返します。
 
<perl>#!/usr/bin/perl
#
 
use RPC::XML;
use RPC::XML::Parser;
use RPC::XML::Client;
 
my $llURL = "http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi";
$P = RPC::XML::Parser->new();
 
my $cli = RPC::XML::Client->new($llURL);
my $req = RPC::XML::request->new(
        "llRemoteData",
        {'Channel' => RPC::XML::string->new("UUID for the open channel from the object GOES HERE!"),
        'IntValue' => RPC::XML::int->new(2007),
        'StringValue' => RPC::XML::string->new("message to pass")});
 
#  Print out the message to send
print "ref(req): ",ref($req),"\n";
$xml = $req->as_string();
print "Length: ",$req->length,"\n\n",$xml,"\n\n";
 
$res = $P->parse($xml);
print ref($res),"\n";
if (ref($res)) {
    %h = %{$res->args->[0]->value};
    foreach $lupe (keys %h) {
      print "$lupe: $h{$lupe}\n";
    }
}
 
#  Submit the request to send the information above.
my $resp = $cli->send_request($req);
 
 
#  Print out the response
print "\n\n\nResponse\n";
print "ref(resp): ",ref($resp),"\n";
$xml = $resp->as_string();
print "Length: ",$resp->length,"\n\n",$xml,"\n\n";
 
$res = $P->parse($xml);
print ref($res),"\n";
if (ref($res)) {
    %h = %{$res->value};
    foreach $lupe (keys %h) {
      $val =  $h{$lupe};
      print "$lupe: $val\n";
    }
}</perl>
 
 
= Java =
 
この Java コードは the org.apache.xmlrpc ライブラリを使っています。これに関する jar フィルは http://www.perisic.com/xmlrpc/cis69mc.jar にあります。(これを使って以下のコードはテストされました。) 必要ならば Java と XMLRPC に関する簡単なイントロダクションを http://perisic.com/xmlrpc で参照してください。
* SLClient.java という名前のファイルにあるコードを保存し、上記の URL から cis69mc.jar という jar ファイルをダウンロードしてください。
* '<add channel id here!!!>' とある場所にチャネル ID を追加してください。結果的にその行は <code>theData.put("Channel", "24a2c834-c984-9209-78df-20608d4c8ade");</code> といった具合になります。
* 次のコマンドでコードをコンパイルします: javac -classpath "cis69mc.jar;." SLClient.jar (未チェックあるいは安全でない操作という警告が出るかもしれませんが無視してください。)
* 次のコマンドでコードを実行してください: java -classpath "cis69mc.jar;." SLClient
 
<java>import java.util.*;
import org.apache.xmlrpc.*;
 
public class SLClient {
public static void main (String [] args) {
  try {
  Hashtable theData = new Hashtable();
  XmlRpcClient server = new XmlRpcClient("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi"); //
  theData.put("Channel", "<add channel id here!!!>");
  theData.put("IntValue", 2483);
  theData.put("StringValue", "The date is: "+ (new Date()).toString() );
  Vector params = new Vector();
  params.add(theData);
 
  Object result = server.execute("llRemoteData", params );
 
  } catch (Exception exception) {
  System.err.println("SL_Client: " + exception);
  exception.printStackTrace();
  }
  }
}</java>
 
= Visual Basic 6 =
 
この Visual Basic 6 コードは Microsoft WinSock 6.0 コントロールを使います。プロジェクトを新規作成し、フォームに Winsock コントロールを配置してください。
 
<vb>
Dim Channel As String
Dim IntValue As Integer
Dim StrValue As String
Dim XMLData As String
 
Private Sub Form_Load()
    Channel = "<add channel id here!!!>"
    IntValue = 2007
    StrValue = "This is a test string."
    XMLData = "<?xml version=""1.0""?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct>"
    XMLData = XMLData & "<member><name>Channel</name><value><string>" & Channel & "</string></value></member>"
    XMLData = XMLData & "<member><name>IntValue</name><value><int>" & CStr(IntValue) & "</int></value></member>"
    XMLData = XMLData & "<member><name>StringValue</name><value><string>" & StrValue & "</string></value></member>"
    XMLData = XMLData & "</struct></value></param></params></methodCall>"
 
    Winsock1.Connect "xmlrpc.secondlife.com", 80
End Sub
 
Private Sub Winsock1_Connect()
    WinsockSend "POST /cgi-bin/xmlrpc.cgi HTTP/1.1" & vbCrLf
    WinsockSend "Host: xmlrpc.secondlife.com" & vbCrLf
    WinsockSend "Content-type: text/xml" & vbCrLf
    WinsockSend "Content-length: " & CStr(Len(XMLData)) & vbCrLf
    WinsockSend "Connection: close" & vbCrLf & vbCrLf
    WinsockSend XMLData & vbCrLf
End Sub
 
Private Sub Winsock1_DataArrival(ByVal bytesTotal As Long)
    Dim TempStr As String
    Winsock1.GetData TempStr
    MsgBox TempStr
End Sub
 
Sub WinsockSend(str As String)
    Winsock1.SendData str
End Sub
</vb>
 
= Command line curl =
 
コマンドラインで XML-RPC をデバッグしたいならば、"curl" が使えます。<code>rpc.xml</code> という名前のファイルに以下の内容を保存してください。その際、UUID をスクリプトのチャネル ID に置き換えてください:
 
<xml>
<?xml version="1.0"?>
<methodCall>
    <methodName>llRemoteData</methodName>
    <params>
        <param>
            <value>
                <struct>
                    <member>
                        <name>Channel</name>
                        <value><string>1ee246fc-e60d-2fd3-6242-f5fc5d19850f</string></value>
                    </member>
                    <member>
                        <name>IntValue</name>
                        <value><int>42</int></value>
                    </member>
                    <member>
                        <name>StringValue</name>
                        <value><string>Hello, world!</string></value>
                    </member>
                </struct>
            </value>
        </param>
    </params>
</methodCall>
</xml>
 
その上で、curl を以下のように実行し、XMLRPC サーバに POST します。
 
<code>
curl --verbose --data "@rpc.xml" http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi
</code>
 
= LSL Server Code =
 
(上記の例に) 対応する LSL コードは以下の通りです。(オブジェクト内のスクリプトにコピー&amp;ペーストし、保存し、rez してください。チャネル ID およびその他の情報がメッセージとして表示されます。) :
 
<lsl>key remoteChannel;
init() {
    llOpenRemoteDataChannel(); // create an XML-RPC channel
    llOwnerSay("My key is " + (string)llGetKey());
}
 
default {
    state_entry() {
        init();
    }
   
    state_exit() {
        return;
    }
                               
    on_rez(integer param) {
        llResetScript();       
    }
                               
    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
        if (type == REMOTE_DATA_CHANNEL) { // channel created
            llSay(DEBUG_CHANNEL,"Channel opened for REMOTE_DATA_CHANNEL" +
                (string)channel + " " + (string)message_id + " " + (string)sender + " " +                       
                (string)ival + " " + (string)sval);
            remoteChannel = channel;
            llOwnerSay("Ready to receive requests on channel \"" + (string)channel + "\"");                       
            state receiving; // start handling requests
        } else {
            llSay(DEBUG_CHANNEL,"Unexpected event type");
        }                     
    }               
}                   
                               
 
state receiving {
 
    state_entry() {
        llOwnerSay("Ready to receive information from outside SL");
    } 
   
    state_exit() {
        llOwnerSay("No longer receiving information from outside SL.");
        llCloseRemoteDataChannel(remoteChannel);
    }
   
    on_rez(integer param) {
        llResetScript();
    }
   
    remote_data(integer type, key channel, key message_id, string sender, integer ival, string sval) {
        if (type == REMOTE_DATA_REQUEST) { // handle requests sent to us
            llSay(DEBUG_CHANNEL,"Request received for REMOTE_DATA_REQUEST " + (string)channel + " " +
                (string)message_id + " " + (string)sender + " " + (string)ival + " " + (string)sval);
            llRemoteDataReply(channel,NULL_KEY,"I got it",2008);
            llOwnerSay("I just received data in "+ llGetRegionName() +
                        " at position " + (string)llGetPos() + "\n" +
                      "The string was " +  sval + "\nThe number was " + (string)ival + ".");
        }
    }
}</lsl>

Revision as of 22:23, 14 April 2010