编程开源技术交流,分享技术与知识

网站首页 > 开源技术 正文

Node知识体系之网络(node知识点)

wxchong 2024-07-15 10:18:56 开源技术 40 ℃ 0 评论

今天我们的主题是关于网络方面的知识。

获取本地 IP

function get_local_ip() {
 const interfaces = require('os').networkInterfaces();
 let IPAdress = '';
 for (const devName in interfaces) {
 const iface = interfaces[devName];
 for (let i = 0; i < iface.length; i++) {
 const alias = iface[i];
 if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
 IPAdress = alias.address;
 }
 }
 }
 return IPAdress;
}

TCP 客户端

NodeJS 使用 net 模块创建 TCP 连接和服务。

启动与测试 TCP

const assert = require('assert');
const net = require('net');
let clients = 0;
let expectedAssertions = 2;
const server = net.createServer(function (client) {
 clients++;
 const clientId = clients;
 console.log('Client connected:', clientId);
 client.on('end', function () {
 console.log('Client disconnected:', clientId);
 });
 client.write('Welcome client: ' + clientId);
 client.pipe(client);
});
server.listen(8000, function () {
 console.log('Server started on port 8000');
 runTest(1, function () {
 runTest(2, function () {
 console.log('Tests finished');
 assert.equal(0, expectedAssertions);
 server.close();
 });
 });
});
function runTest(expectedId, done) {
 const client = net.connect(8000);
 client.on('data', function (data) {
 const expected = 'Welcome client: ' + expectedId;
 assert.equal(data.toString(), expected);
 expectedAssertions--;
 client.end();
 });
 client.on('end', done);
}

UDP 客户端

利用 dgram 模块创建数据报 socket,然后利用 socket.send 发送数据。

文件发送服务

const dgram = require('dgram');
const fs = require('fs');
const port = 41230;
const defaultSize = 16;
function Client(remoteIP) {
 const inStream = fs.createReadStream(__filename); // 从当前文件创建可读流
 const socket = dgram.createSocket('udp4'); // 创建新的数据流 socket 作为客户端
 inStream.on('readable', function () {
 sendData(); // 当可读流准备好,开始发送数据到服务器
 });
 function sendData() {
 const message = inStream.read(defaultSize); // 读取数据块
 if (!message) {
 return socket.unref(); // 客户端完成任务后,使用 unref 安全关闭它
 }
 // 发送数据到服务器
 socket.send(message, 0, message.length, port, remoteIP, function () {
 sendData();
 }
 );
 }
}
function Server() {
 const socket = dgram.createSocket('udp4'); // 创建一个 socket 提供服务
 socket.on('message', function (msg) {
 process.stdout.write(msg.toString());
 });
 socket.on('listening', function () {
 console.log('Server ready:', socket.address());
 });
 socket.bind(port);
}
if (process.argv[2] === 'client') { // 根据命令行选项确定运行客户端还是服务端
 new Client(process.argv[3]);
} else {
 new Server();
}

HTTP 客户端

使用 http.createServer 和 http.createClient 运行 HTTP 服务。

启动与测试 HTTP

const assert = require('assert');
const http = require('http');
const server = http.createServer(function(req, res) {
 res.writeHead(200, { 'Content-Type': 'text/plain' }); // 写入基于文本的响应头
 res.write('Hello, world.'); // 发送消息回客户端
 res.end();
});
server.listen(8000, function() {
 console.log('Listening on port 8000');
});
const req = http.request({ port: 8000}, function(res) { // 创建请求
 console.log('HTTP headers:', res.headers);
 res.on('data', function(data) { // 给 data 事件创建监听,确保和期望值一致
 console.log('Body:', data.toString());
 assert.equal('Hello, world.', data.toString());
 assert.equal(200, res.statusCode);
 server.unref();
 console.log('测试完成');
 });
});
req.end();

重定向

HTTP 标准定义了标识重定向发生时的状态码,它也指出了客户端应该检查无限循环。

  • 300:多重选择
  • 301:永久移动到新位置
  • 302:找到重定向跳转
  • 303:参见其他信息
  • 304:没有改动
  • 305:使用代理
  • 307:临时重定向
