这个系列的前两篇文章主要是根据自己的需求,对Thunderbird的源代码进行修改,改进了Thunderbird的现有功能,关注点都在Thunderbird的老本行---邮件客户端的实现上,那是否Thunderbird就仅仅是一个邮件客户端呢?在我看来,并非如此,它源自Mozilla内核,就继承了Mozilla平台的光荣传统,应该视为一个优秀的可扩展的开发平台,更进一步来看,Mozilla的文化深入其骨髓可以看到后来Adobe的Flex,MicroSoft的WPF都吸收了Mozilla平台界面与逻辑相分离的思想,所以接下来几篇文章我想写一个比较有意思的方面----进程间通信。
进程间通信的概念在操作系统中有过详细的介绍,方法很多,我主要关注两种:socket通信,Pipe(管道)通信。
本文的目的就是开发一个扩展,展示TCP/IP socket技术在Mozilla扩展开发中的应用。
这个系列的前两篇文章主要是根据自己的需求,对Thunderbird的源代码进行修改,改进了Thunderbird的现有功能,关注点都在Thunderbird的老本行---邮件客户端的实现上,那是否Thunderbird就仅仅是一个邮件客户端呢?在我看来,并非如此,它源自Mozilla内核,就继承了Mozilla平台的光荣传统,应该视为一个优秀的可扩展的开发平台,更进一步来看,Mozilla的文化深入其骨髓,可以看到后来Adobe的Flex,MicroSoft的WPF都吸收了Mozilla平台界面与逻辑相分离的思想,所以接下来几篇文章我想写一个比较有意思的方面----进程间通信。
进程间通信的概念在操作系统中有过详细的介绍,方法很多,我主要关注其中两种:socket通信,Pipe(管道)通信。
本文的目的就是开发一个扩展,展示TCP/IP socket技术在Mozilla扩展开发中的应用。
服务器端主代码:
const tBirdBiffServerUi =
{
tBirdBiffServerOnLoad: function()
{//启动服务器
// remove to avoid duplicate initialization
removeEventListener("load", tBirdBiffServerUi.tBirdBiffServerOnLoad, true);
tBirdBiffCommon.setIconPosition();//设置图标位置
//创建服务器对象并初始化
var server = Components.classes["@phinecos.cnblogs.com/TBbiff/server;1"].getService(Components.interfaces.nsISupports).wrappedJSObject;
server.initialize();
server.addWindow(window);//保存当前窗口
server = null;
},
tBirdBiffServerOnClose: function()
{//关闭服务器
// remove to avoid duplicate initialization
removeEventListener("close", tBirdBiffServerUi.tBirdBiffServerOnClose, true);
//移除当前窗口
var server = Components.classes["@dpwhite.com/thunderbirdbiff/server;1"].getService(Components.interfaces.nsISupports).wrappedJSObject;
server.removeWindow(window);
server = null;
}
}
addEventListener("load", tBirdBiffServerUi.tBirdBiffServerOnLoad, true);
addEventListener("close", tBirdBiffServerUi.tBirdBiffServerOnClose, true);
服务器类,负责创建服务器端socket,并异步监听来自客户端的请求,管理邮箱状态的变化和来自客户端的连接。
服务器类
const CI = Components.interfaces, CC = Components.classes, CR = Components.results;
const newMail= "1";
const noMail = "0";
const serverError = "9";
tBirdBiffServer.classID = Components.ID("{d2c9b4c6-2851-4d25-8cb6-3d3b037f8e1e}");//组件ID
tBirdBiffServer.contractID = "@phinecos.cnblogs.com/TBbiff/server;1";
tBirdBiffServer.classDescription = "TBbiff Server Service";
function tBirdBiffServer()
{
this.utility = CC[utilityContractID].getService(CI.nsISupports).wrappedJSObject;//工具类对象
this.prefs = CC["@mozilla.org/preferences-service;1"].getService(CI.nsIPrefBranch);
this.connections = CC["@mozilla.org/supports-array;1"].createInstance(CI.nsICollection);//客户端连接集合
this.useAnimation = null;//是否使用动画效果
this.mailStatus = noMail;//邮箱状态
this.serverSocket = null;//服务器端socket
this.port = null;//服务器端口
this.windowCollection = CC["@mozilla.org/supports-array;1"].createInstance(CI.nsICollection);//保存的窗口集合
this.initialized = false;//是否已经初始化
}
broadcast: function()
{//向客户端发送数据
var deadConnections = new Array();//已断开的连接
var status = this.mailStatus;//获取当前邮箱状态
var count = this.connections.Count();//来自客户端的连接数目
//依次向各个客户端发送服务器的邮箱状态
for(var i = 0; i < count; i++)
{
var connection = this.connections.GetElementAt(i);//来自客户端的连接
connection = connection.wrappedJSObject;
//发送数据给客户端
if(!connection.broadcast(status))
{
connection.closeSocket();//关闭此断开的连接
deadConnections[i] = connection;
}
else
{
deadConnections[i] = null;
}
connection = null;
}
for(var i = 0; i < deadConnections.length; i++)
{//移除已经断开的连接
if(deadConnections[i] != null)
{
this.removeConnection(deadConnections[i]);
}
}
deadConnections = null;
count = null;
},
addConnection: function(value)
{//加入新的来自客户端的连接
this.connections.AppendElement(value);
},
removeConnection: function(value)
{//移除连接
this.connections.RemoveElement(value);
},
getServerSocket: function()
{//创建服务器端socket,并开始异步监听来自客户端的request
this.serverSocket = CC["@mozilla.org/network/server-socket;1"].createInstance(CI.nsIServerSocket);
if(!this.serverSocket)
{
alert("Unable to get a server socket");
}
else
{
try
{
this.serverSocket.init(this.port, false, -1);//初始化服务器端socket,绑定到端口号port
this.serverSocket.asyncListen(tBirdBiffServerSocketListener);//开始异步监听来自客户端的请求
}
catch(e)
{
this.serverSocket = null;
}
}
},
closeServerSocket: function()
{//关闭服务器端socket
if(!this.serverSocket)
{
this.serverSocket.close(null);
this.serverSocket = null;
}
},
initialize: function()
{//初始化服务器
if(this.initialized)
{//已经初始化过了
return;
}
this.port = 25501;//设置服务器监听端口
this.useAnimation = true;
this.getServerSocket();//创建服务器端socket并开始监听
this.monitorBiff(true);//监控状态变化
this.initialized = true;
},
monitorBiff: function(isStarting)
{//监控邮箱状态变化
var sessionService = CC["@mozilla.org/messenger/services/session;1"].getService(CI.nsIMsgMailSession);
if(isStarting)
{
sessionService.AddFolderListener(tBirdBiffServerBiffStateListener, CI.nsIFolderListener.intPropertyChanged);//增加监听者
}
else
{
sessionService.RemoveFolderListener(tBirdBiffServerBiffStateListener, CI.nsIFolderListener.intPropertyChanged);//移除监听者
}
sessionService = null;
},
closeAllConnections: function()
{//关闭所有来自客户端的连接
var count = this.connections.Count();
for(var i = 0; i < count; i++)
{
var connection = this.connections.GetElementAt(i);
connection = connection.wrappedJSObject;
connection.closeSocket();
connection = null;
}
count = null;
this.connections.Clear();
},
finalize: function()
{//析构函数
if(!this.initialized)
{
return;
}
this.closeServerSocket();//关闭服务器端socket
this.monitorBiff(false);//移除监听邮箱状态的监听者
this.closeAllConnections();//关闭所有来自客户端的连接
},
updateUi: function(window)
{//刷新界面状态
var state;
var tip = "T-Bird Biff: ";
var status = window.document.getElementById("thunderbird-biff");
switch(this.mailStatus)
{
case noMail:
{//没有新邮件
tip += this.getLocalizedString("noNewMail");
state = "noMail";
break;
}
case newMail:
{//有新邮件
tip += this.getLocalizedString("newMail");
if(this.useAnimation)
{
state = "newMailAni"; // using gif here due to animation
}
else
{
state = "newMail";
}
break;
}
default:
{//error
this.utility.logError("Unexpected result: " + this.mailStatus);
tip = this.getLocalizedString("weirdness");
state = "weirdness";
break;
}
}
status.setAttribute("tooltiptext", tip);
status.setAttribute("biffState", state);
tip = null;
state = null;
status = null;
},
setMailStatus: function(value)
{//设置邮箱状态
if(this.MailStatus == value)
{//没有变化
return;
}
this.mailStatus = value;
//邮箱状态发生改变,逐个窗口通知其更新状态,这些窗口都是服务器端的Observer
var server = CC[tBirdBiffServer.contractID].getService(CI.nsISupports).wrappedJSObject;
var windowCollection = server.getWindowCollection();
var count = windowCollection.Count();
for(var i = 0; i < count; i++)
{
var window = windowCollection.GetElementAt(i);
this.updateUi(window);//更新此窗口状态
window = null;
}
this.broadcast();//将服务器的邮箱状态通知给各个客户端
windowCollection = null;
count = null;
server = null;
},
check: function()
{//检查服务器邮箱状态
tBirdBiffServerBiffStateListener.clearIntervalTimeout();//清除此前的定时器
this.setMailStatus(this.checkServers());//实际的检查动作
},
checkServers: function()
{
try
{
const biffShowsMailReady = 0;
//帐户管理器
var accountManager = CC["@mozilla.org/messenger/account-manager;1"].getService(CI.nsIMsgAccountManager);
if(accountManager)
{
//获取此用户的所有服务器
var servers = accountManager.allServers;
var numServers = servers.Count();
for(var i = 0; i < numServers; i++)
{
var server = servers.GetElementAt(i).QueryInterface(CI.nsIMsgIncomingServer);
if(server.rootFolder != server.rootMsgFolder)
{
continue;
}
if(server.type != "pop3" && server.type != "imap")
{
if(server == accountManager.localFoldersServer)
{
alert("tBirdBiffServer.checkServers"+server.prettyName + " appears to be Local Folders");
}
else
{
alert("Non-pop3, IMAP, or Local Folders server found. Type is " + server.type + ", name is " + server.prettyName);
continue;
}
}
if(server.biffState == biffShowsMailReady)
{//有新邮件到来
server = null;
servers = null;
numServers = null;
accountManager = null;
return newMail;
}
}
//没有新邮件
servers = null;
numServers = null;
accountManager = null;
return noMail;
}
else
{
accountManager = null;
this.utility.logError("Unable to get account manager");
return serverError;
}
}
catch (e)
{
accountManager = null;
return serverError;
}
},
来自客户端的连接对象类:
function tBirdBiffServerConnection()
{
this.wrappedJSObject = this;
}
tBirdBiffServerConnection.prototype =
{
socket: null,//客户端对应的socket
outputStream: null,//输出流
setSocket: function(value)
{//保存来自客户端的socket连接
try
{
this.outputStream = value.openOutputStream(CI.nsITransport.OPEN_BLOCKING | CI.nsITransport.OPEN_UNBUFFERED, 0, 0);//打开输出流,类型为阻塞型,无缓冲区
}
catch(e)
{
return false;
}
if(!this.outputStream)
{
return false;
}
this.socket = value;
return true;
},
closeSocket: function()
{//关闭来自客户端的socket
if(this.outputStream)
{//关闭输出流
this.outputStream.close(null);
this.outputStream = null;
}
if(this.socket)
{//关闭对应的socket
this.socket.close(null);
this.socket = null;
}
},
broadcast: function(value)
{//向客户端发送数据
if(!this.outputStream)
{
this.closeSocket();
return false;
}
try
{
this.outputStream.write(value, value.length);//发送数据
}
catch (e)
{
this.closeSocket();
return false;
}
return true;
}
}
服务器监听类,负责监听来自客户端的各个请求:
function tBirdBiffServerConnection()
{
this.wrappedJSObject = this;
}
tBirdBiffServerConnection.prototype =
{
socket: null,//客户端对应的socket
outputStream: null,//输出流
setSocket: function(value)
{//保存来自客户端的socket连接
try
{
this.outputStream = value.openOutputStream(CI.nsITransport.OPEN_BLOCKING | CI.nsITransport.OPEN_UNBUFFERED, 0, 0);//打开输出流,类型为阻塞型,无缓冲区
}
catch(e)
{
return false;
}
if(!this.outputStream)
{
return false;
}
this.socket = value;
return true;
},
closeSocket: function()
{//关闭来自客户端的socket
if(this.outputStream)
{//关闭输出流
this.outputStream.close(null);
this.outputStream = null;
}
if(this.socket)
{//关闭对应的socket
this.socket.close(null);
this.socket = null;
}
},
broadcast: function(value)
{//向客户端发送数据
if(!this.outputStream)
{
this.closeSocket();
return false;
}
try
{
this.outputStream.write(value, value.length);//发送数据
}
catch (e)
{
this.closeSocket();
return false;
}
return true;
}
}
const tBirdBiffServerSocketListener =
{
onSocketAccepted: function(serverSocket, clientSocket)
{//接受来自客户端的请求
var connection = new tBirdBiffServerConnection();//新建一个连接对象
//保存当前接收的连接
if(connection.setSocket(clientSocket))
{
var server = CC[tBirdBiffServer.contractID].getService(CI.nsISupports).wrappedJSObject;
//向客户端发送数据
if(connection.broadcast(server.getMailStatus()))
{
server.addConnection(connection);//保存连接对象到在线连接集合中
}
else
{
alert("connection NOT added");
}
server = null;
}
else
{
alert("Creating connection failed");
}
connection = null;
},
onStopListening: function(serverSocket, status)
{//服务器停止监听
alert("Server socket has stopped listening");
}
}
服务器邮箱状态监听者,负责监视邮箱的状态变化:
const tBirdBiffServerBiffStateListener =
{
timer: null,//定时器,负责定时检查邮箱状态
clearIntervalTimeout: function()
{//清除定时器
if(this.timer)
{
this.timer = CC["@mozilla.org/timer;1"].getService(CI.nsITimer);
this.timer.cancel();
this.timer = null;
}
else
{
alert("Timer is null");
}
},
OnItemIntPropertyChanged: function(item, property, oldValue, newValue)
{//参见Thunderbird源代码,此函数负责监视各个文件夹的属性变化,当有新邮件到来时,property的值为“BiffState”
if(property.toString() != "BiffState")
{
return;
}
this.clearIntervalTimeout();
//启动一个定时器
this.timer = CC["@mozilla.org/timer;1"].getService(CI.nsITimer);
this.timer.initWithCallback(tBirdBiffServerCheckCallback, 1000, this.timer.TYPE_ONE_SHOT);
}
}
实际的检查邮箱状态的处理过程放在tBirdBiffServerCheckCallback函数中。
const tBirdBiffServerCheckCallback =
{//定时检查邮箱状态的处理函数
notify: function(timer)
{
var server = CC[tBirdBiffServer.contractID].getService(CI.nsISupports).wrappedJSObject;
server.check();//检查邮箱状态
server = null;
}
}
Ok,本文用javascript,遵循XPCOM规范实现了一个简单的TCP服务器,服务器类型为阻塞式I/O,客户端代码将在下一篇文章中介绍。
Reference:
1, https://addons.mozilla.org/en-US/thunderbird/addon/3788