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);
})
});
复制代码