>[success] # 监听主机和端口号
1. 我们在启动一个服务时,需要指定这个服务的监听地址(host)和监听端口(port),用于接收来自其他设备或进程的连接请求。**通过ip:port的方式发送到我们监听的Web服务器上时**
| 地址 | 含义 |
| --- | --- |
| localhost | 指本机。通常被解析为 127.0.0.1 地址,用于指向本机上单个应用程序或进程内部的通信接口。 |
| 127.0.0.1 | 本机回环地址。它指向的是本机本地的一个虚拟网卡,用于在本机上的进程之间进行网络通信。 |
| 0.0.0.0 | 指本机上所有 IP 地址,用于让服务监听在本机的所有网络接口上等待连接请求,可以对其他设备或进程对于本机的任何 IP 地址发起的连接请求进行响应。有时也成为通配符地址。 **监听IPV4上所有的地址** ,再根据端口找到不同的应用程序|
| 网卡对应 IP | 网卡对应的 IP 地址,例如 192.168.1.100。如果要监听来自本地网络中其他设备对于本机的连接请求,可以使用网卡对应的 IP。 |
2. **关于监听端口**,端口占用两个字节 **256 * 256 = 65536** ,也就 **0 - 65536** 的端口号都能使用,但是一般 **0-1024**的都是特殊端口,推荐自定义的范围在**1025~65535**之间的端口
* 以node 为例
~~~
const http = require('http');
const server1 = http.createServer((req, res) => {
// 请求处理逻辑
});
// 监听在 localhost:8080
server1.listen(8080, 'localhost', () => {
console.log('Server is listening on localhost:8080');
});
const server2 = http.createServer((req, res) => {
// 请求处理逻辑
});
// 监听在 127.0.0.1:8081
server2.listen(8081, '127.0.0.1', () => {
console.log('Server is listening on 127.0.0.1:8081');
});
const server3 = http.createServer((req, res) => {
// 请求处理逻辑
});
// 监听在 0.0.0.0:8082 包括回环地址 127.0.0.1 和其他网卡对应的 IP 地址
server3.listen(8082, '0.0.0.0', () => {
console.log('Server is listening on 0.0.0.0:8082');
});
const server4 = http.createServer((req, res) => {
// 请求处理逻辑
});
// 监听在 192.168.1.100:8083(假设网卡的 IP 是 192.168.1.100)
server4.listen(8083, '192.168.1.100', () => {
console.log('Server is listening on 192.168.1.100:8083');
});
~~~
>[info] ## 创建 http服务
| API | 参数 | 介绍 |
| --- | --- | --- |
| http.createServer(\[requestListener\]) | requestListener(request, response) | 创建 Http 服务器实例,requestListener 为请求事件的回调函数 |
| server.listen(port\[, hostname\]\[, backlog\]\[, callback\]) | port (Number):监听的端口号hostname (String):主机名backlog (Number):等待队列最大长度callback (Function):服务器启动后的回调函数 | 启动 Http 服务器,指定端口和主机名等配置信息 |
* **传入一个回调函数,这个回调函数在 被调用时会传入两个参数**,`req:reques`t请求对象,包含请求相关的信息, `res:response`响应对象,包含我们要发送给客户端的信息;
**请求对象常用属性和方法**
| 属性/方法 | 描述 |
| --- | --- |
| `req.method` | 请求方法(例如 GET、POST、PUT 等) |
| `req.url` | 请求 URL 的完整信息(包括协议、主机名、路径和查询参数等) |
| `req.headers` | 请求头对象,由键值对构成,可通过属性名或`getHeader()`方法获取对应的值 |
| `req.socket` | 底层套接字(socket)对象,可以用于查看底层传输协议的信息 |
| `req.httpVersion` | 请求使用的 HTTP 版本号 |
| `req.on('data', callback)` | 注册`data`事件的回调函数,用于接收消息主体的数据块 |
| `req.on('end', callback)` | 注册`end`事件的回调函数,表明消息主体读取完毕 |
**响应对象常用属性和方法**
| 属性/方法 | 描述 |
| --- | --- |
| `res.statusCode` | 状态码(例如 200、404 等) |
| `res.statusMessage` | 状态消息(例如 OK、Not Found 等) |
| `res.setHeader(name, value)` | 设置响应头信息,用于声明服务器所发送的内容类型、编码等信息 |
| `res.writeHead(statusCode[, statusMessage][, headers])` | 一次性写入相应头信息 |
| `res.write(chunk[, encoding][, callback])` | 向客户端发送内容,可以多次调用 |
| `res.end([chunk][, encoding][, callback])` | 结束响应,通常在数据发送完毕后调用 |
>[danger] ##### 案例
~~~
const http = require('http')
// 创建http 服务
const server = http.createServer((req, res) => {
/**
* 传入一个回调函数,这个回调函数在 被调用时会传入两个参数
* req:request请求对象,包含请求相关的信息
* res:response响应对象,包含我们要发送给客户端的信息;
*/
// 1.url信息
console.log(req.url)
// 2.method信息(请求方式)
console.log(req.method)
// 3.headers信息(请求信息)
console.log(req.headers)
// 设置响应头
res.setHeader('Content-Type', 'text/html;charset=utf-8')
// 利用响应在页面输出内容, res: response对象 => Writable可写流,write 可以使用多次,但是最后一定要使用 end 来结束响应,否则客户端会一直等待
res.write('你好', 'utf-8', () => {
console.log('发送成功')
})
// 在每次响应结束后用end方法,才能保证结束响应,告诉客户端,我的话说完了,你可以呈递给用户了
res.end('结尾')
})
// 可以监听多个端口 在没有指定host 默认启动的就是 0.0.0.0在同一个网段下的主机中,通过ip地址是可以访问的
server.listen(8080, () => {
console.log('启动8080 端口服务: http://localhost:8080')
})
~~~
* 响应数据后使用`res.end()`来结束本次响应。在结束之前,你可以使用`res.write()`多次写入响应数据,但必须在最后使用`res.end()`来结束此次响应。否则客户端将一直等待服务器的响应数据,导致请求一直处于等待状态(**因为是流在使用流的时候要关闭**)
![](https://img.kancloud.cn/0e/12/0e1237b7beeba9ce0722524541cd2560_944x518.png)
>[danger] ##### 设置响应头
1. `res.setHeader`:一次写入一个头部信息;/` res.writeHead`:同时写入header和status
2. 如果不规定响应头浏览器默认按照当前操作系统的默认编码去解析,win是gbk所以会出现请求后出现乱码的情况,text/plain 是普通文本,text/html是将html解析后展示,`setHeader('Content-Type', 'text/plain; charset=utf-8') `告诉浏览器用utf-8解析
~~~
const http = require('http')
// 1.创建server服务器
const server = http.createServer((req, res) => {
// 设置header信息: 数据的类型以及数据的编码格式
// 1.单独设置某一个header
// res.setHeader('Content-Type', 'text/plain;charset=utf8;')
// 2.和http status code一起设置
res.writeHead(200, {
'Content-Type': 'application/json;charset=utf8;',
})
// 3. statusCode
// res.statusCode = 403
// 4. setHead 响应头
res.writeHead(401)
const list = [{ name: '1', age: 18 }]
res.end(JSON.stringify(list))
})
// 2.开启server服务器
server.listen(8000, () => {
console.log('服务器开启成功~')
})
~~~
>[danger] ##### 获取请求连接中的参数
~~~
const http = require('http')
const url = require('url')
const qs = require('querystring')
// 1.创建server服务器
const server = http.createServer((req, res) => {
// 1.参数一: query类型参数
// /home/list?offset=100&size=20
// 1.1.解析url
const urlString = req.url
const urlInfo = url.parse(urlString)
// 1.2.解析query: offset=100&size=20
const queryString = urlInfo.query
const queryInfo = qs.parse(queryString)
console.log(queryInfo.offset, queryInfo.size)
res.end('hello world aaaa bbb')
})
// 2.开启server服务器
server.listen(8000, () => {
console.log('服务器开启成功~')
})
~~~
>[danger] ##### 服务端获取请求头数据
1. keep-alive,http是基于TCP协议的,但是通常在进行一次请求和响应结束后会立刻中断,在http1.0中,如果想要继续保持连接
* 浏览器需要在请求头中添加connection: keep-alive;
* 服务器需要在响应头中添加connection:keey-alive;
* 当客户端再次放请求时,就会使用同一个连接,直接一方中断连接;
2. http1.1中,所有连接默认是connection: keep-alive的;
* 不同的Web服务器会有不同的保持keep-alive的时间
* Node中默认是5s中
~~~
const http = require('http')
const url = require('url')
// 1.创建server服务器
const server = http.createServer((req, res) => {
console.log(req.headers)
console.log(req.headers['content-type'])
// cookie/session/token
const token = req.headers['authorization']
console.log(token)
res.end('查看header的信息~')
})
// 2.开启server服务器
server.listen(8000, () => {
console.log('服务器开启成功~')
})
~~~
>[danger] ##### 服务端获取请求体中数据
1. `req.on('data')`是 Node.js 中 HTTP 请求对象中的一个事件,用于获取 POST 方法提交的数据。
* 具体来说,当客户端向服务端发送一个 POST 请求时,请求数据会被包含在 HTTP 请求体中,而 Node.js 的`http`模块中的`req`对象(即 HTTP 请求对象)可以通过监听`'data'`事件来逐步获取请求体中的数据。这个事件会在每次获取到数据的时候触发,数据会以 Buffer 的形式作为回调函数的参数传递
~~~
const http = require('http');
http.createServer((req, res) => {
if (req.method === 'POST') {
let postData = '';
// 获取客户端请求头里面的数据
req.on('data', chunk => {
postData += chunk;
});
req.on('end', () => {
console.log(postData);
res.end('Data received\n');
});
} else {
res.end('Hello World\n');
}
}).listen(3000);
~~~
2. 创建了一个 HTTP 服务器,并在其中监听`'data'`事件和`'end'`事件。当发起 POST 请求时,服务端会在每次接收到数据时触发`'data'`事件,并将数据以 Buffer 的形式传入回调函数中。我们可以将这些数据片段拼接起来,得到完整的请求体数据。当请求体数据接收完毕时,`'end'`事件会被触发,我们可以在其回调函数中做出响应。若是 GET 请求,则直接响应`"Hello World\n"`。
~~~
let postData = JSON.stringify({
'msg': 'Hello World!'
});
let options = {
hostname: 'localhost',
port: 3000,
path: '/',
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': postData.length
}
};
// 发起对服务端的请求数据
let req = http.request(options, res => {
console.log(`statusCode: ${res.statusCode}`);
res.on('data', chunk => {
console.log(`BODY: ${chunk}`);
});
});
req.on('error', error => {
console.error(error);
});
req.write(postData);
req.end();
~~~
>[info] ## 综合案例
>[danger] ##### 根据url访问模拟网站请求
~~~
var http = require('http')
// 1. 创建 Server
var server = http.createServer()
// 2. 监听 request 请求事件,设置请求处理函数
server.on('request', function (req, res) {
console.log('收到请求了,请求路径是:' + req.url)
console.log('请求我的客户端的地址是:', req.socket.remoteAddress, req.socket.remotePort)
// res.end('hello nodejs')
// 根据不同的请求路径发送不同的响应结果
// 1. 获取请求路径
// req.url 获取到的是端口号之后的那一部分路径
// 也就是说所有的 url 都是以 / 开头的
// 2. 判断路径处理响应
var url = req.url
if (url === '/') {
res.end('index page')
} else if (url === '/login') {
res.end('login page')
} else if (url === '/products') {
var products = [{
name: '苹果 X',
price: 8888
},
{
name: '菠萝 X',
price: 5000
},
{
name: '小辣椒 X',
price: 1999
}
]
// 响应内容只能是二进制数据或者字符串
res.end(JSON.stringify(products))
} else {
res.end('404 Not Found.')
}
})
// 3. 绑定端口号,启动服务
server.listen(3000, function () {
console.log('服务器启动成功,可以访问了。。。')
})
~~~
>[danger] ##### 利用文件的读取
1. 利用读取文件响应,其中响应到浏览器的内容,算然读取出来的的是16进制,但是res.end,既可以toString()一下,也可以省略
~~~
var http = require('http')
var fs = require('fs')
var server = http.createServer()
server.on('request', function (req, res) {
// / index.html
var url = req.url
if (url === '/') {
// 我们要发送的还是在文件中的内容
fs.readFile('../views/index.html', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('文件读取失败,请稍后重试!')
} else {
// data 默认是二进制数据,可以通过 .toString 转为咱们能识别的字符串
// res.end() 支持两种数据类型,一种是二进制,一种是字符串
res.setHeader('Content-Type', 'text/html; charset=utf-8')
res.end(data)
}
})
} else if (url === '/xiaoming') {
fs.readFile('../resource/ab2.jpg', function (err, data) {
if (err) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('文件读取失败,请稍后重试!')
} else {
// data 默认是二进制数据,可以通过 .toString 转为咱们能识别的字符串
// res.end() 支持两种数据类型,一种是二进制,一种是字符串
// 图片就不需要指定编码了,因为我们常说的编码一般指的是:字符编码
res.setHeader('Content-Type', 'image/jpeg')
res.end(data)
}
})
}
})
server.listen(3000, function () {
console.log('Server is running...')
})
~~~
>[warning] ## 注意
>[danger] ##### 修改代码后帮自动自动服务
1. `nodemon`可以在检测到代码变更时自动重启 Node.js 服务
~~~
npm install -g nodemon
~~~
在启动服务时使用`nodemon`命令替代`node`命令即可
~~~
nodemon app.js
~~~
当修改完代码后保存文件,`nodemon`就会检测到变更并自动重启服务器,从而使新的代码生效。
>[danger] ##### 在访问过程中发现触发两次
1. 是因为通过浏览器请求时候,访问了`/favicon.ico`一次额外请求导致我们运行下面代码时候打印两次
~~~
const http = require('http')
// 1.创建server服务器
const server = http.createServer((req, res) => {
console.log('服务器被访问~')
res.end('hello world aaaa')
})
// 2.开启server服务器
server.listen(8000, () => {
console.log('服务器开启成功~')
})
~~~
2. 因为上面代码是所有连接访问都会接受到,浏览器的默认`/favicon.ico ` 也额外接到了,可以通过判断`request `返回的指定`url `限制针对指定的条件返回,这样就会是数以自己的被打印出来
- 基础
- 什么是Node.js
- 理解 I/O 模型
- 理解node 中 I/O
- 对比node 和java 使用场景
- node 模块管理
- 内置模块 -- buffer
- 内置模块 -- fs
- fs -- 文件描述符
- fs -- 打开文件 api
- fs -- 文件读取 api
- fs -- 文件写入 api
- fs -- 创建目录 api
- fs -- 读取文件目录结构 api
- fs -- 文件状态(信息) api
- fs -- 删除文件/目录 api
- fs -- 重命名 api
- fs -- 复制文件 api
- 内置模块 -- events
- 内置模块 -- stream
- 可读流 -- Readable
- 可写流 -- Writable
- Duplex
- Transform
- 内置模块 -- http
- http -- 从客户端发起
- http -- 从服务端发起
- 内置模块 -- url
- 网络开发