初始實現
var net = require('net');//1 引入net模塊 var chatServer = net.createServer();//創建net服務器 var clientList=[];//保存多個客戶端的數組 chatServer.on('connection', function (client) {//服務器連接客戶端 client.name=client.remoteAddress+':'+client.remotePort; /*增加name屬性*/ client.write('Hi'+client.name+'!\n'); clientList.push(client); client.on('data', function (data) { /*添加事件監聽器,這樣就可以訪問到連接事件所對應的client對象,當client發送數據給服務器時,這一事件就會觸發*/ for(var i=0;i<clientList.length;i++){ if(clientList[i]!==this){ // 把數據發送給其他客戶端 clientList[i].write(this.name+"says "+data); } } }); }); chatServer.listen(9000, "127.0.0.1");//服務器端口
注意:這里有個坑——如果有個客戶端斷開連接,那么所有人都會玩完!
因為如果再往服務器發送消息,這時候服務器並不知道某個客戶端已經斷開了連接,因此會繼續向其發送數據,但是這時斷開的這個客戶端對應的socket已經無法寫入數據,而對已關閉的socket進行write()操作node程序會拋出異常,進而導致全軍覆沒。所以,這個問題應該從兩個方面來解決:
(1)當客戶端斷開連接時,通知服務器,將其從客戶端列表中移除,防止其調用write方法(V8引擎也會把響應的socket對象作為垃圾回收,並釋放相應的內存);
(2)采用更保險的方式調用write()方法。
改進如下:
最后,監聽客戶端關閉事件,並記錄錯誤
var net = require('net');//1 引入net模塊 var chatServer = net.createServer();//創建net服務器 var clientList = [];//保存多個客戶端的數組 chatServer.on('connection', function (client) {//服務器連接客戶端 // 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) { /*添加事件監聽器,這樣就可以訪問到連接事件所對應的client對象,當client發送數據給服務器時,這一事件就會觸發*/ //廣播消息給其他客戶端 broadcast(data,client); }); //監聽客戶端終止 client.on('end',function(){ console.log(''+client.name+'quit');//如果某個客戶端斷開連接,node控制台就會打印出來 clientList.splice(clientList.indexOf(client),1); }); /*記錄錯誤*/ 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的可寫狀態 if (clientList[i].writable) { // 把數據發送給其他客戶端 clientList[i].write(client.name + "says " + message); }else{ /*socket不可寫,則將其從列表中移除*/ cleanup.push(clientList[i]); clientList[i].destroy(); } } } /*刪除掉服務器的客戶端數組中,已斷開的客戶端*/ for(var i=0;i<cleanup.length;i++){ clientList.splice(clientList.indexOf(cleanup[i]),1); } } }); //服務器端口 chatServer.listen(9000, function(){ console.log("server bound : 9000"); });