接前面
继续了解Node.js。看看其内置网络模块,记个流水账:
- net
- dgram
- tls
- http
- http/2
- https
从哪儿开始?
依靠JavaScript起家,网络功能一定是Nodejs强项了,从什么地方开始了解它好一点?
先从熟悉的Qt开始,先列个表格,大致对比一下:
协议/技术 |
Qt |
Node.js |
Python |
Deamon Socket/Named Pipe |
QLocalServer , QLocalSocket |
net.Server , net.Socket |
socket , socketserver.UnixStreamServer |
TCP |
QTcpServer , QTcpSocket |
net.Server , net.Socket |
socket , socketserver.TCPServer |
UDP |
QUdpSocket |
dgram.Socket |
socket , socketserver.UDPServer |
SCTP |
QSctpServer , QSctpSocket |
sctp (三方) |
(三方) |
SSL/TLS |
QSslSocket , QSslServer |
tls.Server |
ssl.SSLSocket , ssl.SSLServer |
HTTP |
QHttpServer (GPL), QNetworkAccessManager |
http.Server ,http2 |
http.server , http.client |
HTTPS |
QHttpServer (GPL), QNetworkAccessManager |
https.Server ,http2 |
http.server |
WebSocket |
QWebSocketServer , QWebSocket |
ws (三方) |
websockets (三方) |
DNS |
QDnsLookup |
dns |
dnspython (三方) |
例子?
几个小例子,找找感觉
老规矩,每个.mjs文件都是独立的程序,可以直接运行测试
本地IPC通讯
nodejs的net模块提供了IPC(local server),和TCP功能。
先看看如何使用net模块,通过windows下命名管道或Unix的deamon socket实现的 本机IPC功能。
服务端:
- 使用 net.createServer() 创建server对象
- 使用 server.listen() 开启监听【named pipe路径 或 deamon socket路径】
- 使用server.on() 来响应新的socket连接
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 | // myipcserver.mjs
import * as net from 'node:net';
import * as process from 'node:process';
const server = net.createServer();
let ipcPath;
if (process.platform === 'win32') {
ipcPath = '\\\\.\\pipe\\debaotest_pipe';
} else {
ipcPath = '/tmp/debaotest.sock';
}
server.on('connection', (socket) => {
console.log('Client connected');
socket.on('data', (data) => {
console.log(`From Client: ${data}`);
});
socket.write('Hello 1+1=10 from server');
socket.on('end', () => {
console.log('Client disconnected');
});
});
server.listen(ipcPath, () => {
console.log(`Server listening on ${ipcPath}`);
});
|
客户端:
- 使用 net.createConnection() 连接服务端并创建client
- 使用 client.write() 发送数据
- 使用 client.on() 响应接受数据
- client收到服务端数据后,调用client.end() 结束连接
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 | // myipcclient.mjs
import * as net from 'node:net';
import * as process from 'node:process';
let ipcPath;
if (process.platform === 'win32') {
ipcPath = '\\\\.\\pipe\\debaotest_pipe';
} else {
ipcPath = '/tmp/debaotest.sock';
}
const client = net.createConnection({ path: ipcPath }, () => {
console.log('Connected to server');
client.write('Hello 1+1=10 from client');
});
client.on('data', (data) => {
console.log(`From Server: ${data}`);
client.end();
});
client.on('end', () => {
console.log('Disconnected from server');
});
|
客户端输出结果:
| Connected to server
From Server: Hello from server
Disconnected from server
|
只要不调用client.end(),这个客户端程序就会一直运行。说明nodejs在跑事件循环,client.end()会结束事件循环,这部分功课,后面需要补一补。
TCP通讯
TCP和前面的IPC用起来基本一样。
服务端
只需要把ipcpath去掉,让listen监听TCP端口就可以了:
| let port = 3000;
server.listen(port, () => {
console.log(`Server listening on ${port}`);
});
|
listen用法
| server.listen(3000, callback);
server.listen(3000, "127.0.0.1", callback);
server.listen({ host: '0.0.0.0', port: 3000 }, callback);
|
客户端
客户端和IPC客户端也一样,只要改一下createConnection(),连接指定的地址和端口就行了:
| const client = net.createConnection(3000, () => {
console.log('Connected to server');
client.write('Hello from client');
});
|
createConnection用法:
| net.createConnection(3000, callback);
net.createConnection(3000, '127.0.0.1', callback);
net.createConnection({ port: 3000, host: '127.0.0.1' }, callback);
|
UDP 通讯
udp 在 dgram模块中。
服务端
- 使用 dgram.createSocket() 创建UDP server
- 使用 server.bind() 绑定监听端口
- 使用server.on() 监听数据
- 使用server.send() 发送数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | import * as dgram from 'node:dgram';
const server = dgram.createSocket('udp4');
server.on('message', (msg, rinfo) => {
console.log(`Received message from ${rinfo.address}:${rinfo.port} - ${msg}`);
server.send('Hello 1+1=10 from UDP server', rinfo.port, rinfo.address, (err) => {
if (err) {
console.error(err);
server.close();
}
});
});
server.on('listening', () => {
const address = server.address();
console.log(`UDP Server listening on ${address.address}:${address.port}`);
});
server.bind(3000);
|
客户端
- 使用 dgram.createSocket() 创建 client
- 使用client.send() 发送UDP数据
- 使用client.on() 监听数据
- 使用client.close() 关闭连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 | import * as dgram from 'node:dgram';
const client = dgram.createSocket('udp4');
const message = 'Hello 1+1=10 from UDP client';
client.send(message, 3000, '127.0.0.1', (err) => {
if (err) {
console.error(err);
client.close();
} else {
console.log(`Message sent: ${message}`);
}
});
client.on('message', (msg, rinfo) => {
console.log(`Received response from ${rinfo.address}:${rinfo.port} - ${msg}`);
client.close();
});
|
调用client.close() 之前,进程一直存在。
Http通讯
nodejs中有三个模块:
http
用于创建基于 HTTP 协议的服务器和客户端。
https
用于创建基于 TLS/SSL 的安全的 HTTP 服务器和客户端。
http2
用于创建基于 HTTP/2 协议的服务器和客户端,支持 TLS/SSL 进行加密
先看看http模块
服务端
- http.createServer() 创建 server
- server.listen() 启动监听
1
2
3
4
5
6
7
8
9
10
11
12 | import * as http from 'node:http';
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello 1+1=10!\n');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server running at http://localhost:${PORT}/`);
});
|
客户端
浏览器是最好的客户端。
不过nodejs写客户端来做些测试也不复杂:
- http.request() 用于创建请求req和设置响应回调
- req.on() 用于处理错误
- req.end() 标志请求结束,开始发送请求
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 | import * as http from 'node:http';
const options = {
hostname: 'localhost',
port: 3000,
path: '/',
method: 'GET',
};
const req = http.request(options, (res) => {
let data = '';
res.o('data', (chunk) => {
data += chunk;
});
res.on('end', () => {
console.log(`Response from server: ${data}`);
});
});
req.on('error', (e) => {
console.error(`Error: ${e.message}`);
});
req.end();
|
HTTPS
https 和 http2都可以,看看 http2:
- 准备证书和私钥
- http2.createSecureServer() 创建server
- server.listen() 进行监听
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 | import * as fs from 'node:fs';
import * as http2 from 'node:http2';
const options = {
key: fs.readFileSync('127_0_0_1_ssl_key.pem'),
cert: fs.readFileSync('127_0_0_1_ssl_cert.pem'),
};
const server = http2.createSecureServer(options, (req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello 1+1=10 (over HTTP/2)!\n');
});
server.listen(3000, () => {
console.log('HTTP/2 Server running on port 3000');
});
|
使用浏览器访问:https://127.0.0.1:3000
即可。
tls 通讯
服务端
- 准备私钥和证书
- 使用tls.createServer() 创建server,设置回调
- server.listen() 启动监听
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 | import * as fs from 'node:fs';
import * as tls from 'node:tls';
const options = {
key: fs.readFileSync('127_0_0_1_ssl_key.pem'),
cert: fs.readFileSync('127_0_0_1_ssl_cert.pem'),
};
const server = tls.createServer(options, (cleartextStream) => {
console.log('Client connected');
cleartextStream.write('Hello 1+1=10 from TLS server!\n');
cleartextStream.setEncoding('utf8');
cleartextStream.on('data', (data) => {
console.log(`Received from client: ${data}`);
});
cleartextStream.on('end', () => {
console.log('Client disconnected');
});
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`TLS Server running on port ${PORT}`);
});
|
客户端
- tls.connect() 创建client进行连接
- client.write() 发送数据
- client.on() 监听
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | import * as tls from 'node:tls';
const options = {
host: 'localhost',
port: 3000,
rejectUnauthorized: false, // for test only!!!
};
const client = tls.connect(options, () => {
console.log('Connected to TLS server');
client.write('Hello from TLS client!\n');
});
client.setEncoding('utf8');
client.on('data', (data) => {
console.log(`Received from server: ${data}`);
});
client.on('end', () => {
console.log('Connection closed');
});
|
参考
- https://nodejs.org/api/net.html
- https://nodejs.org/api/dgram.html
- https://docs.python.org/3/library/socket.html
- https://docs.python.org/3/library/socketserver.html
- https://doc.qt.io/qt-6/qthttpserver-index.html