2012-07-19 63 views
1

我是網絡I/O編程的新手,我碰到了一個障礙 - 基本上我想要做的是讓桌面應用程序與谷歌地圖JavaScript API。爲了促進這一點,我已經構建了一個Java applet,它將充當桌面應用程序和瀏覽器JavaScript應用程序之間的橋樑。當我在Eclipse中一起運行桌面應用程序和applet時,它們可以完美地通信,並且我可以通過將字符串寫入綁定到Applet與ServerSocket建立連接的同一端口的Socket來調用Applet函數。爲了在Eclipse中進行測試,我將字符串「sendJSAlertTest」發送到套接字的輸出流,然後使用ServerSocket輸入流中的java.lang.reflect API派生Method實例,最後調用applet中的結果方法。當applet在瀏覽器中運行時,我將「sendJSAlert」寫入套接字,因爲它導致實際調用javascript。 Eclipse中使用appletviewer的結果是,桌面應用程序上下文打印輸出「awesome sendJSAlert」,而applet上下文打印sendJSAlertTest()方法的輸出,「Hello Client,我是服務器!」。將「sendJSAlert」傳遞給在瀏覽器中運行的小程序的結果是,桌面應用程序打印出null,這表明由於某種原因,ServerSocket的輸入流爲空,並且瀏覽器本身什麼也不做,因爲它應該生成一個javascript alert框文本「你好客戶端,我是服務器!」。我使用的瀏覽器是谷歌Chrome,而且目前我只是運行在本地機器上的一切(還涉及如沒有遠程服務器)通過套接字在Java桌面應用程序和Java瀏覽器小應用程序之間建立通信

下面是相關的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應用,我填寫輸入與本地計算機上有效的端口號的指定參數和按下該調用馬甲()的javascript測試插座連接按鈕。這應該創建一個ServerSocket並將其綁定到指定的端口,然後我通過SocketClient.listenSocket單獨連接我的桌​​面客戶端應用程序。 Eclipse在桌面應用上下文中的結果是「awesome sendJSAlertTest」,在appletviewer上下文中是輸出「sendJSAlert遠程調用,消息:Hello Client,我是服務器!」。調用sendJSAlert()的webapp應該調用相同消息的javascript alert函數,創建一個彈出框,其中包含消息「sendJSAlert遠程調用,消息:Hello Client,我是服務器!但在瀏覽器中沒有任何反應(Chrome java或javascript調試控制檯),並且桌面應用程序輸出爲空,而不是像預期的那樣「真棒sendJSAlert」

所以問題:可能導致不同結果的原因?我知道瀏覽器的安全沙箱可能是一個問題,但我已經包含了許可文件,它應該允許任何本地主機端口上通過套接字通信:

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

,雖然我已經無法正常應用的權限這當然是可能的(我用太陽policytool gui); applet代碼中究竟需要做什麼(如果有的話)來應用權限?安全問題是否會導致我看到的響應缺乏?我希望在Chrome的java調試控制檯中報告一個異常,但是沒有任何...

任何幫助將不勝感激,謝謝!

-CCJ

UPDATE: 好吧,一些新的信息:我在Chrome再次運行小程序的JavaScript控制檯打開(可以發誓我想在此之前沒有效果,但顯然不是),並取得了以下控制檯輸出 -

"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頁面,包含applet的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中我的applet的套接字權限嗎?

更新2: 我簽署了小程序並將行policy.url.3 = file:$ {user.home} \ Desktop \ dev \ invention \ ATT \ MapViz \ ccmPolFile.policy添加到我的java.security文件中在$ {java.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; 
    } 
}); 

這應該讓我做任何事情,因爲小程序現在已經簽署。我想繼續退出等式,但政策文件由於某種原因而不起作用。我很快就會回來與如何工作了

+0

1)鑑於你沒有添加一個,提示我問..你的問題是什麼? 2)爲了更快地獲得更好的幫助,請發佈[SSCCE](http://sscce.org/)。對於這種用例,它需要是一個混合應用程序/ applet(擴展applet,但有一個'main(String [])'來啓動應用程序。)。 3)觀察到的任何差異可能與安全性有關。一旦小應用程序加載並嘗試與應用程序通信,請檢查Java控制檯的輸出。 – 2012-07-19 02:39:48

+0

@Andrew Thompson:是的,對的是真正的問題......現在加入。如果我按照您的建議設置應用程序(將applet嵌入到應用程序中),如何將applet上下文設置爲在瀏覽器中運行? – CCJ 2012-07-19 04:46:45

+1

下面是一個[混合的例子](https://sites.google.com/site/drjohnbmatthews/subway)這個applet並沒有嵌入到網頁的任何地方。針對您的使用案例,小程序和應用程序。版本顯然會做非常不同的事情,而不是那個爲兩者加載相同GUI的例子。 – 2012-07-19 04:55:54

回答

1

righto,所以關注我的更新上面的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 
} 

顯然這是一個不安全的雜牌,但它的確有竅門 - 我沒有收到安全例外,桌面應用程序打印出「真棒sendJSAlert」,所以我知道IO通過套接字在客戶端和服務器上下文之間工作。實際的js警報功能並沒有觸發,但我認爲這與上面的listenSocket()中的horrid infinite while循環有關...

回家信息:由於某種原因,建立來自在Chrome瀏覽器的小程序我需要簽署小程序並使用AccessController。doPrivileged的()調用我的安全敏感的代碼,儘管有設置我的本地策略和安全文件授予我的小應用程序的權限

的Google見參考文獻:

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

UPDATE:最後工作100%:DI改變listenSocket()方法在上面SocketServer.java這樣:

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()方法將阻塞,直到建立連接反正製成,所以對於這種情形,其中我只想一次將一個命令傳遞給serversocket inputstream while while循環沒有意義。對if的改變允許程序實際上繼續到java.reflect的東西,該東西調用直接調用JavaScript函數的applet中的方法。由於端口仍然是硬編碼的,並且applet使用doPrivileged(...),所以這仍然不是一個好的解決方案,但它確實滿足了通過java applet橋從桌面Java應用程序在Web瀏覽器中調用JavaScript的用例所以它成爲更強大實施的良好跳板!

+0

我有三個Java發行版安裝,並且這些版本也有相應的三個JDK,因此它可能是我正在編輯的java.security文件(位於C:\ Program文件(x86)\ Java \ JRE7 \ lib \ security)可能不是什麼Chrome加載以應用applet安全策略......無論如何都要進行測試 – CCJ 2012-07-19 21:23:28

相關問題