Node.js踩坑之旅(二)--net模块中socket的end和server的close以及两者都有的unref


这几天看书看到 net模块的 socketserver对象,发现 socket具有 end 方法和同名事件server则具有 close 方法和同名事件

而两者都同时具有一个名为unref的方法。endcloseunref这三个名字本身,或者是它们所代表的方法都或多或少存在相似之处。这着实是让我这种初学者增加了不少理解难度。

net.Server对象

close方法

close,字面上理解就是关闭的意思。作为server对象的方法,那么应该就是用来关闭当前server的方法。

事实上也确实如我们从字面上直接分析得出的结论一样,close方法的确是用来关闭server对象的。不过,并不是直接关闭

Stops the server from accepting new connections and keeps existing connections. This function is asynchronous, the server is finally closed when all connections are ended and the server emits a ‘close’ event. The optional callback will be called once the ‘close’ event occurs.

以上是Node.js官方文档中截取的关于server.close()的描述。可以看出,close方法执行后。服务器将不会接受任何新增的连接,但仍会保证现有的连接。而服务器将会在所有连接都终止之后被关闭

举个例子:

客户端:

let net = require('net');

let client = new net.Socket();

client.connect(2000, '192.168.199.106', ()=>{
	console.log('已连接到服务器端');
	client.write('你好');
	client.end('再见。');
});

client.on('data', (data)=>{
	console.log('接收到数据:');
	console.log(data.toString());
});

client.on('error', (err)=>{
	console.warn(err);
	client.destory();
});

client.on('end', ()=>{
	console.log('ended');
});

client.on('close', ()=>{
	console.log('finally closed');
});

服务器端:

let net = require('net');
let tcpServer1 = net.createServer();

tcpServer1.on('listening', ()=>{
    console.log('开始监听');
});

tcpServer1.on('connection', (socket)=>{
    console.log('连接已建立' + '\n');
    tcpServer1.getConnections((err, count)=>{
        if(err){
            console.warn(err);
        } else {
            console.log(`当前有${count}个连接`);
        }
    });
    socket.on('data', (data)=>{
        console.log(data.toString());
    });
    socket.on('error', (err)=>{
        console.warn(err);
        socket.destroy();
    });
    socket.on('end', ()=>{
        tcpServer1.close();
    });
   
});
tcpServer1.on('close', ()=>{
    console.log('closed');
});
tcpServer1.listen(2000);

在服务器端的代码中,服务器的socket对象一旦监听到end事件就会执行tcpServer1.close()。 而客户端代码中的client.end()方法则会触发服务器end事件。

首先使用telnet命令来连接服务器
在这里插入图片描述
这时的服务器端
在这里插入图片描述
接着运行客户端程序
在这里插入图片描述
可以看到,客户端这边已经运行结束。而前面说过,一旦客户端的client.end()执行之后,便会触发服务器端end事件从而执行tcpServer1.close()

此时的服务器端
在这里插入图片描述
尽管tcpServer1.close()已经执行,但是可以看到程序并没有运行结束。这是因为还有一个telnet连接,当我把这个连接关闭。

在这里插入图片描述
server对象真正被关闭时,会触发close事件。所以在上面的截图中可以看到,close事件的监听函数被触发了,在控制台输出了"closed"。

至于为啥会输出两行"closed",是因为前面socket对象的end事件被触发了两次,因为每次客户端连接断开都会触发。也就意味着,tcpServer1.close()被执行了两次。所以会出现两行"closed"。

net.Socket对象

allowHalfOpen参数

end方法和end事件之前,不得不提net.Server这个对象在构造时的一个重要参数allowHalfOpen

这个参数默认值为false。当它为true时,接收到对方发送的FIN包后,不会发送一个ACK包回去。如果要完成TCP的四次挥手,则需要手动调用socket对象的end方法。

同时如果这个值为真,那么对方对于本方来说可写不可读

end方法和end事件

end方法是用来给对方发送FIN包的,而end事件则是接收到来自对方的FIN包之后才触发的。

close事件

socket对象虽然没有close方法,但是有close事件。这个事件是在socket对象要被彻底关闭时被触发也就是在end事件触发之后触发。

unref方法

net.Server的unref方法

Calling unref() on a server will allow the program to exit if this is the only active server in the event system. If the server is already unrefed calling unref() again will have no effect.

这是Node.js官方文档的描述。其中的"if this is the only active server in the event system"让我困惑了许久,什么叫做“事件系统中惟一的活动服务器呢”?

一开始我以为是如果同时有两个服务器程序在运行,那么调用了unref方法也不会使得程序退出。

可是我同时运行了两个服务器程序,在一个程序里面调用了unref方法,这个程序就退出了。

这让我非常疑惑,后来我发现,应该是一个文件内有两个服务器程序。如下代码所示:

let net = require('net');
let tcpServer1 = net.createServer();
let tcpServer2 = net.createServer();

tcpServer1.on('listening', ()=>{
    console.log('开始监听');
});

tcpServer1.on('connection', (socket)=>{
    console.log('连接已建立' + '\n');
    tcpServer1.getConnections((err, count)=>{
        if(err){
            console.warn(err);
        } else {
            console.log(`当前有${count}个连接`);
        }
    });
    socket.on('data', (data)=>{
        console.log(data.toString());
    });
    socket.on('error', (err)=>{
        console.warn(err);
        socket.destroy();
    });
    socket.on('end', ()=>{
        tcpServer1.unref();
    });
   
});
tcpServer1.on('close', ()=>{
    console.log('closed');
});
tcpServer1.listen(2000);

tcpServer2.on('listening', ()=>{
    console.log('开始监听');
});

tcpServer2.on('connection', (socket)=>{
    console.log('连接已建立' + '\n');
    tcpServer2.getConnections((err, count)=>{
        if(err){
            console.warn(err);
        } else {
            console.log(`当前有${count}个连接`);
        }
    });
    socket.on('data', (data)=>{
        console.log(data.toString());
    });
    socket.on('error', (err)=>{
        console.warn(err);
        socket.destroy();
    });
    socket.on('end', ()=>{
        tcpServer2.unref();
    });
   
});
tcpServer2.on('close', ()=>{
    console.log('closed');
});
tcpServer2.listen(2001);

其实我就是把代码复制粘贴了一遍,一个程序里有两个服务器对象。这时我发现,即便是其中的一个调用了unref方法也不会使得程序运行结束。因为此时,事件系统中还有另一个活动的服务器对象。

net.Socket的unref方法

net.Serverunref方法非常类似,只不过是当事件系统中只有唯一地活跃socket对象才会使程序运行结束。

猜你喜欢

转载自blog.csdn.net/hjc256/article/details/86613109