我有一个 Eclipse 插件,需要打开一对到本地计算机上运行的 Flash 应用程序的套接字。 Flash 需要一个策略文件(XML blob)来授予访问相关端口的权限。 Flash 更喜欢通过端口 843 获取此策略文件,Java 将端口 < 1024 视为特权端口,Mac OS X 和 Linux 类似地限制对端口 < 1024 的访问。我不想以 root 权限运行我的 Eclipse 插件,因此提供服务端口 843 上的策略文件不是一个选项。根据 Adobe 文档,如果 Flash 无法在端口 843 上获取策略文件,它将转而请求其尝试连接的端口上的策略文件。 ActionScript 代码如下所示:
/**
* Connecting to some port to communicate with the debugger. We initiate the
* connection because Flex doesn't allow us to listen to any ports.
*/
private function initSockets():void
{
requestSocket = new Socket();
requestSocket.addEventListener(Event.CONNECT, requestConnected);
requestSocket.addEventListener(Event.CLOSE, closed);
requestSocket.addEventListener(ProgressEvent.SOCKET_DATA, processRequestData);
requestSocket.addEventListener(IOErrorEvent.IO_ERROR, ioError);
requestSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
requestSocket.connect("localhost", SCConstants.DEBUG_LESSON_REQUEST_PORT);
eventSocket = new Socket();
eventSocket.addEventListener(Event.CONNECT, eventConnected);
eventSocket.addEventListener(Event.CLOSE, closed);
eventSocket.addEventListener(ProgressEvent.SOCKET_DATA, processEventData);
eventSocket.addEventListener(IOErrorEvent.IO_ERROR, ioError);
eventSocket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityError);
eventSocket.connect("localhost", SCConstants.DEBUG_LESSON_EVENT_PORT);
}
在 Eclipse 插件方面,我继承了一些在 OS X 上大部分时间都可以运行的代码,但有时在 Windows 上会失败。在 Wi-Fi 而不是有线以太网上运行也往往会失败,尽管我不知道为什么这很重要。
public Boolean connect() throws DebugException {
try {
try {
// connection code
fRequestServerSocket = new ServerSocket(requestPort);
fRequestServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
fEventServerSocket = new ServerSocket(eventPort);
fEventServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
TWBLogger.logInfo("Open socket request server:" + fRequestServerSocket);
TWBLogger.logInfo("Open socket event server:" + fEventServerSocket);
String policy = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<cross-domain-policy>\n" +
"<allow-access-from domain=\"*\" to-ports=\"5000,5001\" secure=\"false\" />\n" +
"</cross-domain-policy>\0";
// Because of the Flash security policy the first thing
// that will accept on the socket will be the Flash Player
// trying to verify us. The Flash player will request security
// policy file with the following string: <policy-file-request/>\0
// We will serve back the above policy file and then close the socket
// The next thing to accept is our process in the VM.
fRequestSocket = fRequestServerSocket.accept();
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// Wait some time before giving flash the policy file. Otherwise they don't get it. ;(
// 3 is too much ... ;(
Thread.sleep(100);
fRequestWriter.print(policy);
fRequestWriter.flush();
fRequestSocket.close();
// this should be the real connection
fRequestSocket = fRequestServerSocket.accept();
TWBLogger.logInfo("Open socket request:" + fRequestSocket);
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// the same situation for the EventSocket
fEventSocket = fEventServerSocket.accept();
fEventReader = new BufferedReader(new InputStreamReader(fEventSocket.getInputStream()));
TWBLogger.logInfo("Open socket event:" + fEventSocket);
} catch (SocketTimeoutException e) {
TWBLogger.logWaring("Connection to the Client Timed out.");
cleanSockets();
return false;
requestFailed("Connection to the VM timed out. Please close any other running lessons that you debug and try again", e);
} catch (SocketSecurityException e) {
requestFailed("Security error occured when connecting to the VM", e);
} catch (Exception e) {
if (!fTerminated)
requestFailed("Error occured when connecting to the VM. Please close any other running lessons that you debug.", e);
}
} catch (DebugException e) {
// close the sockets so that we can debug another application
cleanSockets();
throw e;
}
// our VM is single threaded
fThread = new TWBThread(this);
fThreads = new IThread[] {fThread};
// start listening for events from the VM
fEventDispatch = new EventDispatchJob();
fEventDispatch.schedule();
// start listening for breakpoints
IBreakpointManager breakpointManager = getBreakpointManager();
breakpointManager.addBreakpointListener(this);
breakpointManager.addBreakpointManagerListener(this);
return true;
}
这段代码看起来有问题。它不会等待来自 Flash 的消息,而是将策略响应塞入端口。正如我所说,它在大多数情况下都有效,但有时会失败,并且似乎不符合 Adobe 的文档。
我尝试监听每个端口上的请求数据包并发送特定于端口的响应。我在环回接口(interface) (Mac OS X) 上使用 WireShark 观察套接字流量。我看到传入的策略请求和发送的响应,但 Flash 仍然在两个端口上给我带来安全沙箱违规。
我还尝试在上面所示的 initSockets 开头添加此行:
Security.loadPolicyFile("xmlsocket://localhost:5002");
然后我在插件中添加了代码以监听端口 5002 并发送以下主策略文件内容:
private final static String FLASH_POLICY_RESPONSE =
"<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\n" +
"<cross-domain-policy>\n" +
"<site-control permitted-cross-domain-policies=\"master-only\"/>\n" +
"<allow-access-from domain=\"*\" to-ports=\"5000,5001\"/>\n" +
"</cross-domain-policy>\0";
我再次看到请求进来并且响应出去,但是Flash似乎没有响应它。我没有收到安全沙箱违规错误,但端口上也没有流量。
谁能告诉我在 Java 和 Flash 之间打开套接字的正确方法吗?
最佳答案
我找到了解决这个问题的方法。我很早就犯了一个错误,使用 BufferedReader.readLine 来读取策略请求。这是不合适的,因为策略请求是以空值终止的,而不是以新行终止的。这很令人困惑,因为当底层流关闭时它确实会返回。因此,我收到了请求并发送了响应,但响应是在 ActionScript 代码已经确定请求失败之后发送的。
在 Java 端,我使用以下代码在端口上建立通信:
// Create server sockets.
fRequestServerSocket = new ServerSocket(REQUEST_PORT);
fRequestServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
TWBLogger.logInfo("Open socket request server:" + fRequestServerSocket);
fEventServerSocket = new ServerSocket(EVENT_PORT);
fEventServerSocket.setSoTimeout(ACCEPT_TIMEOUT);
TWBLogger.logInfo("Open socket event server:" + fEventServerSocket);
// Serve up the Flash policy file.
serveFlashPolicy();
// Connect request socket.
fRequestSocket = fRequestServerSocket.accept();
TWBLogger.logInfo("Open socket request:" + fRequestSocket);
fRequestWriter = new PrintWriter(fRequestSocket.getOutputStream());
fRequestReader = new BufferedReader(new InputStreamReader(fRequestSocket.getInputStream()));
// Connect event socket.
fEventSocket = fEventServerSocket.accept();
TWBLogger.logInfo("Open socket event:" + fEventSocket);
fEventReader = new BufferedReader(new InputStreamReader(fEventSocket.getInputStream()));
提供策略文件的处理方式如下:
private void serveFlashPolicy() {
ServerSocket serverSocket = null;
Socket socket = null;
TWBLogger.logInfo("Waiting for flash policy request on port " + FLASH_POLICY_PORT);
try {
serverSocket = new ServerSocket(FLASH_POLICY_PORT);
serverSocket.setSoTimeout(ACCEPT_TIMEOUT);
socket = serverSocket.accept();
PrintWriter writer = new PrintWriter(socket.getOutputStream());
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
StringBuilder request = new StringBuilder();
int c;
while (0 < (c = reader.read())) {
request.append((char) c);
}
String policyRequest = request.toString();
if (policyRequest.startsWith(FLASH_POLICY_REQUEST)) {
writer.print(FLASH_POLICY_RESPONSE);
writer.print("\0");
writer.flush();
}
} catch (IOException e) {
TWBLogger.logWaring("IOException on port " + FLASH_POLICY_PORT + ": " + e.toString());
e.printStackTrace();
} finally {
if (null != socket) {
try {
socket.close();
} catch (Exception e) {
// Ignore
}
}
if (null != serverSocket) {
try {
serverSocket.close();
} catch (Exception e) {
// Ignore
}
}
}
TWBLogger.logInfo("Flash policy complete on port " + FLASH_POLICY_PORT);
}
Flash 政策响应如下所示:
private final static String FLASH_POLICY_RESPONSE =
"<?xml version=\"1.0\"?>\n" +
"<!DOCTYPE cross-domain-policy SYSTEM \"/xml/dtds/cross-domain-policy.dtd\">\n" +
"<cross-domain-policy>\n" +
"<allow-access-from domain=\"*\" to-ports=\"5000,5001\"/>\n" +
"</cross-domain-policy>";
我之前发送的 site-control
标记仅允许在从端口 843 提供服务的主策略文件中使用。
关于java - 如何从 Eclipse 插件提供 Flash 策略文件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13772889/