1

私はネットワークI/Oプログラミングに不慣れで、問題に遭遇しました。基本的に私がやりたいのは、デスクトップアプリにGoogle MapsJavaScriptAPIと通信させることです。これを容易にするために、デスクトップアプリとブラウザーのjavascriptアプリの間のブリッジとして機能するJavaアプレットを作成しました。デスクトップアプリとアプレットをEclipseで一緒に実行すると、それらは完全に通信でき、アプレットがServerSocket接続を確立したのと同じポートにバインドされたソケットに文字列を書き込むことでアプレット関数を呼び出すことができます。Eclipseでテストするために、文字列「sendJSAlertTest」をソケットの出力ストリームに送信し、ServerSocket入力ストリームからjava.lang.reflect APIを使用してメソッドインスタンスを派生させ、最後にアプレットで結果のメソッドを呼び出します。アプレットがブラウザで実行されている場合、JavaScriptの実際の呼び出しにつながるため、代わりに「sendJSAlert」をソケットに書き込みます。appletviewerを使用したEclipseでの結果は、デスクトップアプリケーションコンテキストが出力「awesomesendJSAlert」を出力し、アプレットコンテキストがsendJSAlertTest()メソッド「HelloClient、I'm aServer!」からの出力を出力することです。ブラウザで実行されているアプレットに「sendJSAlert」を渡した結果、デスクトップアプリケーションはnullを出力します。これは、何らかの理由でServerSocketの入力ストリームが空であり、ブラウザ自体がjavascriptアラートボックスを生成する必要があるときに何もしないことを示しています。 「こんにちはクライアント、私はサーバーです!」というテキスト。私が使用しているブラウザはGoogleChromeです。

以下は、関連するJavaコードとHTMLです。

SocketClient.java

import java.awt.event.*;
import java.io.*;
import java.net.*;

public class SocketClient {


Socket socket = null;
PrintWriter out = null;
BufferedReader in = null;
private InetAddress myAddress;
private String remoteFunction;

public SocketClient(){

}


public void listenSocket(int portNum){
//Create socket connection

 try{
   System.out.println("@Client Trying to create socket bound to port " + portNum);
   socket = new Socket(<my hostname here as a string>, portNum);
   System.out.println("the attached socket port is " + socket.getLocalPort());
   System.out.flush();
   out = new PrintWriter(socket.getOutputStream(), true);
   out.println("sendJSAlertTest");
   in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
   String line = in.readLine();
   System.out.println("@CLient side Text received from server: " + line);
 } catch (UnknownHostException e) {
   System.out.println("Unknown host: <my hostname here as a string>.eng");
   System.exit(1);
 } catch  (IOException e) {
   System.out.println("No I/O");
   e.printStackTrace();
   System.exit(1);
 }
}

public void setRemoteFunction(String funcName){
  remoteFunction = funcName;
}
public String getRemoteFunction(){
 return remoteFunction;
}

}

SocketServer.java

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.*;

