Node.js 為實現tcp 提供了一個模組->net 使用時直接require這個模組
建立一個tcp服務
- net.createServer(callback),回撥函式是連線事件的監聽器,當連線到來時才會執行,它的參 數socket套接字是一個duplex(雙工流)可以支援讀操作和寫操作,socket每次連線時都會產生一個新的socket,每個socket與自己的客戶端通訊
- server.listen可以設定服務監聽的埠,主機名, backlog服務端處理的最大請求,預設是511,還有回撥函式,當服務啟動成功會呼叫
- server.maxConnections 配置服務的最大連線數
- server.getConnections 得到連線時(當請求到來時),會觸發該方法
- socket.write 在 socket 上傳送資料
- socket.on('data',callback) 接受客戶端資料
- socket.on('end',callback)客戶端關閉時呼叫
- socket.end(); 可以觸發客戶端的關閉事件
- server.on('close',callbacl) 只有顯示呼叫server.close()時會觸發, close事件表示服務端不再接收新的請求了,當前的連線還能繼續使用,當客戶端連線全部關閉後會執行close事件
- server.unref() 如果所有客戶端都關閉了,服務端就關閉,如果有新的客戶端連線仍然可以繼續通訊
let net = require('net');
let server = net.createServer(function(socket){
server.maxConnections = 2;
server.getConnections(function(err,count){
socket.write(`當前最大容納${server.maxConnections},現在${count}人`)
});
socket.setEncoding('utf8');
socket.on('data',function(data){
console.log(data);
socket.end();
server.close();
server.unref();
});
socket.on('end',function(){
console.log('客戶端關閉');
});
});
let port = 8080;
server.listen(port,'localhost',function(){
console.log(`server start ${port}`)
});
// close事件只有呼叫close方法才會觸發
server.on('close',function(){
console.log('服務端關閉');
})
//當埠被佔用了,更改埠號
server.on('error',function(err){
//EADDRINUSE 當前埠號被呼叫
if(err.code === 'EADDRINUSE'){
server.listen(++port)
}
});
複製程式碼
測試
可以通過telnet localhost 8080 (telnet會有亂碼問題,不太好用)
或用putty工具來訪問服務,我的設定如下,開啟一個與伺服器的連線後,回車即傳送內容到服務
例:客戶端輸入的內容寫入1.txt檔案
let net = require('net');
let path = require('path');
let ws = require('fs').createWriteStream(path.join(__dirname,'./1.txt'));
// pipe (readale data不能同時使用)
let server = net.createServer(function(socket){
socket.pipe(ws,{end:false});
setTimeout(function(){
ws.end(); // 關閉可寫流
socket.unpipe(ws); // 取消管道
},5000)
});
server.listen(8080);
複製程式碼
利用可讀流pipe方法,邊讀取客戶端輸入的內容,邊寫入檔案,但是當多個客戶端輸入內容時,如果一個客戶端關閉,當前連線的socket就會關閉,同時會關閉ws可寫流,但多個socket用的一個ws,所以引數{end:false} 用於設定這個可寫流不關閉,其他未關閉的客戶端仍然可寫
例:等待客戶端輸入 ,過5s 再列印出來
let net = require('net');
let server = net.createServer(function(socket){
socket.pause();
socket.setTimeout(5000);
socket.on('data',function(chunk){
socket.pause();
console.log(chunk);
})
socket.on('timeout',function(){
//socket.resume();
socket.end();
});
});
server.listen(8080);
複製程式碼
- socket.pause(); 暫停觸發data事件
- socket.setTimeout(5000);設定超時時間,如果超時,會觸發timeout事件,一般超時會關閉客戶端
客戶端訪問服務端時,服務將一個檔案傳送給客戶端
先建立一個檔案
let fs = require('fs');
fs.writeFileSync(__dirname+'/1.txt',Buffer.alloc(1024*1024*10));
複製程式碼
let net = require('net');
let rs = require('fs').createReadStream(__dirname+'/1.txt');
let server = net.createServer(function(socket){
rs.on('data',function(chunk){
let flag = socket.write(chunk);
console.log(flag);
console.log('快取區的大小'+socket.bufferSize);
});
//可寫流的快取區的資料全部寫到目標檔案時觸發
socket.on('drain',function(){
console.log('清空快取')
})
});
server.listen(8080);
複製程式碼
socket.bufferSize : 快取區的大小
例:簡單的聊天室
socket.destroy();//銷燬socket
let net = require('net');
let clients = {}; //儲存{使用者名稱:socket}的對映
// 發言 將聊天內容傳送給其他幾個人
function broadcast(nickname,chunk){
Object.keys(clients).forEach(key=>{
//自己聊天的內容,不應該傳送給自己
if(key!=nickname){
clients[key].write(`${nickname}:${chunk} \r\n`);
}
})
}
let server = net.createServer(function(socket){
server.maxConnections = 3; //允許同時3個人聊天
// 當客戶端連線服務端時,提示使用者輸入使用者名稱
server.getConnections((err,count)=>{
socket.write(`歡迎來到聊天室 當前使用者數${count}個,請輸入使用者名稱\r\n`);
});
let nickname;
socket.setEncoding('utf8');
//當一個使用者關閉了聊天,銷燬socket,並刪除使用者
socket.on('end',function(){
clients[nickname] &&clients[nickname].destroy();
delete clients[nickname]; // 刪除使用者
});
socket.on('data',function(chunk){
chunk = chunk.replace(/\r\n/,'')
//如果nickname存在,說明使用者輸入的是聊天內容
if(nickname){
// 發言,將聊天內容傳送給其他幾個人
broadcast(nickname,chunk);
}else{
//如果不存在nickname時,使用者輸入的內容就是nickname
nickname = chunk;
clients[nickname] = socket;
socket.write(`您的新使用者名稱是${nickname} \r\n`);
}
});
});
server.listen(8080);
複製程式碼
例:聊天室2
該聊天室的功能如下:
1、預設情況下使用者名稱是匿名
2、通過關鍵命令改名, r:使用者名稱
3、支援顯示線上的使用者列表 l
4、廣播的功能,將聊天內容傳送給所有其他線上使用者 b:xxx
5、私聊的功能,只傳送內容給指定使用者 s:使用者名稱:聊天內容
let net = require('net');
let clients = {};
// 改名 r命令
function rename(key,data,socket){
clients[key].nickname = data;
socket.write(`您當前的使用者名稱是${data}\r\n`);
}
// 展示使用者列表 l命令
function list(socket){
let str = `當前使用者列表是:\r\n`
let ls = Object.keys(clients).map(key=>{
return clients[key].nickname;
}).join('\r\n');
socket.write(str+ls+'\r\n');
}
// 私聊 nickname:使用者名稱 content:傳送的內容 key
function private(nickname,content,key){
let user;
Object.keys(clients).forEach(function(key){
if(clients[key].nickname === nickname){
user = clients[key].socket;
}
});
user.write(clients[key].nickname+":"+content+'\r\n');
}
//廣播
function broadcast(nickname,content){
Object.keys(clients).forEach(item=>{
if(clients[item].nickname!= nickname){
clients[item].socket.write(content+'\r\n')
}
})
}
let server = net.createServer(function (socket) {
//使用者預設是匿名的,所以用socket.remoteAddress + socket.remotePort來標識一個使用者
let key = socket.remoteAddress + socket.remotePort; // 唯一
clients[key] = {nickname:'匿名',socket}//預設使用者名稱
server.getConnections((err, count) => {
socket.write(`歡迎來到聊天室 當前使用者${count}個\r\n`);
});
socket.setEncoding('utf8');
socket.on('data', function (chunk) {
chunk = chunk.replace(/\r\n/, '');
let chars = chunk.split(':');
switch (chars[0]) {
case 'r': // r:zhangsan
rename(key,chars[1],socket);
break;
case 'l':
list(socket);
break;
case 'b': // b:content
broadcast(key,chars[1]);
break;
case 's': // s:使用者名稱:content
private(chars[1],chars[2],key);
break;
default:
socket.write('當前命令無法解析,重新輸入\r\n')
}
});
});
server.listen(8080, function () {
console.log(`server start 8080`);
})
複製程式碼
例:用net模組建立客戶端
let net = require('net');
let socket = net.createConnection({port:8080},function(){
socket.write('hello');
socket.on('data',function(data){
console.log(data);
});
});
複製程式碼
服務端測試程式碼
let net = require('net');
let server = net.createServer(function(socket){
socket.setEncoding('utf8');
socket.on('data',function(data){
console.log(data);
});
});
server.on('connection',function(){
console.log('客戶端連線')
})
server.listen(8080);
複製程式碼
啟動伺服器,node命令執行客戶端檔案即可看到效果
UDP (引用dgram模組)
客戶端(不需要繫結埠,隨機分配)
let dgram = require('dgram');
//建立socket
let socket = dgram.createSocket('udp4');
//傳送訊息
socket.send('hello',8080,function(){
console.log('成功')
});
//接收訊息
socket.on('message',function(data){
console.log(data.toString());
})
複製程式碼
服務端
let dgram = require('dgram');
let socket = dgram.createSocket('udp4');
// 服務端監聽一個埠 資料到來時 可以讀出資訊
socket.bind(8080,'localhost',function(){
//讀取訊息
socket.on('message',function(data,rinfo){
console.log(data.toString());
//傳送訊息
socket.send('hello',rinfo.port);
})
});
複製程式碼