首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >通过套接字建立Java桌面应用程序与Java浏览器小程序之间的通信

通过套接字建立Java桌面应用程序与Java浏览器小程序之间的通信
EN

Stack Overflow用户
提问于 2012-07-19 10:08:38
回答 1查看 3.3K关注 0票数 1

我刚接触网络I/O编程,遇到了一个问题--基本上我想做的是让一个桌面应用程序与google maps javascript API对话。为了促进这一点,我构建了一个java applet,它将作为桌面应用程序和浏览器javascript应用程序之间的桥梁。当我在Eclipse中一起运行桌面应用程序和applet时,它们可以完美地通信,并且我能够通过将字符串写入绑定到applet建立ServerSocket连接的同一端口的套接字来调用applet函数。为了在Eclipse中进行测试,我将字符串"sendJSAlertTest“发送到套接字的输出流,然后使用java.lang.reflect API从ServerSocket输入流派生一个方法实例,最后在applet中调用生成的方法。当小程序在浏览器中运行时,我将"sendJSAlert“写到套接字中,因为它会导致实际的javascript调用。在使用小程序查看器的Eclipse中,结果是桌面应用程序上下文打印输出"awesome“,小程序上下文打印sendJSAlertTest()方法的输出"Hello Client,I'm a Server!”。将"sendJSAlert“传递给在浏览器中运行的小程序的结果是,桌面应用程序打印null,这表明由于某种原因,ServerSocket的输入流是空的,而浏览器本身在应该生成一个带有文本"Hello Client,I'm a Server!”的javascript警告框时什么也不做。我使用的浏览器是Google Chrome,目前我只是在本地机器上运行所有东西(例如,还没有远程服务器参与)

下面是相关的Java代码和HTML:

SocketClient.java

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
<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输入,然后按下Test Socket Connection按钮,这将调用sockPuppet() javascript。这将创建一个ServerSocket并将其绑定到指定的端口,然后我通过SocketClient.listenSocket将我的桌面客户端应用程序单独连接到该端口。在桌面应用程序上下文中,Eclipse的结果是"awesome“,而在appletviewer上下文中,结果是输出"sendJSAlert remotely,with message: Hello Client,I'm a Server!”。调用sendJSAlert()的webapp应用程序应该对同一消息调用javascript alert函数,并创建一个弹出框,其中包含消息"sendJSAlert remotely,with message : Hello Client,I'm a Server!“但是在浏览器( Chrome java或javascript调试控制台)中什么也没有发生,桌面应用程序的输出是空的,而不是像预期的那样“棒极了的sendJSAlert”

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

代码语言:javascript
运行
复制
grant {
 permission java.net.SocketPermission
    "localhost:1024-", 
 "accept, connect, listen, resolve";
};

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

任何帮助都将不胜感激,谢谢!

-CCJ

更新:好的,一些新的信息:我在打开了javascript控制台的情况下,在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安全策略文件中

代码语言:javascript
运行
复制
//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";
};

我还没有给applet签名,但我的理解是,如果策略文件是有序的,applet就不需要签名……如果我说错了,请告诉我。无论如何,有没有人有任何建议,为什么在策略文件具有上述权限的情况下仍会抛出此安全异常?JRE查找的工作目录中的策略文件是否有命名约定?我的工作目录策略文件现在只被命名为ccmPolFile,但是我不清楚JRE应该如何定位它;我是否需要在applet代码中添加一些东西,以便将JRE指向预期的工作目录策略文件?此外,我添加的系统策略文件授权本身不应该足以满足CCM.jar中我的小程序的套接字权限吗?

更新2:我对小程序进行了签名,并在我的java.security文件中的${java.home}/lib/security中添加了policy.url.3=file:${user.home}\Desktop\dev\invention\ATT\MapViz\ccmPolFile.policy行(通过http://docs.oracle.com/javase/tutorial/security/tour2/step4.html#Approach2,这显然是JRE定位要加载的策略文件的方式)……可悲的是,结果是完全相同的安全异常。我所知道的唯一一件事是

代码语言:javascript
运行
复制
AccessController.doPrivileged(new PrivilegedAction() {
    public Object run() {
        // perform the security-sensitive operation here
        return null;
    }
});

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2012-07-20 05:08:07

好的,所以在上面的更新2之后,我将SocketServer.java代码中的listenSocket()方法改为

代码语言:javascript
运行
复制
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 alert函数没有触发,但我认为这与上面listenSocket()中可怕的无限while循环有关……

带回家的消息:出于某些原因,为了从google chrome中的小程序建立套接字连接,我需要对小程序进行签名,并使用AccessController.doPrivileged()来调用我的安全敏感代码,尽管我已经设置了本地策略和安全文件来授予我的小程序这些权限。

googlers用户可查看参考文献:

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()方法改成了这样:

代码语言:javascript
运行
复制
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函数。由于端口仍然是硬编码的,并且小程序使用doPrivileged(...)这仍然不是一个很好的解决方案,但它确实满足了通过java applet桥从桌面java应用程序调用web浏览器中的javascript的用例,因此它为更健壮的实现提供了一个良好的跳板!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11552980

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档