在 Chrome 原生消息传递中:原生应用程序实例对象的生命周期有多长?
In Chrome Native messaging: how long is the native app instance object lifetime?
与 Chrome 扩展对话的本机应用程序是否永远存在?我的意思是它应该存在,例如在回发发生之前?我找不到要在清单文件中添加的任何配置。
我有一个页面,页面上有一些对象。单击一个对象并使用本机应用程序 sending/receving 一条消息后,它不再适用于其余对象。
我的确切问题:本机应用程序实例对象的生命周期有多长?该对象是否应该响应,例如永远?或者我需要一个循环,例如如果这是连续通信,要从 stdin 读取消息吗?
这是我的后台脚本:
var host_name = "files.mffta.java.nativeapp";
var port = null;
initPort();
function initPort() {
console.log( 'Connecting to native host: ' + host_name );
port = chrome.runtime.connectNative( host_name );
port.onMessage.addListener( onNativeMessage );
port.onDisconnect.addListener( onDisconnected );
}
// Listen for messages that come from the content script.
chrome.runtime.onMessage.addListener(
function( messageData, sender, sendResponse ) {
if( messageData ) {
sendNativeMessage(messageData);
sendResponse( { res: 'done!' } );
}
} );
// Sending a message to the port.
function sendNativeMessage(messageData) {
if( port == null )
initPort();
console.log( 'Sending message to native app: ' + JSON.stringify( messageData ) );
port.postMessage( messageData );
console.log( 'Sent message to native app.' );
}
// Receiving a message back from the Native Client API.
function onNativeMessage( message ) {
console.log( 'recieved message from native app: ' + JSON.stringify( message ) );
alert( "messaged received from Native: " + JSON.stringify( message ) );
//sending a message to Content Script to call a function
if( message.methodName && message.methodName != "" ) {
chrome.tabs.query( { active: true, currentWindow: true }, function( tabs ) {
chrome.tabs.sendMessage( tabs[0].id, message, function( response ) {
// Call native again to return JavaScript callback function results
alert ("calc res received by extension : " + response);
sendNativeMessage({ type: "JSCallbackRes", callbackRes: response });
} );
} );
}
}
// Disconnecting the port.
function onDisconnected() {
console.log( "ERROR: " + JSON.stringify( chrome.runtime.lastError ) );
console.log( 'disconnected from native app.' );
port = null;
}
我的扩展清单:
{
"name": "Files.ChromeExt.Operarations",
"version": "1.0",
"manifest_version": 2,
"description": "This extension calls a Native API which that API calls some x-Files related operations.",
"icons": {
"128": "x-files_icon.png"
},
"permissions": [
"nativeMessaging", "activeTab"
],
"background": {
"persistent": true,
"scripts": ["main.js"]
},
"content_scripts" : [{"matches": ["http://localhost/*","https://localhost/*"],
"js": ["contentscripts/page.js"]}]
}
Java 程序: [如评论中所问]
import java.io.IOException;
import javax.swing.JOptionPane;
public class Applet {
public Applet(){}
public static void main(String[] args) {
try {
readMessage();
sendMessage("{\"msg\" : \"hello\"}");
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage());
}
}
public static String readMessage() {
String msg = "";
try {
int c, t = 0;
for (int i = 0; i <= 3; i++) {
t += Math.pow(256.0f, i) * System.in.read();
}
for (int i = 0; i < t; i++) {
c = System.in.read();
msg += (char) c;
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "error in reading message from JS");
}
return msg;
}
public static void sendMessage(String msgdata) {
try {
int dataLength = msgdata.length();
System.out.write((byte) (dataLength & 0xFF));
System.out.write((byte) ((dataLength >> 8) & 0xFF));
System.out.write((byte) ((dataLength >> 16) & 0xFF));
System.out.write((byte) ((dataLength >> 24) & 0xFF));
// Writing the message itself
System.out.write(msgdata.getBytes());
System.out.flush();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "error in sending message to JS");
}
}
}
通过检查 chrome 日志,我可以看到这些消息:
- Native Messaging 主机尝试发送一条长度为 1936028240 字节的消息。
- {"message":"Error when communicating with the native messaging host."}", 来源: chrome-extension://XXX
我正在 64 位 Win 8.0 机器上测试这些。
更新:
我现在确信,如果调用 connectNative 并且端口没有因错误而停止,主机将永远存在。因此,上述错误消息的根本原因一定不是端口生命周期。我的意思是我的通信中出现了一些错误,强制停止了端口。
非常感谢您提出的任何意见。
如果使用 chrome.runtime.sendNativeMessage
,本机应用程序将在收到消息之后和发送消息之前立即运行。由于消息的接收是异步的,因此当 sendNativeMessage
的回调被调用时,您不能假设应用程序仍然存在。
如果您想确保本机应用程序停留更长时间,请使用 chrome.runtime.connectNative
。这将创建一个端口,并且本机应用程序将一直存在,直到它退出或直到扩展在端口上调用 disconnect()
。如果您的应用程序意外提前终止,那么您很可能在执行本机消息传递协议时出错。
有关本机消息传递协议的确切格式,请查看文档:
https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-protocol
至于你的编辑,错误信息很清楚:长度无效。长度应该是系统的 本地字节顺序 (可能是 little endian or big endian)。当你得到如下偏移量差过大的arror信息时,那么有两种可能:
- 整数的字节顺序不正确,或者
- 您的输出包含一些意外字符,这会导致字节移位并导致字节位于错误的位置。
要了解您处于哪种情况,请将数字视为十六进制数。如果有很多尾随零,则表明字节顺序不正确。例如,如果您的消息长度为 59,则十六进制值为 3b
。如果字节顺序不正确,则会显示以下消息:
Native Messaging host tried sending a message that is 989855744 bytes long.
1493172224 是十六进制的 3b 00 00 00
,你可以观察到 3b
在那里,但在错误的一端(换句话说,字节顺序颠倒了)。针对您的系统解决此问题的方法是编辑您的代码,以相反的顺序打印字节。
如果号码的十六进制视图看起来与您的号码不相符,则很可能是消息长度不正确。回想一下,stdio 用于通信,因此如果您将任何其他内容(例如错误)输出到 stdout (System.out
) 而不是 stderr (System.err
),那么协议就会被违反,您的应用程序将被终止。
System.err.println(ex.getStackTrace()); // <-- OK
System.out.println("[ error -> " + ex.getMessage() + " ]"); // <-- BAD
在 Windows 上,还要检查您是否 set the mode for stdout (and stdin) to O_BINARY. Otherwise Windows will insert an extra byte before 0A
(0D 0A
), which causes all bytes to shift and the number to be incorrect. ,因此对于您的情况来说它不相关。
与 Chrome 扩展对话的本机应用程序是否永远存在?我的意思是它应该存在,例如在回发发生之前?我找不到要在清单文件中添加的任何配置。
我有一个页面,页面上有一些对象。单击一个对象并使用本机应用程序 sending/receving 一条消息后,它不再适用于其余对象。
我的确切问题:本机应用程序实例对象的生命周期有多长?该对象是否应该响应,例如永远?或者我需要一个循环,例如如果这是连续通信,要从 stdin 读取消息吗?
这是我的后台脚本:
var host_name = "files.mffta.java.nativeapp";
var port = null;
initPort();
function initPort() {
console.log( 'Connecting to native host: ' + host_name );
port = chrome.runtime.connectNative( host_name );
port.onMessage.addListener( onNativeMessage );
port.onDisconnect.addListener( onDisconnected );
}
// Listen for messages that come from the content script.
chrome.runtime.onMessage.addListener(
function( messageData, sender, sendResponse ) {
if( messageData ) {
sendNativeMessage(messageData);
sendResponse( { res: 'done!' } );
}
} );
// Sending a message to the port.
function sendNativeMessage(messageData) {
if( port == null )
initPort();
console.log( 'Sending message to native app: ' + JSON.stringify( messageData ) );
port.postMessage( messageData );
console.log( 'Sent message to native app.' );
}
// Receiving a message back from the Native Client API.
function onNativeMessage( message ) {
console.log( 'recieved message from native app: ' + JSON.stringify( message ) );
alert( "messaged received from Native: " + JSON.stringify( message ) );
//sending a message to Content Script to call a function
if( message.methodName && message.methodName != "" ) {
chrome.tabs.query( { active: true, currentWindow: true }, function( tabs ) {
chrome.tabs.sendMessage( tabs[0].id, message, function( response ) {
// Call native again to return JavaScript callback function results
alert ("calc res received by extension : " + response);
sendNativeMessage({ type: "JSCallbackRes", callbackRes: response });
} );
} );
}
}
// Disconnecting the port.
function onDisconnected() {
console.log( "ERROR: " + JSON.stringify( chrome.runtime.lastError ) );
console.log( 'disconnected from native app.' );
port = null;
}
我的扩展清单:
{
"name": "Files.ChromeExt.Operarations",
"version": "1.0",
"manifest_version": 2,
"description": "This extension calls a Native API which that API calls some x-Files related operations.",
"icons": {
"128": "x-files_icon.png"
},
"permissions": [
"nativeMessaging", "activeTab"
],
"background": {
"persistent": true,
"scripts": ["main.js"]
},
"content_scripts" : [{"matches": ["http://localhost/*","https://localhost/*"],
"js": ["contentscripts/page.js"]}]
}
Java 程序: [如评论中所问]
import java.io.IOException;
import javax.swing.JOptionPane;
public class Applet {
public Applet(){}
public static void main(String[] args) {
try {
readMessage();
sendMessage("{\"msg\" : \"hello\"}");
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.getMessage());
}
}
public static String readMessage() {
String msg = "";
try {
int c, t = 0;
for (int i = 0; i <= 3; i++) {
t += Math.pow(256.0f, i) * System.in.read();
}
for (int i = 0; i < t; i++) {
c = System.in.read();
msg += (char) c;
}
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "error in reading message from JS");
}
return msg;
}
public static void sendMessage(String msgdata) {
try {
int dataLength = msgdata.length();
System.out.write((byte) (dataLength & 0xFF));
System.out.write((byte) ((dataLength >> 8) & 0xFF));
System.out.write((byte) ((dataLength >> 16) & 0xFF));
System.out.write((byte) ((dataLength >> 24) & 0xFF));
// Writing the message itself
System.out.write(msgdata.getBytes());
System.out.flush();
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "error in sending message to JS");
}
}
}
通过检查 chrome 日志,我可以看到这些消息:
- Native Messaging 主机尝试发送一条长度为 1936028240 字节的消息。
- {"message":"Error when communicating with the native messaging host."}", 来源: chrome-extension://XXX
我正在 64 位 Win 8.0 机器上测试这些。
更新: 我现在确信,如果调用 connectNative 并且端口没有因错误而停止,主机将永远存在。因此,上述错误消息的根本原因一定不是端口生命周期。我的意思是我的通信中出现了一些错误,强制停止了端口。
非常感谢您提出的任何意见。
如果使用 chrome.runtime.sendNativeMessage
,本机应用程序将在收到消息之后和发送消息之前立即运行。由于消息的接收是异步的,因此当 sendNativeMessage
的回调被调用时,您不能假设应用程序仍然存在。
如果您想确保本机应用程序停留更长时间,请使用 chrome.runtime.connectNative
。这将创建一个端口,并且本机应用程序将一直存在,直到它退出或直到扩展在端口上调用 disconnect()
。如果您的应用程序意外提前终止,那么您很可能在执行本机消息传递协议时出错。
有关本机消息传递协议的确切格式,请查看文档: https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-protocol
至于你的编辑,错误信息很清楚:长度无效。长度应该是系统的 本地字节顺序 (可能是 little endian or big endian)。当你得到如下偏移量差过大的arror信息时,那么有两种可能:
- 整数的字节顺序不正确,或者
- 您的输出包含一些意外字符,这会导致字节移位并导致字节位于错误的位置。
要了解您处于哪种情况,请将数字视为十六进制数。如果有很多尾随零,则表明字节顺序不正确。例如,如果您的消息长度为 59,则十六进制值为 3b
。如果字节顺序不正确,则会显示以下消息:
Native Messaging host tried sending a message that is 989855744 bytes long.
1493172224 是十六进制的 3b 00 00 00
,你可以观察到 3b
在那里,但在错误的一端(换句话说,字节顺序颠倒了)。针对您的系统解决此问题的方法是编辑您的代码,以相反的顺序打印字节。
如果号码的十六进制视图看起来与您的号码不相符,则很可能是消息长度不正确。回想一下,stdio 用于通信,因此如果您将任何其他内容(例如错误)输出到 stdout (System.out
) 而不是 stderr (System.err
),那么协议就会被违反,您的应用程序将被终止。
System.err.println(ex.getStackTrace()); // <-- OK
System.out.println("[ error -> " + ex.getMessage() + " ]"); // <-- BAD
在 Windows 上,还要检查您是否 set the mode for stdout (and stdin) to O_BINARY. Otherwise Windows will insert an extra byte before 0A
(0D 0A
), which causes all bytes to shift and the number to be incorrect.