通过套接字在Java桌面应用程序和Java浏览器applet之间建立通信

时间:2012-07-19 02:08:38

标签: java javascript sockets google-chrome japplet

我是网络I / O编程的新手,我遇到了麻烦 - 基本上我想做的是让桌面应用与谷歌地图javascript API对话。为了促进这一点,我构建了一个java applet,它将充当桌面应用程序和浏览器javascript应用程序之间的桥梁。当我在Eclipse中一起运行桌面应用程序和applet时,它们可以完美地进行通信,并且我能够通过将字符串写入绑定到applet与之建立ServerSocket连接的同一端口的Socket来调用applet函数。为了在Eclipse中进行测试,我发送字符串" sendJSAlertTest"到socket的输出流,然后使用ServerSocket输入流中的java.lang.reflect API派生一个Method实例,最后在applet中调用生成的方法。当applet在浏览器中运行时,我写了#34; sendJSAlert"而不是因为它导致javascript的实际调用。 Eclipse中使用appletviewer的结果是桌面应用程序上下文打印输出" awesome sendJSAlert"并且applet上下文打印sendJSAlertTest()方法的输出," Hello Client,我是服务器!"。传递" sendJSAlert"的结果对于在浏览器中运行的applet,桌面应用程序打印为null,表明由于某种原因,ServerSocket的输入流是空的,并且浏览器本身在应该生成带有文本" Hello的javascript警告框时什么也不做。客户,我是服务器!"。我使用的浏览器是谷歌浏览器,目前我只是在本地计算机上运行所有内容(例如,还没有涉及远程服务器)

以下是相关的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>

在webapp中,我使用本地计算机上的有效端口号填写args输入,然后按下测试套接字连接按钮,该按钮调用sockPuppet()javascript。这应该创建一个ServerSocket并将其绑定到指定的端口,然后我将我的桌面客户端应用程序单独连接到SocketClient.listenSocket。 Eclipse在桌面应用程序上下文中的结果是&#34; awesome sendJSAlertTest&#34;并且在appletviewer上下文中输出&#34; sendJSAlert远程调用,消息:Hello Client,我是服务器!&#34;。调用sendJSAlert()的webapp应该在同一条消息上调用javascript alert函数,创建一个弹出框,其中包含远程调用的消息&#34; sendJSAlert,消息:Hello Client,I&#39; ma!!&#34 ;但是在浏览器中没有任何反应(也不是Chrome java或javascript调试控制台),并且桌面应用程序输出为null而不是&#34; awesome sendJSAlert&#34;正如预期的那样

所以问题是:可能导致不同结果的原因是什么?我知道浏览器的安全沙箱可能是一个问题,但我已经包含了一个权限文件,该文件应允许通过任何localhost端口上的套接字进行通信:

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

虽然我没有正确应用权限(我使用了sun policytool gui),但这当然是可能的。应用权限的applet代码(如果有的话)到底需要做什么?一个安全问题会导致我看到的缺乏响应吗?我希望在Chrome的java调试控制台中报告异常,但是没有任何...

任何帮助都将非常感谢,谢谢!

-CCJ

更新: 好的,一些新信息:我在Chrome浏览器中再次运行applet,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应该如何定位它;我需要添加到applet代码中以将JRE指向预期的工作目录策略文件吗?此外,我添加的系统策略文件授权是否应该足以满足CCM.jar中我的applet的套接字权限?

更新2: 我签署了applet并添加了一行policy.url.3 = file:$ {user.home} \ Desktop \ dev \ Invention \ ATT \ MapViz \ ccmPolFile.policy到$ {java.home} /中的java.security文件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;
    }
});

这应该让我几乎做任何事情,因为applet现在已经签名了。我想继续退出等式,但政策文件由于某种原因不起作用。我很快就会回来了解它是如何运作的

1 个答案:

答案 0 :(得分: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
}

显然这是一个不安全的kludge,但它可以解决问题 - 我没有收到任何安全异常,桌面应用程序打印出“非常棒的sendJSAlert”,所以我知道IO通过套接字在客户端和服务器上下文之间工作。实际的js警报功能没有触发,但我认为这与上面的listenSocket()中的horrid无限while循环有关...

带回家消息:出于某种原因,要从谷歌浏览器中的小程序建立套接字连接我需要签署小程序并使用AccessController.doPrivileged()来调用我的安全敏感代码,尽管已设置我的本地策略和安全文件授予我的applet这些权限

googlers见refs:

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%:D我将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()方法阻塞,直到建立连接,因此对于这种情况,我只想一次将一个命令传递给serversocket输入流,而while循环没有意义。更改为if允许程序实际继续执行java.reflect内容,该内容调用applet中直接调用javascript函数的方法。由于端口仍然是硬编码的,并且applet使用了doPrivileged(...),这仍然不是一个很好的解决方案,但它确实满足了通过java applet桥从桌面java应用程序在Web浏览器中调用javascript的用例所以它为更好的实现提供了良好的跳板!