緣由:因有多個(gè)客戶端會不定期的與服務(wù)器端建立聯(lián)系,想要區(qū)分哪個(gè)客戶端來完成相應(yīng)的處理
原版代碼:
var net = require('net');//1 引入net模塊
var chatServer = net.createServer();//創(chuàng)建net服務(wù)器
var clientList=[];//保存多個(gè)客戶端的數(shù)組
chatServer.on('connection', function (client) {//服務(wù)器連接客戶端
client.name=client.remoteAddress+':'+client.remotePort;
/*增加name屬性*/
client.write('Hi'+client.name+'!\n');
clientList.push(client);
client.on('data', function (data) {
/*添加事件監(jiān)聽器,這樣就可以訪問到連接事件所對應(yīng)的client對象,當(dāng)client發(fā)送數(shù)據(jù)給服務(wù)器時(shí),這一事件就會觸發(fā)*/
for(var i=0;i<clientList.length;i++){
if(clientList[i]!==this){
// 把數(shù)據(jù)發(fā)送給其他客戶端
clientList[i].write(this.name+"says "+data);
}
}
});
});
chatServer.listen(9000, "127.0.0.1");//服務(wù)器端口
注意:這里有個(gè)坑——如果有個(gè)客戶端斷開連接,那么所有人都會玩完!
因?yàn)槿绻偻?wù)器發(fā)送消息,這時(shí)候服務(wù)器并不知道某個(gè)客戶端已經(jīng)斷開了連接,因此會繼續(xù)向其發(fā)送數(shù)據(jù),但是這時(shí)斷開的這個(gè)客戶端對應(yīng)的socket已經(jīng)無法寫入數(shù)據(jù),而對已關(guān)閉的socket進(jìn)行write()操作node程序會拋出異常,進(jìn)而導(dǎo)致全軍覆沒。所以,這個(gè)問題應(yīng)該從兩個(gè)方面來解決:
(1)當(dāng)客戶端斷開連接時(shí),通知服務(wù)器,將其從客戶端列表中移除,防止其調(diào)用write方法(V8引擎也會把響應(yīng)的socket對象作為垃圾回收,并釋放相應(yīng)的內(nèi)存);
(2)采用更保險(xiǎn)的方式調(diào)用write()方法。
改進(jìn)如下:
最后,監(jiān)聽客戶端關(guān)閉事件,并記錄錯(cuò)誤
var net = require('net');//1 引入net模塊
var chatServer = net.createServer();//創(chuàng)建net服務(wù)器
var clientList = [];//保存多個(gè)客戶端的數(shù)組
chatServer.on('connection', function (client) {//服務(wù)器連接客戶端
// console.log(' client remoteAddress =' + client.remoteAddress);
// console.log(' client remotePort = ' + client.remotePort);
client.name = client.remoteAddress + ':' + client.remotePort;
/*增加name屬性*/
client.write('Hi' + client.name + '!\n');
// console.log(''client.name+'connected');
clientList.push(client);
console.log('clientList length = ' + clientList.length);
for(var i = 0; i<clientList.length; i++){
console.log('client remoteAddress'+[i] + clientList[i].name);
}
client.on('data', function (data) {
/*添加事件監(jiān)聽器,這樣就可以訪問到連接事件所對應(yīng)的client對象,當(dāng)client發(fā)送數(shù)據(jù)給服務(wù)器時(shí),這一事件就會觸發(fā)*/
//廣播消息給其他客戶端
broadcast(data,client);
});
//監(jiān)聽客戶端終止
client.on('end',function(){
console.log(''+client.name+'quit');//如果某個(gè)客戶端斷開連接,node控制臺就會打印出來
clientList.splice(clientList.indexOf(client),1);
});
/*記錄錯(cuò)誤*/
client.on('error',function(e){
console.log(' error'+e);
});
function broadcast(message,client){
var cleanup=[];//斷開了的客戶端們
for (var i = 0; i < clientList.length; i++) {
if (clientList[i] !== client) {
//檢查socket的可寫狀態(tài)
if (clientList[i].writable) {
// 把數(shù)據(jù)發(fā)送給其他客戶端
clientList[i].write(client.name + "says " + message);
}else{
/*socket不可寫,則將其從列表中移除*/
cleanup.push(clientList[i]);
clientList[i].destroy();
}
}
}
/*刪除掉服務(wù)器的客戶端數(shù)組中,已斷開的客戶端*/
for(var i=0;i<cleanup.length;i++){
clientList.splice(clientList.indexOf(cleanup[i]),1);
}
}
});
//服務(wù)器端口
chatServer.listen(9000, function(){
console.log("server bound : 9000");
});