TCP
Nagle算法
如果每次发送一个很小的数据包,比如一个字节内容的数据包而不优化,就会导致网络中只有极少数有效数据的数据包,这会导致浪费大量的网络资源。Nagle算法针对这种情况,要求缓存区的数据达到一定数据量或者一定时间后才将其发出,所以数据包将会被Nagle算法合并,以此来优化网络。这种优化虽然提高了网络带宽的效率,但有的数据可能会被延迟发送。
在Nodejs中,由于TCP默认启动Nagle算法,可以调用socket.setNoDelay(ture)
去掉Nagle算法,使得write()
可以立即发送数据到网络中。但需要注意的是,尽管在网络的一端调用write()
会触发另一端的data事件,但是并不是每次write()
都会触发另一端的data事件,在关闭Nagle算法后,接收端可能会将接收到的多个小数据包合并,然后只触发一次data事件。也就是说socket.setNoDelay(ture)
只能解决一端的数据粘包问题。
什么是粘包?
在组包过程中,把上一个包的内容与下一个包里的粘在了一起被错误地当成了一个数据包解析了出来。这就是所谓的粘包。
粘包出现的根本原因是不确定消息的边界。接收端在面对"无边无际"的二进制流的时候,根本不知道收了多少 01 才算一个消息。一不小心拿多了就说是粘包。其实粘包根本不是 TCP 的问题,是使用者对于 TCP 的理解有误导致的一个问题。
只要在发送端每次发送消息的时候给消息带上识别消息边界的信息,接收端就可以根据这些信息识别出消息的边界,从而区分出每个消息。
通过拆包封包的方式解决粘包方案
node.js中,不会按照发送端的write
写入对应触发接收端的data事件,它会有以下3种情况:
- 发送端多次
write()
的数据可能被打包成一个数据包发送到接收端。 - 发送端通过
write()
一次写入的数据可能因为体积过大被截断到多个数据包中 - 发送端通过
write()
一次写入打包成一个数据报发送到接收端
我们要处理就是前2中情况。TCP是基于流的传输机制,那么它的数据顺序在传输过程中是确定的先进先出原则。所以,可以通过在每次write()
在数据头部添加一些标识,将每次write()
传输的数据间隔开,然后在接收端基于这些间隔数据的标识将数据拆分或合并。
UDP
UDP又称用户数据包协议,与TCP一样同属于网络传输层。UDP与TCP最大的不同是UDP不是面向连接的。
UDP的应用场景:DNS,音视频传输
- server.js
const PORT = 3333;
const HOST = '127.0.0.1';
const dgram = require("dgram");
var server = dgram.createSocket('udp4');
server.on("listen", function() {
const address = server.address();
console.log('UDP Server listening on ' + address.address + ":" + address.port);
})
server.on('message', function (message, remote) {
console.log(remote.address + ':' + remote.port +' - ' + message);
});
socket.bind(POST, HOST);
- client
const PORT = 3333;
const HOST = '127.0.0.1';
const dgram = require('dgram');
const message = Buffer.from('Test Message');
const client = dgram.createSocket('udp4');
client.send(message, PORT, HOST, function(err, bytes) {
if (err) throw err;
console.log('UDP message sent to ' + HOST +':'+ PORT);
client.close();
});
UDP套接字事件
UDP套接字相对TCP套接字使用起来更简单,它只是一个EventEmitter的实例,而非Stream的实例。它具备如下自定义事件。
- message:当UDP套接字侦听网卡端口后,接收到消息时触发该事件,触发携带的数据为消息Buffer对象和一个远程地址信息。
- listening:当UDP套接字开始侦听时触发该事件。
- close:调用close()方法时触发该事件,并不再触发message事件。如需再次触发message事件,重新绑定即可。
- error:当异常发生时触发该事件,如果不侦听,异常将直接抛出,使进程退出。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。