最近在公司多个项目同时开工,连更新博客的时间都没了。但是该做的还得做,不能找理由、找借口,今天就挤点时间出来写完一篇吧。今天要说的node模块可以一个大头戏:net。此模块可以很多其他模块的基础,稳定性和重要性不言而喻。
创建一个TCP服务器,此函数有2个可选参数:
-
options 第一个参数是一个配置项,只有一个配置(allowHalfOpen)。表示允许服务器保持半开连接。
-
connectionListener 第二个参数是连接触发的事件函数,只要有客户端连接上来,此事件就会被触发。
// 创建一个服务器
var net = require('net');
var server = net.createServer(function(){
console.log('有客户端连接!');
});
server.listen(8008, function(){
console.log('服务器一开始监听8008端口!');
});
运行以上代码,就会开启本机8008端口的TCP监听,当有客户端连接上来就会打印指定字符串:"有客户端连接!"(效果如下图)。
这2个方法是一个实现逻辑,其实就是一个方法,node源码是这么写的:
exports.connect = exports.createConnection = function() {
// ...
};
此方法就是作为客户端去连接一个服务器端。此方法有2个参数,第一个参数(options)同样是配置项,第二个参数是一个可选参数,表示连接建立后推送的事件。
-
options 此参数是一个配置项,并且是必须的。配置项包括:
- port 连接到的端口
- host 对方(服务器)的主机地址,默认是"localhost"
- localAddress 需要绑定的本地接口
- path 在UNIX机器上的套接字路径
- allowHalfOpen 是否允许半连接
-
connectionListener 连接建立之后推送的事件
// 创建一个服务器
var net = require('net');
var server = net.createServer(function(){
console.log('有客户端连接!');
});
server.listen(8008, function(){
console.log('服务器一开始监听8008端口!');
});
// 创建一个客户端
var net = require('net');
net.connect({
port: 8008
}, function(){
console.log('已经连接到本机的8008端口');
});
分别运行上面的服务器和客户端代码,效果如下:
另外这个方法还有简单版本,不需要配置options。
// 创建一个客户端
var net = require('net');
net.connect(8008, function(){
console.log('已经连接到本机的8008端口');
});
或者
// 创建一个客户端
var net = require('net');
net.connect('/root/c.sock', function(){
console.log('已经连接到本机的8008端口');
});
这是一个类,用来创建TCPserver(上面我们提到的net.createServer方法返回的就是一个net.Server实例)
开启接受连接。此方法有4个参数:
- port 需要监听的端口,如果端口传入0的话,当前服务器会监听一个随机端口。
- host 此参数是一个可选参数,如果不传此参数,当前服务器会接受所有IPV4的连接。
- backlog 挂起连接的数量(默认值为511)
- callback 监听开启回调函数(即listening事件)
var Server = require('net').Server;
var tcpServer = new Server();
// 开启监听8800端口
tcpServer.listen(8800);
// 监听开启事件
tcpServer.on('listening', function(){
console.log('监听服务已开启,端口:8800');
});
以上代码很简单,首先开启了8800端口的监听,然后定义了listening事件,一旦开启8800端口监听成功,就会推送listening事件。当然,server.listen方法还有很多用法:
- server.listen(path, [callback]) 表示连接一个UNIX套接字
- server.listen(handle, [callback]) 表示根据传入的处理对象(server或者socket)进行连接
停止接受新的连接,但是此方法调用之后现有的连接会保持,直到所有的连接都断开并且服务器触发了'close'事件之后,server才会真正的关闭。
var net = require('net');
var server = new net.Server();
server.listen(8800);
server.on('listening', function() {
console.log('started.');
});
setTimeout(function() {
server.close(function() {
console.log('closed.');
});
}, 3000);
运行以上代码之后,如果3秒内没有连接,那么服务器会自动退出,否则会等待所有连接处理完毕,才会关闭服务器端。另外以上代码也可以这样写:
var net = require('net');
var server = new net.Server();
server.on('listening', function() {
console.log('started.');
});
server.on('close', function() {
console.log('closed.');
});
server.listen(8800);
setTimeout(function() {
server.close();
}, 3000);
此方法返回一个JSON数据,包括绑定的IP地址、IP类型、端口号。
var net = require('net');
var server = new net.Server();
server.on('listening', function() {
console.log(server.address()); // { address: '0.0.0.0', family: 'IPv4', port: 8000 }
});
server.listen(8000);
注意,只有在服务器触发了'listening'事件之后,server.address方法才可以调用,否则此方法会返回null值:
var net = require('net');
var server = new net.Server();
console.log(server.address()); // null
server.listen(8000);
server.unref方法会允许程序退出(在'事件系统'中只有当前服务器时)
var net = require('net');
var server = new net.Server();
server.listen(8000);
server.unref();
server.ref方法是server.unref的反操作,会阻止服务器退出(之前调用了server.unref)
var net = require('net');
var server = new net.Server();
server.listen(8000);
server.unref();
setTimeout(function() {
server.ref();
}, 3000);
此时服务器不会因为调用了server.unref方法而退出。
获取当前连接数。
var net = require('net');
var server = new net.Server();
server.on('listening', function() {
console.log('server is listening.');
});
server.on('connection', function() {
server.getConnections(function(err, count) {
if(err) return;
console.log('当前的连接数:%d', count);
});
});
server.listen(8000);
net.Server的事件的事件有:'listening'、'connection'、'close'、'error',所有的事件都在上面的代码中间接介绍了。
net.Socket是一个类,是TCP或UNIX套接字抽象。并且实现了复杂模式的Stream接口(关于Stream的更多信息,我会在以后的博客中介绍)。
创建一个socket实例,此方法有一个可选的配置参数:options,其中有4个属性可以配置
- fd 指定socket文件描述符(默认值为null)
- allowHalfOpen 是否允许半连接(默认值为false)
- readable 是否可读(默认值为false)
- writable 是否可写(默认值为false)
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('connect', function() {
console.log('The socket is established.');
});
socket.on('error', function() {
console.log('Something is wrong.');
});
建立一个socket连接,并且可以指定端口、主机或者是连接文件。如果我们需要建立一个socket连接,可以这个干:
服务器端:
var net = require('net');
var server = new net.Server();
server.listen(8088, function() {
console.log('listening.');
});
server.on('connection', function(socket) {
console.log('someone is in.');
});
在服务器端,我们需要创建一个net.Server实例,用来作为服务器等待连接。
客户端:
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('connect', function() {
console.log('The socket is established.');
});
socket.on('error', function() {
console.log('Something is wrong.');
});
在客户端我们利用net.connect方法连接上了我们刚刚创建的服务器实例,并且定义了'connect'事件(socket连接建立立即推送的事件)和'error'事件。如果连接建立成功,在服务器端就会打印出“listening.”和“someone is in.”,在客户端并打印“The socket is established.”。如果出现出错,客户端会触发'error'事件(打印“Something is wrong.”)。
待写入字符大小
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('connect', function() {
console.log('The socket is established.');
console.log(socket.bufferSize); // 0
});
当socket被设置成可读时,此函数可用来设置Stream编码格式。
使用socket发送数据,此函数有2个可选参数:
- encoding 如果发送的数据为字符串,可以设置编码(默认为UTF-8)
- callback 当数据全部被处理之后调用的回调
客户端:
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('connect', function() {
console.log('The socket is established.');
socket.write('Hello Socket', function() {
console.log('done.');
});
});
服务器端:
var net = require('net');
var server = new net.Server();
server.listen(8088, function() {
console.log('listening.');
});
server.on('connection', function(socket) {
console.log('someone is in.');
socket.on('data', function(data) {
console.log(data); // Hello Socket
});
});
半关闭socket,如果传入了可选参数data,那么相当于调用了socket.write(data)
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('connect', function() {
console.log('The socket is established.');
socket.end('bye.');
});
调用此方法后,除了一些出错信息,其他一切I/O都不可进行(可以认为是客户端主动关闭连接)
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('error', function(err) {
console.log('error:' + err.message); // error:This socket is closed.
});
socket.on('connect', function() {
console.log('The socket is established.');
socket.destroy();
socket.write('Hello'); // 此数据将不被传输,因为连接已经关闭
});
暂停读取数据、回复读取数据
发送端代码:
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.connect(8088);
socket.on('error', function(err) {
console.log('error:' + err.message);
});
socket.on('connect', function() {
console.log('The socket is established.');
setInterval(function(){
socket.write(new Date() + 'Hello');
}, 1000);
});
接收端代码:
var net = require('net');
var server = new net.Server({
allowHalfOpen: true
});
server.listen(8088, function() {
console.log('listening.');
});
server.on('connection', function(socket) {
console.log('someone is in.');
socket.on('data', function(data) {
console.log(data.toString());
});
socket.pause();
setTimeout(function(){
socket.resume();
}, 1500);
});
设置socket超时,超时(指定时间)被触发之后,此socket将继续可用,我们必须手动调用socket.end或socket.destroy方法来中断连接
服务器端:
var net = require('net');
var server = new net.Server({
allowHalfOpen: true
});
server.listen(8088, function() {
console.log('listening.');
});
server.on('connection', function(socket) {
console.log('someone is in.');
socket.on('data', function(data) {
console.log(data.toString());
});
});
客户端:
var net = require('net');
var socket = new net.Socket({
allowHalfOpen: true
});
socket.on('timeout', function() {
console.log('timeout');
socket.end('end');
});
socket.setTimeout(2000);
socket.connect(8088);
设置无延迟发送(关闭Nagle算法)。将数据立即发送出去,而无需使用Nagle算法。
开启心跳
- enable 是否开启心跳(默认false)
- initialDelay 心跳间隔
这3个方法与上面net.Server的3个方法类似,这里就不赘述
- socket.remoteAddress 远程地址
- socket.localAddress 本地地址
- socket.remotePort 远程端口
- socket.localPort 本地端口
- socket.bytesRead 已接收字节数
- socket.bytesWritten 已发送字节数
以上的事件都在上面的代码中使用过,已经熟悉了。而'drain'事件表示写入buffer为空时触发的事件(可用于上传逻辑)。
- net.isIP(input) 判断是否是IP地址(如果是IPV4就返回数字4,如果是IPV6就返回数字6,如果都不是则返回0):
var net = require('net');
console.log(net.isIP('172.16.0.90')); // 4
console.log(net.isIP('2001:0DB8:02de::0e13')); // 6
- net.isIPv4(input) 判断是否是IPV4
var net = require('net');
console.log(net.isIPv4('172.16.0.90')); // true
console.log(net.isIPv4('2001:0DB8:02de::0e13')); // false
- net.isIPv6(input) 判断是否是IPV6
var net = require('net');
console.log(net.isIPv6('172.16.0.90')); // false
console.log(net.isIPv6('2001:0DB8:02de::0e13')); // true
讲到这里,net模块就讲完了。其实与其他语言比较起来,使用node来做socket代码非常简洁并且天生异步的模式也非常符合生产环境使用。
这边博客与上一篇间隔时间很长,大概有20多天的样子。期间忙于公司的事情与自我学习(主要是docker方向的),并且看到在cnode已经有人在写关于node源码的文章了,所以我觉得关于node源码的部分我不全部讲解,只是在个别模块与注意点上写几篇跟人观点。