我试图在两个或多个插件之间实现客户端 - 服务器通信,其中每个插件同时是服务器和客户端。我使用nsIServerSocket作为客户端部分的服务器部分和websockets。这是代码:
function startServer(port) {
var listener = {
onSocketAccepted: function(serverSocket, transport) {
console.log("Accepted connection on " + transport.host + ":" + transport.port);
var input = transport.openInputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0);//.QueryInterface(Ci.nsIAsyncInputStream);
var output = transport.openOutputStream(Ci.nsITransport.OPEN_BLOCKING, 0, 0);
var sin = Cc["@mozilla.org/scriptableinputstream;1"].createInstance(Ci.nsIScriptableInputStream);
try{
sin.init(input);
var readBytes = sin.available();
var request = '';
request = sin.read(readBytes);
console.log('Received: ' + request);
//getUrl(request);
output.write("yes", "yes".length);
output.flush();
}
finally{
sin.close();
input.close();
output.close();
}
}
}
try{
var serverSocket = Cc["@mozilla.org/network/server-socket;1"].createInstance(Ci.nsIServerSocket);
serverSocket.init(port, true, 5);
console.log("Opened socket on " + serverSocket.port);
serverSocket.asyncListen(listener);
}catch(e){
console.log(e);
}
}
对于服务器部分,以及客户端部分的以下内容:
var address="ws://otherAddress:1234";// + port;
var window = Cc["@mozilla.org/appshell/appShellService;1"]
.getService(Ci.nsIAppShellService)
.hiddenDOMWindow;
ws = new window.WebSocket(address);
try{
ws.onmessage = function () {
};
ws.onopen = function(){
console.log("connection opened");
// Web Socket is connected. You can send data by send() method
ws.send("lol ");
};
ws.onclose = function() {
// websocket is closed. };
console.log("websocket is closed");
}
}catch(evt){
console.log(evt.data);
}
客户端代码在用户点击按钮时启动....此代码部分正常工作,因为从控制台我看到,当用户点击按钮时,服务器收到连接打开,但我无法接收消息......任何人都可以帮助我?感谢
更新1
我在控制台中看到的消息是这样的:
"Received: GET / HTTP/1.1
Host: localhost:1234
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:27.0) Gecko/20100101 Firefox/27.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: it-IT,it;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Sec-WebSocket-Version: 13
Origin: resource://gre-resources
Sec-WebSocket-Key: zh/EpJRRsOAgLfPIbI1EDg==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
更新2 在nmaier和IvyLynx回答之后(非常感谢!!),我修改了我的代码,插入了一个完整的" ServerSocket"实现(主要是因为将来我也会传递二进制数据)。这是localhost案例的代码:
var {Cc, Ci, Cu, Cr, components} = require("chrome");
// the thread manager can be important when using asynchronous mode
var thread_manager = Cc["@mozilla.org/thread-manager;1"].getService();
var socket_service = Cc["@mozilla.org/network/socket-transportservice;1"].getService(Ci.nsISocketTransportService);
// make some constructors so we don't have to worry about this later
var socket = Cc["@mozilla.org/network/serversocket;1"].createInstance(Ci.nsIServerSocket);
// set the second argument to false if you want it to listen
// to connections beyond the computer the extension runs on
socket.init(-1, true, -1);
var output_stream_bin = Cc["@mozilla.org/binaryoutputstream;1"].createInstance(Ci.nsIBinaryOutputStream);
var input_stream_bin = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
// this is so we can easily instantiate nsIInputStreamPump, which allows us to read input streams properly
var input_stream_pump_c = Cc["@mozilla.org/network/input-stream-pump;1"];
var input_stream_base, input_stream_async_c, input_stream_async, recieved_bytes, recieved_total, input_stream_pump;
var output_stream_base, output_stream_async_c, output_stream_async, generalStream;
var client, client_input_stream, client_output_stream, client_input_stream_pump;
var data_to_send = "hi hi"; // this holds what we want to send
var socket_transport = socket_service.createTransport(null, 0, "localhost", socket.port, null);
var socket_listener = {
onSocketAccepted: function(socket, transport){
client = transport;
client_input_stream = client.openInputStream(0, 0, 0);
client_output_stream = client.openOutputStream(0, 0, 0);
client_output_stream.QueryInterface(Ci.nsIAsyncOutputStream);
generalStream = client_output_stream;
client_input_stream_pump[this_transport] = input_stream_pump_c.createInstance(Ci.nsIInputStreamPump);
client_input_stream_pump[this_transport].init(client_input_stream, -1, -1, 0, 0, false);
client_input_stream_pump[this_transport].asyncRead(socket_reader, socket);
},
onStopListening: function(socket, status){
}
};
socket.asyncListen(socket_listener);
// this guy will get called when we're ready to send data
var output_stream_callback = {
onOutputStreamReady: function(stream){
output_stream_bin.setOutputStream(stream);
output_stream_bin.writeBytes(data_to_send, data_to_send.length);
data_to_send = "";
}
};
var socket_reader = {
onDataAvailable: function(request, context, stream, offset, count){
input_stream_bin.setInputStream(stream);
if(input_stream_bin.available() > 0){
recieved_bytes = input_stream_bin.readByteArray(count);
recieved_total = "";
// this loop converts bytes to characters
// if you don't need to pass binary data around
// you can just use nsIScriptableInputStream instead of
// nsIBinaryInputStream and skip this
for (var i = 0; i < recieved_bytes.length; i++){
recieved_total += String.fromCharCode(recieved_bytes[i]);
}
console.log("Received " + recieved_total)
}else{
stream.close();
}
},
onStartRequest: function(request, context){
},
onStopRequest: function(request, context, status){
}
};
require("sdk/widget").Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: data.url("icon.png"),
onClick: listTabs
});
function listTabs() {
//console.log(client_output_stream);
generalStream.asyncWait(output_stream_callback,0,0,thread_manager.mainThread);
};
问题是generalStream变量。当用户点击扩展图标时我调用asyncWait方法,但我也用其他方法插入调用。每个generalStream.asyncWait都会引发以下问题(实际上,在执行扩展时,实际存在配置文件的路径):
console.error: client:
Message: TypeError: generalStream is undefined
Stack:
listTabs@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2NP
aadiTkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/lo
ader.js -> resource://jid1-exo2npaaditkqg-at-jetpack/client/lib/main.js:742
_emitOnObject@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2N PaadiTkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/l
oader.js -> resource://gre/modules/commonjs/sdk/deprecated/events.js:153
_emit@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2NPaadiTkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/deprecated/events.js:123 _onEvent@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2NPaadi
Tkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/widget.js:278
WidgetView__onEvent@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2NPaadiTkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/widget.js:426
WC_addEventHandlers/listener/<@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2NPaadiTkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/widget.js:884
notify@resource://gre/modules/XPIProvider.jsm -> jar:file:///.../extensions/jid1-exo2NPaadiTkqg@jetpack.xpi!/bootstrap.js -> resource://gre/modules/commonjs/toolkit/loader.js -> resource://gre/modules/commonjs/sdk/timers.js:40
答案 0 :(得分:4)
nmaier很可能是正确的,为什么你的代码不起作用,但我仍然会将这个答案作为补充信息发布
首先,您不需要同时使用WebSockets和XPCOM套接字来创建服务器和客户端的扩展。这些技术中的任何一种都足以满足此目的。就个人而言,自己做了这个,我建议使用XPCOM套接字,除非你真的想在浏览器之间创建可移植的代码(因为你没有提到类似的东西,我建议删除WebSockets实现并坚持使用XPCOM套接字 - 但WebSockets更容易处理,所以你可能更喜欢它们 - 没关系,只需选择一个)。我提到这个,因为你有WebSocket和XPCOM Socket都在监听连接,我认为这是因为你想要这个:
ServerSocket接收客户端连接 - &gt;仅接收来自客户端的连接
WebSocket连接到服务器连接 - &gt;仅发送到服务器连接
相反,您可以只有nsIServerSocket
来读取和发送数据到另一个服务器套接字。此外,正如nmaier所说,阻塞流是一个坏主意,除非你真的需要它们,在这种情况下,你不需要。如果你使用异步模式,它可能也会帮助事情更好地工作。
以下是使用nsIServerSocket
所需内容的示例实现,我将其包括在内,因为了解其工作原理会给我带来很多痛苦和时间。如果不想要本机应用程序级别tcp套接字的强大功能(换句话说,您不需要读取二进制流或进行复杂处理或编写自己的通信协议),WebSockets就是可能对你而言更合适,因为他们会处理一个简单的消息传递系统。简单地说,如果以下是不你的一杯茶,只需坚持使用WebSockets,直到它们无法满足您的需求为止。
注意:以下代码不会尝试命名空间并立即使全局命名空间崩溃 - 它不是生产代码,所以通常,您希望所有这些变量和对象都包含在您的扩展名的命名空间对象。
以下是nsIServerSocket
和二进制流的示例实现:
// these aliases will shorten the code
var {
utils: Cu,
interfaces: Ci,
classes: Cc,
results: Cr,
stack: Cs,
manager: Cm,
Exception: Ce,
Constructor: CC,
} = Components;
// get related services
// the thread manager can be important when using asynchronous mode
var thread_manager = Cc["@mozilla.org/thread-manager;1"].getService();
var socket_service = Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci.nsISocketTransportService);
// make some constructors so we don't have to worry about this later
var socket_c = CC("@mozilla.org/network/server-socket;1", "nsIServerSocket", "init");
var output_stream_bin_c = CC("@mozilla.org/binaryoutputstream;1", "nsIBinaryOutputStream", "setOutputStream");
var input_stream_bin_c = CC("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream", "setInputStream");
// this is so we can easily instantiate nsIInputStreamPump, which allows us to read
// input streams properly
var input_stream_pump_c = Cc["@mozilla.org/network/input-stream-pump;1"];
// normally all these would be placed in a global object. they're declared here
// so we can instantiate them later, but this is just a sample, not production code!
var input_stream_base, input_stream_async_c, input_stream_async, input_stream_bin, recieved_bytes, recieved_total, input_stream_pump;
var output_stream_base, output_stream_async_c, output_stream_async, output_stream_bin;
var client, client_input_stream, client_output_stream, client_input_stream_pump;
var data_to_send = ""; // this holds what we want to send
// this socket will only listen on localhost
// set the second argument to false if you want it to listen
// to connections beyond the computer the extension runs on
var socket = new socket_c(-1, true, -1);
var socket_transport = socket_service.createTransport(null, 0, "localhost", socket.port, null);
// this guy will get called when we're ready to send data
var output_stream_callback = {
onOutputStreamReady: function(stream){
output_stream_bin = new output_stream_bin_c(stream);
output_stream_bin.writeBytes(data_to_send, data_to_send.length);
data_to_send = "";
}
};
var socket_reader = {
onDataAvailable: function(request, context, stream, offset, count){
input_stream_bin = new input_stream_bin_c(stream);
if(input_stream_bin.available() > 0){
recieved_bytes = input_stream_bin.readByteArray(count);
recieved_total = ""; // this holds the stuff we get
// this loop converts bytes to characters
// if you don't need to pass binary data around
// you can just use nsIScriptableInputStream instead of
// nsIBinaryInputStream and skip this
for (var i = 0; i < recieved_bytes.length; i++){
recieved_total += String.fromCharCode(recieved_bytes[i]);
}
}else{
stream.close();
// Nothing there, closing stream.
}
},
onStartRequest: function(request, context){
},
onStopRequest: function(request, context, status){
}
};
var socket_listener = {
onSocketAccepted: function(socket, transport){
client = transport;
client_input_stream = client.openInputStream(0, 0, 0);
client_output_stream = client.openOutputStream(0, 0, 0);
client_output_stream.QueryInterface(Ci.nsIAsyncOutputStream);
client_input_stream_pump[this_transport] = input_stream_pump_c.createInstance(Ci.nsIInputStreamPump);
client_input_stream_pump[this_transport].init(client_input_stream, -1, -1, 0, 0, false);
client_input_stream_pump[this_transport].asyncRead(socket_reader, socket);
},
onStopListening: function(socket, status){
}
};
socket.asyncListen(socket_listener);
(编辑 - 此部分应放在一个函数中,只有在建立连接后才会被调用,并且仅在您想要发送数据时调用)
var stream = client_output_stream; // what stream you want to send the data to
// this is the only place where the thread_manager is required
stream.asyncWait(output_stream_callback,0,0,thread_manager.mainThread);
这个实现是完全异步,所以它应该永远不会对性能产生影响或导致问题,除非有太多的数据(我猜,我真的不认为以前性能会有问题)您的连接出现问题)或出现问题(例如从输出流回调中调用.asyncWait
)。
您的输入位于recieved_total
中,在您在连接的客户端输出流上调用data_to_send
之前,您要发送的内容将放在.asyncWait
中。请记住,这是一个示例实现,因此如果您想使用它,您需要更改它以便它使用命名空间,并且您需要为要获取或发送的任何数据添加处理函数。此外,如果您希望有多个客户端连接,则还需要单独处理(通过对象数组或其他内容)。
如果您需要有关所用组件的信息,那么MDN显然是可以实现的目标。但是,nsIInputStreamPump
由于某种原因没有页面。为此,您需要转到源idl
中的.js
实现。 mozilla lxr也是查看javascript中套接字测试实现的好地方,因为其中有一些var {Cc, Ci, Cu, Cr, components} = require("chrome");
文件用于测试实现。
编辑 -
尝试用{替换var Components = require("chrome");
var {
utils: Cu,
interfaces: Ci,
classes: Cc,
results: Cr,
stack: Cs,
manager: Cm,
Exception: Ce,
Constructor: CC,
} = Components;
然后添加
console.log(Components);
与原始代码中一样。另外,在require
行之后添加components
,以便了解您是否真正获得{{1}}对象。
答案 1 :(得分:2)
nsISocketServer
实现了一个简单的TCP /绑定服务器,但没有实现websocket协议。
nsISocketTransport
通过nsISocketTransportService
)。鉴于原始TCP套接字处理和通常是一团糟,无论如何你都需要实现一些简单的交换协议,我猜第一个在服务器套接字中实现websocket协议的选项会更容易(至少,你可以免费获得客户端实现)。
PS:阻止模式是一个坏主意,因为它会在很长一段时间内阻止UI线程。
PS:显然,某人implemented the websocket protocol already in coffee script和其他人(来自附加SDK团队)也实现了它in (what appears to be some form of :p) Javascript(尽管后者几乎不是自包含且难以阅读/喘气)。
编辑我好奇并写了a stand-alone JS code module WebSocket server,这似乎主要起作用。 :P