我是网络 I/O 编程的新手,我遇到了一个障碍——基本上我想做的是让桌面应用程序与 google maps javascript API 对话。为了促进这一点,我构建了一个 java 小程序,它将充当桌面应用程序和浏览器 javascript 应用程序之间的桥梁。当我在 Eclipse 中一起运行桌面应用程序和小程序时,它们可以完美通信,并且我能够通过将字符串写入绑定到小程序已建立 ServerSocket 连接的同一端口的套接字来调用小程序函数。为了在 Eclipse 中进行测试,我将字符串“sendJSAlertTest”发送到套接字的输出流,然后使用 java.lang.reflect API 从 ServerSocket 输入流派生一个 Method 实例,最后在 applet 中调用生成的方法。当小程序在浏览器中运行时,我将“sendJSAlert”写入套接字,因为它会导致实际调用 javascript。在 Eclipse 中使用 appletviewer 的结果是桌面应用程序上下文打印输出“awesome sendJSAlert”,而applet 上下文打印来自 sendJSAlertTest() 方法的输出“Hello Client,我是服务器!”。将“sendJSAlert”传递给在浏览器中运行的小程序的结果是桌面应用程序打印 null,这表明由于某种原因 ServerSocket 的输入流为空,并且浏览器本身在它应该生成一个 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 的 Test Socket Connection 按钮。这应该创建一个 ServerSocket 并将其绑定到指定的端口,然后我通过 SocketClient.listenSocket 将我的桌面客户端应用程序单独连接到该端口。Eclipse 在桌面应用程序上下文中的结果是“很棒的 sendJSAlertTest”,而在 appletviewer 上下文中是输出“远程调用 sendJSAlert,带有消息:Hello Client,我是服务器!”。调用 sendJSAlert() 的 web 应用程序应该在同一条消息上调用 javascript 警报函数,创建一个弹出框,其中包含消息“sendJSAlert 远程调用,消息:Hello Client,我是服务器!”
所以问题是:不同结果的原因可能是什么?我知道浏览器的安全沙箱可能是一个问题,但我已经包含了一个权限文件,它应该允许通过任何 localhost 端口上的套接字进行通信:
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页面和包含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 中的小程序的套接字权限?
更新 2:我签署了 applet 并将行 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;
}
});
自从小程序现在签名以来,这应该让我几乎可以做任何事情。我想继续退出等式,但策略文件由于某种原因无法正常工作。我很快就会回来看看结果如何