class SocketServer {


ServerSocket server = null;
Socket client = null;
BufferedReader in = null;
PrintWriter out = null;
String line;
private NetComm hNet;
private Method serverMethod;

SocketServer(NetComm netmain){
   hNet = netmain;



}



public void listenSocket(int portNum){

try{
  System.out.println("@server Trying to create socket bound to port " + portNum);
  server = new ServerSocket(portNum);
  System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
  System.out.println("Could not listen on port " + portNum);
  System.exit(-1);
}

try{
  client = server.accept();
  System.out.println("Connection accepted!");
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try{
  in = new BufferedReader(new InputStreamReader(client.getInputStream()));
  out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

while(true){
  try{
     System.out.println("trying to read from inputstream...");
    line = in.readLine();
    System.out.println(line);
    //Now that we have a method name, invoke it
    try {
        serverMethod = hNet.getClass().getDeclaredMethod(line,
  String.class);
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    try {
        serverMethod.invoke(hNet, "Hello Client, I'm a Server!");
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    //Send data back to client
    out.println("awesome " + line);


   } catch (IOException e) {
     System.out.println("Read failed");
     System.out.flush();
     System.exit(-1);
   }

  }
  }

  protected void finalize(){
  //Clean up
  try{
    in.close();
    out.close();
    server.close();
  } catch (IOException e) {
    System.out.println("Could not close.");
    System.exit(-1);
 }
}

public int getBoundLocalPort(){
  return server.getLocalPort();
}

}

NetComm.java

import cresco.ai.att.ccm.core.CCMMain;
import cresco.ai.att.ccm.gui.DataPanel;

import java.io.*;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.ArrayList;

import javax.servlet.*;
import javax.servlet.http.*;

import java.applet.*;


public class NetComm extends JApplet{//HttpServlet{
private CCMMain hMain;
private DataPanel dpLocal;
private SocketServer sockserver;
private Method serverMethod;

String testStr;
Integer testInt;  /*integer */
Character testChar; /*character*/

//Testing this...
ServerSocket server = null;
Socket client = null;
BufferedReader in = null;
PrintWriter out = null;
String line;






@Override
public void init(){
    sockserver = new SocketServer(this);


    //For offline debug (should be disabled in a release to the webapp):
            //initSocketServer is commented out in the release version and
            //invoked in the Eclipse testbed version.  In the webapp,
            //initSocketServer is invoked from javascript (see below js sockPuppet())
    //////initSocketServer(0);




    String msg = "Hello from Java (using javascript alert)";
    try {
        getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
   msg +"\")"));
    }
    catch (MalformedURLException me) { }


}

public void sendJSAlertTest(String message){
    System.out.println("sendJSAlert remotely invoked, with message: " +
    message);

}

public void sendJSAlert(String message){

    try {
        getAppletContext().showDocument(new URL("javascript:doAlert(\"" +
    message +"\")"));
    }
    catch (MalformedURLException me) { }

}



public void initSocketServer(int portNum){
    sockserver.listenSocket(portNum);
}
public void finalizeSocketServer(){
    sockserver.finalize();
}

public int socket2Me(int portNum){

    try {
        socks.add(new ServerSocket(portNum));
        return 0; //socket opened successfully
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
        return -1; //socket failed to open
    }




}

public int getSocketServerPort(){
    return sockserver.getBoundLocalPort();
}


public void showRectTest(){
    try {
        getAppletContext().showDocument(new
                    URL("javascript:overlayRect()"));
    }
    catch (MalformedURLException me) { }
}




public void setGUI(DataPanel d){
    dpLocal = d;
}


 }

MapViz.html

<html>
<head>
<title>Welcome to Geographic Midpoint Map Vizualization!</title>
<meta name="viewport"
    content="width=device-width, initial-scale=1.0, user-scalable=no">
<meta charset="UTF-8">
<link href="https://google-developers.appspot.com/maps/documentation/javascript
/examples/default.css"
    rel="stylesheet" type="text/css">

...google maps stuff omitted...

<script type="text/javascript">
<script type="text/javascript">
function overlayRect(){
    var c=document.getElementById("myCanvas");
    var ctx=c.getContext("2d");
    ctx.fillStyle="#FF0000";
    ctx.fillRect(0,0,150,75);
}
function doAlert(s){
    alert(s);
}
function testJava(){
    document.ccmApplet.showRectTest();
}
function sockPuppet(){
    var i = parseInt(document.getElementById("args").value,10);
    alert("parsing the input args... got " + i);
    if(i == NaN || i == null){
        i = 0;
    }
    alert("passed NaN OR null block, i is " + i);
    //i = 6672; //because $%*& you, that's why!
    document.ccmApplet.initSocketServer(i);
    //document.ccmApplet.listenSocket(i);
    alert("inittializing socket server...");
    //queryPort();
    alert("querying port...");
    document.ccmApplet.finalizeSocketServer();
    //document.ccmApplet.finalize();
    alert("finalizing socket server...");
}
function queryPort(){
    var d = document.getElementById("debug");
    var s1 = "Last port opened was: ";
    //var s2 = document.ccmApplet.getLastBoundPort();
    var s2 = document.ccmApplet.getSocketServerPort();
    var sFinal = s1.concat(s2);
    d.value = sFinal;
}
</script>
 </head>
 <body>
 <applet width="500" height="50" name="ccmApplet" archive="CCM.jar"
 code="cresco.ai.att.ccm.io.NetComm" MAYSCRIPT></applet>
 <p></p>
<canvas id="myCanvas" width="200" height="100"></canvas>
 <div id="map_canvas"></div>
<input id="args" type="textentry" value="" />
<button height="50" width="50" onClick="sockPuppet()">Test Socket
    Creation</button>
<input id="debug" type="debugthingy" value="debug area..." />
<button height="50" width="50" onClick="testJava()">Test Java Callback</button>
  </body>
 </html>

Webアプリで、args入力にローカルマシンの有効なポート番号を入力し、sockPuppet()javascriptを呼び出す[ソケット接続のテスト]ボタンを押します。これにより、ServerSocketが作成され、指定されたポートにバインドされます。次に、SocketClient.listenSocketを介してデスクトップクライアントアプリを個別に接続します。デスクトップアプリコンテキストでのEclipseの結果は「素晴らしいsendJSAlertTest」であり、appletviewerコンテキストでは、「Hello Client、I'm aServer!」というメッセージとともに「sendJSAlertがリモートで呼び出されました」という出力が返されます。sendJSAlert()を呼び出すWebアプリは、同じメッセージでjavascriptアラート関数を呼び出し、「sendJSAlertがリモートで呼び出されました。メッセージ:Hello Client、I'm aServer!」というメッセージを含むポップアップボックスを作成する必要があります。

だから質問:異なる結果の原因は何でしょうか?ブラウザのセキュリティサンドボックスが問題になる可能性があることは知っていますが、任意のローカルホストポートのソケットを介した通信を許可するアクセス許可ファイルを含めました。

grant {
 permission java.net.SocketPermission
    "localhost:1024-", 
 "accept, connect, listen, resolve";
};

パーミッションを適切に適用していない可能性は確かにあります(sun policytool guiを使用しました)。パーミッションを適用するには、アプレットコード(ある場合)で正確に何をする必要がありますか?セキュリティ上の問題により、私が見ている応答が失われる可能性がありますか?ChromeのJavaデバッグコンソールで例外が報告されると思いますが、何もありませんでした...

どんな助けでも大歓迎です、ありがとう!

-CCJ

更新:わかりました、いくつかの新しい情報:javascriptコンソールを開いた状態でChromeでアプレットを再度実行し(以前にこれを試しても効果がないことを誓ったかもしれませんが、明らかにそうではありません)、次のコンソール出力を受け取りました-

"Uncaught Error: java.security.AccessControlException: access denied 
("java.net.SocketPermission" "<myipaddress>:4218" "accept,resolve") MapVizApp.html:154
 sockPuppet MapVizApp.html:154 onclick MapVizApp.html:179 Uncaught Error: Error 
 calling method on NPObject. sockPuppet onclick " 

では、なぜこのセキュリティ例外をトリップするのかという問題があります。上記の権限を持つポリシーファイルは、htmlページおよびアプレットを含むjarファイルと同じ作業ディレクトリにあり、システムのJREセキュリティポリシーファイルに次のものを追加しました。

//Grants my NetComm applet the ability to accept, connect, and listen on unpriv. ports
grant codeBase "file:${user.home}\Desktop\dev\invention\ATT\MapViz\CCM.jar" {
  permission java.net.SocketPermission
    "localhost:1024-", 
  "accept, connect, listen, resolve";
};

まだアプレットに署名していませんが、ポリシーファイルが正常であれば、アプレットに署名する必要はないことを理解していました...間違っている場合はお知らせください。とにかく、ポリシーファイルに上記の権限が付与されているにもかかわらず、なぜこのセキュリティ例外がスローされるのかについて、誰かが何か提案がありますか?JREが検索する作業ディレクトリ内のポリシーファイルの命名規則はありますか?今のところ私の作業ディレクトリポリシーファイルはccmPolFileという名前ですが、JREがどのようにそれを見つけるのかはっきりしていません。JREが目的の作業ディレクトリポリシーファイルを指すように、アプレットコードに追加する必要があるものはありますか?さらに、追加したシステムポリシーファイルは、CCM.jar内のアプレットのソケット権限を満たすのに十分であると認めるべきではありませんか?

更新2:アプレットに署名し、行policy.url.3 = file:$ {user.home} \ Desktop \ dev \vention \ ATT \ MapViz\ccmPolFile.policyを${javaのjava.securityファイルに追加しました。 home} / lib / security(http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2経由これは明らかにJREがロードするポリシーファイルを見つける方法です)...悲しいことに、結果まったく同じセキュリティ例外です。私が知っている唯一のことは

AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
        // perform the security-sensitive operation here
        return null;
    }
});