const http = require('http');
const https = require('https');
const url = require('url'); // 有很多接续 URLs 的方法
// 构造函数被用来创建一个对象来构成请求对象的声明周期
function Request() {
 this.maxRedirects = 10;
 this.redirects = 0;
}
Request.prototype.get = function(href, callback) {
 const uri = url.parse(href); // 解析 URLs 成为 Node http 模块使用的格式,确定是否使用 HTTPS
 const options = { host: uri.host, path: uri.path };
 const httpGet = uri.protocol === 'http:' ? http.get : https.get;
 console.log('GET:', href);
 function processResponse(response) {
 if (response.statusCode >= 300 && response.statusCode < 400) { // 检查状态码是否在 HTTP 重定向范围
 if (this.redirects >= this.maxRedirects) {
 this.error = new Error('Too many redirects for: ' + href);
 } else {
 this.redirects++; // 重定向计数自增
 href = url.resolve(options.host, response.headers.location); // 使用 url.resolve 确保相对路径的 URLs 转换为绝对路径 URLs
 return this.get(href, callback);
 }
 }
 response.url = href;
 response.redirects = this.redirects;
 console.log('Redirected:', href);
 function end() {
 console.log('Connection ended');
 callback(this.error, response);
 }
 response.on('data', function(data) {
 console.log('Got data, length:', data.length);
 });
 response.on('end', end.bind(this)); // 绑定回调到 Request 实例,确保能拿到实例属性
 }
 httpGet(options, processResponse.bind(this))
 .on('error', function(err) {
 callback(err);
 });
};
const request = new Request();
request.get('http://google.com/', function(err, res) {
 if (err) {
 console.error(err);
 } else {
 console.log(`
 Fetched URL: ${res.url} with ${res.redirects} redirects
 `);
 process.exit();
 }
});

HTTP 代理

  • ISP 使用透明代理使网络更加高效
  • 使用缓存代理服务器减少宽带
  • Web 应用程序的 DevOps 利用他们提升应用程序性能
const http = require('http');
const url = require('url');
http.createServer(function(req, res) {
 console.log('start request:', req.url);
 const options = url.parse(req.url);
 console.log(options);
 options.headers = req.headers;
 const proxyRequest = http.request(options, function(proxyResponse) { // 创建请求来复制原始的请求
 proxyResponse.on('data', function(chunk) { // 监听数据,返回给浏览器
 console.log('proxyResponse length:', chunk.length);
 res.write(chunk, 'binary');
 });
 proxyResponse.on('end', function() { // 追踪代理请求完成
 console.log('proxied request ended');
 res.end();
 });
 res.writeHead(proxyResponse.statusCode, proxyResponse.headers); // 发送头部信息给服务器
 });
 req.on('data', function(chunk) { // 捕获从浏览器发送到服务器的数据
 console.log('in request length:', chunk.length);
 proxyRequest.write(chunk, 'binary');
 });
 req.on('end', function() { // 追踪原始的请求什么时候结束
 console.log('original request ended');
 proxyRequest.end();
 });
}).listen(8888); // 监听来自本地浏览器的连接

封装 request-promise

const https = require('https');
const promisify = require('util').promisify;
https.get[promisify.custom] = function getAsync(options) {
 return new Promise((resolve, reject) => {
 https.get(options, (response) => {
 response.end = new Promise((resolve) => response.on('end', resolve));
 resolve(response);
 }).on('error', reject);
 });
};
const rp = promisify(https.get);
(async () => {
 const res = await rp('https://jsonmock.hackerrank.com/api/movies/search/?Title=Spiderman&page=1');
 let body = '';
 res.on('data', (chunk) => body += chunk);
 await res.end;
 console.log(body);
})();

DNS 请求

使用 dns 模块创建 DNS 请求。

  • A:dns.resolve,A 记录存储 IP 地址
  • TXT:dns.resulveTxt,文本值可以用于在 DNS 上构建其他服务
  • SRV:dns.resolveSrv,服务记录定义服务的定位数据,通常包含主机名和端口号
  • NS:dns.resolveNs,指定域名服务器
  • CNAME:dns.resolveCname,相关的域名记录,设置为域名而不是 IP 地址
const dns = require('dns');
dns.resolve('www.chenng.cn', function (err, addresses) {
 if (err) {
 console.error(err);
 }
 console.log('Addresses:', addresses);
});

crypto 库加密解密

const crypto = require('crypto')
function aesEncrypt(data, key = 'key') {
 const cipher = crypto.createCipher('aes192', key)
 let crypted = cipher.update(data, 'utf8', 'hex')
 crypted += cipher.final('hex')
 return crypted
}
function aesDecrypt(encrypted, key = 'key') {
 const decipher = crypto.createDecipher('aes192', key)
 let decrypted = decipher.update(encrypted, 'hex', 'utf8')
 decrypted += decipher.final('utf8')
 return decrypted
}

发起 HTTP 请求的方法

  • HTTP 标准库
  • 无需安装外部依赖
  • 需要以块为单位接受数据,自己监听 end 事件
  • HTTP 和 HTTPS 是两个模块,需要区分使用
  • Request 库
  • 使用方便
  • 有 promise 版本 request-promise
  • Axios
  • 既可以用在浏览器又可以用在 NodeJS
  • 可以使用 axios.all 并发多个请求
  • SuperAgent
  • 可以链式使用
  • node-fetch
  • 浏览器的 fetch 移植过来的

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表