Node.js底层网络编程

讲解如何构造TCP、UDP包来通信

TCP编程和Net模块

创建一个基本的网络服务器,使用net模块,创建服务器方法。监听connection事件,每次客户端连接到服务端的时候会触发该事件。socket参数是个双重流,可写可读。要运行服务器,需要监听一个端口,还可以添加一个回调,目的是确认连接成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
process.stdout.write('\u001B[2J\u001B[0;0f');
const server = require('net').createServer();
server.on('connection', socket => {
console.log('Client connected');
socket.write('Welcome new client\n');
socket.on('data', data => {
console.log('data is: ', data);
});
// socket.setEncoding('utf8');
socket.on('end', () => {
console.log('Client disconnected');
});
});
server.listen(8000, () => console.log('Server bound'));

工作在多个Sockets上

搭建一个简单的chat room

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const server = require('net').creatServer();
let count = 0;
let sockets = {};
// 优化点3:增加时间戳
function timeStamp() {
const now = new Date();
return `${now.getHours()}:${now.getMinutes()}`;
}
server.on('connection', socket => {
socket.id = count++;
console.log('Client connected');
// socket.write('Welcome new Client\n');
// 优化点2:不用数字代替登陆到聊天室的人
socket.write('Please type your name: ');
socket.on('data', data => {
if(!sockets[socket.id]) {
socket.name = data.toString().trim();
socket.write(`Welcome ${socket.name}\n`);
sockets[socket.id] = socket;
return;
}
Object.entries(sockets).forEach(([key,cs]) => {
// 优化1: 自己输入的不用显示在屏幕上
if(socket.id === key) return;
cs.write(`${socket.name} ${timeStamp()}: `);
cs.write(data);
});
});
socket.on('end', () => {
delete sockets[socket.id];
console.log('Client disconnected');
});
});
server.listen(8000, () => console.log('Server bound'));

DNS模块

可以使用它将域名转化为地址,反之亦然。加载DNS模块,运用lookup方法来查询,这个方法很特殊,因为没有用任何网络通信,而是用底层操作系统的模块来解析。意思是它用到了libuv线程。DNS模块中的其他方法直接用网络来通信没有用到libuv线程。例如:lookup相等的一个方法是resolve4,resolve,resolveMx,reserve(把ip转化成域名)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const dns = require('dns');
dns.lookup('www.google.com', (err, address) => {
if(err) throw err;
console.log(address);
});
dns.resolve4('www.google.com', (err, address) => {
console.log(address); // return array
});
dns.reserve('192.168.0.1', (err, hostnames) => {
console.log(hostnames);
});

UPD数据报Sockets

dgram模块来操作UDP数据报sockets,使用createSocket来新建socket,参数是udp4或者udp5,要监听这个socket,使用bind方法指定端口和主机。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const dgram = require('dgram');
const PORT = 3333;
const HOST = '127.0.0.1';
// Server
const server = dgram.createSocket('udp4');
server.on('listening', () => console.log('UDP Server listening'));
server.on('message', (msg, rinfo) => {
console.log(`${rinfo.address}:${rinfo.port} - ${msg}`);
});
server.bind(PORT, HOST);
// Client
const client = dgram.createSocket('udp4');
// It can be a buffer instead of string
const msg = Buffer.from('adsfasdf');
// client.send(msg, 0, msg.length, PORT, HOST, (err)...)
client.send('HAHAHAHAH', PORT, HOST, (err) => {
if(err) throw err;
console.log('UDP message sent');
client.close();
});