アプレットが署名されたので、これでほとんど何でもできます。方程式からサインアウトし続けたかったのですが、何らかの理由でポリシーファイルが機能していません。私はそれがどのようにうまくいくかについてすぐに戻ってきます

4

1 に答える 1

1

そうです、上記の更新 2 に従って、SocketServer.java コードの listenSocket() メソッドを次のように変更します。

public void listenSocket(int portNum){
  AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
            int portNum = 4444;
try{
  System.out.println("@server Trying to create socket bound to port " + portNum);
  server = new ServerSocket(portNum); 
  System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
  System.out.println("Could not listen on port " + portNum);
  System.exit(-1);
}

try{
  client = server.accept();
  System.out.println("Connection accepted!");
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try{
  in = new BufferedReader(new InputStreamReader(client.getInputStream()));
  out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

while(portNum==4444){
  try{
     System.out.println("trying to read from inputstream...");
    line = in.readLine();
    System.out.println(line);
    //Now that we have a method name, invoke it
    try {
        serverMethod = hNet.getClass().getDeclaredMethod(line, 
     String.class);
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    try {
        serverMethod.invoke(hNet, "Hello from Javascript invoked by the
    desktop app!");
    } catch (IllegalArgumentException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    //Send data back to client
    out.println("awesome " + line);
    //System.out.println(line);
    //System.out.flush();

  } catch (IOException e) {
    System.out.println("Read failed");
    System.out.flush();
    System.exit(-1);
  }

}

return null;
        }
    });//end doPrivileged
}

明らかにこれは安全でないクラッジですが、うまくいきます.セキュリティ例外は発生せず、デスクトップアプリは「awesome sendJSAlert」を出力するので、ソケットを介してクライアントとサーバーのコンテキスト間でIOが機能していることがわかります。実際の js アラート関数は起動しませんでしたが、上記の listenSocket() の恐ろしい無限 while ループと関係があると思います...

ホーム メッセージ: 何らかの理由で、Google Chrome でアプレットからソケット接続を確立するには、アプレットに署名し、AccessController.doPrivileged() を使用してセキュリティに敏感なコードを呼び出す必要がありました。アプレットのアクセス許可

グーグル社員は参照を参照してください:

http://www.coderanch.com/how-to/java/HowCanAnAppletReadFilesOnTheLocalFileSystem

http://docs.oracle.com/javase/7/docs/api/java/security/AccessController.html

http://www-personal.umich.edu/~lsiden/tutorials/signed-applet/signed-applet.html

http://docs.oracle.com/javase/tutorial/security/tour2/step4.html

更新: 最終的に 100% 動作:DI は、SocketServer.java の上記の listenSocket() メソッドを次のように変更しました。

public void listenSocket(int portNum){
  AccessController.doPrivileged(new PrivilegedAction() {
        public Object run() {
            int portNum = 4444;
try{
  System.out.println("@server Trying to create socket bound to port " + portNum);
  server = new ServerSocket(portNum); 
  System.out.println("the attached socket port is " + server.getLocalPort());
} catch (IOException e) {
  System.out.println("Could not listen on port " + portNum);
  System.exit(-1);
}

try{
  client = server.accept();
  System.out.println("Connection accepted!");
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try{
  in = new BufferedReader(new InputStreamReader(client.getInputStream()));
  out = new PrintWriter(client.getOutputStream(), true);
} catch (IOException e) {
  System.out.println("Accept failed: " + portNum);
  System.exit(-1);
}

try {
    line = in.readLine();
    System.out.println("line is " + line + " from the inputstream to the 
            serversocket");
} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}

if(line != null){
  System.out.println("trying to read from non-null inputstream...");
//line = in.readLine();
System.out.println(line);
//Now that we have a method name, invoke that bitch!
try {
    serverMethod = hNet.getClass().getDeclaredMethod(line, String.class);
} catch (SecurityException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

try {
    serverMethod.invoke(hNet, "Hello From Javascript invoked by a desktop 
             app!");
} catch (IllegalArgumentException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (IllegalAccessException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
} catch (InvocationTargetException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

//Send data back to client
out.println("awesome " + line);
//System.out.println(line);
//System.out.flush();

 }

return null;
        }
    });//end doPrivileged
}

とにかく接続が確立されるまで server.accept() メソッドはブロックするため、一度に 1 つのコマンドのみをサーバーソケット入力ストリームに渡したいこのシナリオでは、while ループは意味がありませんでした。if への変更により、プログラムは、javascript 関数を直接呼び出すアプレット内のメソッドを呼び出す java.reflect スタッフに実際に進むことができました。ポートはまだハードコーディングされており、アプレットは doPrivileged(...) を利用しているため、これはまだ優れたソリューションではありませんが、Java アプレット ブリッジを介してデスクトップ Java アプリケーションから Web ブラウザで JavaScript を呼び出すユース ケースを満たしています。そのため、より堅牢な実装への良い出発点になります!

于 2012-07-19T21:08:07.177 に答える