文章目录
TCP
TCP基础
TCP协议:传输控制协议,提供面向连接的、可靠的数据传输服务
- 面向连接:数据传输之前,客户端与服务器端要建立连接
- 可靠的:数据传输是有序的、要对数据进行校验
Scoket:网络套接字,是进行网络连接的数据结构(服务器的Socket和客户端的Socket)
- 服务器的Socket:实现对指定端口号的监听,若有客户端向监听的端口发出连接请求,Socket就会监听到
- 客户端的Socket:向服务器端发送请求
TCP数据分组称为段(Segment)。TCP将来自应用层的数据进行分块并封装成TCP段进行发送。
TCP连接的建立采用三次握手方法。
Socket之间的连接过程包括以下3个步骤。
- 服务器监听。服务器端Socket处于等待连接的状态,实时监控网络状态。
- 客户端请求。客户端Socket提出连接请求,要连接的目标是服务器端的Socket。
- 连接确认。
net模块提供的API
Node中的net模块:实现TCP的连接和服务,封装了大多数的类和方法,方便用户使用TCP进行网络连接
导入该模块:
const net = require('net');
net.Server类: 服务器类,用于创建TCP服务器
创建net.Server对象:
const server = new net.Server([options][, connectionlistener])
const server = net.createServer([参数])
返回一个net.Server类的实例(对象)。
该方法可以创建一个TCP服务器,是创建net.Server对象的快捷方式。
用法:
net.createServer([options][, connectionlistener])
net.Server类实现的事件:
close
:当服务器关闭时被触发。如果有连接存在,则直到所有连接结束才会触发这个事件。connection
:当一个新的连接建立时被触发,Socket是一个net.Socket的实例对象。error
:发生错误时被触发。listening
:当服务被绑定后调用server.listen()方法。
net.Server类提供的方法:
server.address()
方法用于返回绑定的ip地址、地址族和服务端口。server.close()
方法使服务器停止接受建立新的连接并保持现有的连接server.listen()
方法用来启动一个服务器来监听连接。
net.Socket: 客户端类,用于创建TCP客户端
创建net.Socket对象:
const client = net.Socket([options])
net.Socket类实现的事件:
close
:当Socket完全关闭时发出该事件。connect
:当一个Socket连接成功建立时触发该事件。data
:当接收到数据时触发该事件。drain
:当写入缓冲区变为空时触发该事件。end
:当Socket的另一端发送一个FIN包时触发该事件,从而结束Socket的可读端。error
:当错误发生时触发该事件。close事件也会紧接着该事件被触发。ready
:当Socket准备使用时触发该事件。connect事件发生后立即触发该事件。timeout
:当Socket超时时触发该事件。
net.Socket类提供的方法:
socket.connect()
:用于在指定的Socket上启动一个连接。socket.write()
:用于在Socket上发送数据。
net.createConnection()方法
:
- 返回启动连接的net.Socket对象。
- 当连接建立之后,在返回的Socket上将触发一个connect事件。
- 语法格式:
net.createConnection(options[, connectListener])
net.createConnection(port[, host][, connectListener])
Node实现TCP
创建服务器端:
// 导入net模块
const net = require('net');
// 创建TCP服务器
const server = net.createServer((client) => { //'client'表示的客户端
let clientNo = 0; // 为每个连接到服务器的客户顿编号
console.log(clientNo+'号客户端已经连接');
// 绑定'end'事件:客户端断开连接时触发
client.on('end',function() {
console.log(clientNo+'号客户端断开连接!');
})
client.write(clientNo+'号客户端,你好!');
client.pipe(client);
// 绑定'data'事件,接收客户端的数据时触发
client.on('data',(data => {
console.log(clientNo+'号客户端发送的数据是:'+data.toString());
}))
});
// 绑定'error'事件:当连接发生错误时触发
server.on('error',(err => {
throw err;
}))
// 启动监听:指定监听的端口号
server.listen(8000,() => {
console.log('TCP服务器已经启动了!!!');
})
创建客户端:
const net = require('net');
//创建TCP客户端
var client = net.Socket();
//连接服务器:调用connect方法
client.connect(8000,'127.0.0.1',()=> {
console.log('已经连接到服务器');
client.write('我是TCP客户端'); //向服务器发送数据
})
//接收服务器端数据:绑定'data'事件
client.on('data',(data)=> {
console.log('接收的服务器端数据:'+data.toString());
})
//绑定'end'事件
client.on('end',()=> {
console.log('结束结束数据!');
})
UDP
UDP基础
UDP协议:用户数据报协议,提供的是不可靠的、面向无连接的传输服务(只有数据的发送方和接收方)
- 面向无连接:在传输数据之前没有明确的连接链路(即不是所有的数据都是通过一条链路传输)
- 不可靠的:因为数据传输不是通过一条链路完成的,因此接收方接收的数据不一定是按发送的顺序接收,这样就可能造成数据包的丢失
- UDP传输给IP的数据单元称作UDP数据报(Datagram)。
- UDP使用端口号为不同的应用保留其各自的数据传输通道。UDP使用Socket,只不过是无连接的数据报Socket。
dgram模块
dgram核心模块用于实现UDP通信。
导入该模块:
const dgram = require('dgram');
dgram.Socket类
- dgram.Socket类提供实现UDP应用的基本框架。
- dgram.Socket对象是一个封装了数据报功能的事件触发器。
- dgram.Socket实例由dgram.createSocket()方法创建。
dgram.Socket类实现的事件:
close
:使用close()方法关闭一个Socket之后触发该事件。error
:发生任何错误都会触发该事件。listening
:当一个Socket开始监听数据报信息时触发该事件。message
:当有新的数据报被Socket接收时触发该事件。
dgram.Socket类提供的方法:
socket.bind()
方法用于设置Socket在指定的端口和地址上监听数据报信息。socket.send()
方法用于在Socket上发送一个数据报。
dgram.createSocket()方法
- 该方法用于创建dgram.Socket对象。一旦创建了Socket,调用socket.bind()方法会指示Socket开始监听数据报消息。
- 基本用法:dgram.createSocket(options[, callback])
- 创建一个特定类型的dgram.Socket对象:dgram.createSocket(type[, callback])
Node实现UDP
服务器:
// 导入dgram模块
const dgram = require('dgram');
// 创建dgram.Socket对象(服务器端---数据的接收端)
const server = dgram.createSocket('udp4');
// 绑定'error'事件
server.on('error',(err => {
console.log(`服务器异常:${err.stack}`);
}))
// 绑定'message'事件:参数msg客户端发送的数据,
// 参数rinfo封装了客户端的信息(包括地址、端口号等)
server.on('message',((msg,rinfo) => {
var str_msg = msg.toString();
console.log(`服务器接收来自:${rinfo.address}:${rinfo.port}的${str_msg}`);
console.log(rinfo);
//将接收到的消息返回给客户端
server.send('南栀',rinfo.port,rinfo.address);
}))
// 注册监听
server.on('listening',() => {
// 获取服务器端的地址
var server_address = server.address();
console.log(`服务器监听:${server_address.address}:${ server_address.port }`);
})
// 绑定端口
server.bind(8089);
客户端:
const dgram = require('dgram');
const client_msg = Buffer.from('你好,我是UDP客户端');
// 创建客户端的Socket
const client = dgram.createSocket('udp4');
// 绑定事件
client.on('close',() => {
console.log('Socket已关闭');
})
client.on('error',() => {
console.log(err);
})
client.on('message',((msg,rinfo) => {
if(msg === 'exit'){
client.close();
}
console.log(`接收到来自服务器:${rinfo.address}:${rinfo.port}的信息:${msg.toString()}`);
}))
// 向服务器端发送数据
client.send(client_msg,8089,'localhost',(error => {
if(error) {
client.close();
}
}))
HTTP
HTTP基础
HTTP是一种通用的、无状态的、与传输数据无关的应用层协议。
客户端与服务器之间的HTTP交互过程:
- 客户端和服务器建立TCP连接
- 客户端向服务器发送HTTP请求报文
- 服务器向客户端返回HTTP响应报文
- 关闭HTTP连接。
http模块
导入HTTP模块:
const http = reuqire('http')
http.Server类:
- http.Server类提供了实现HTTP服务器的基本框架。
http.Server类主要实现的功能
- 基于TCP连接建立一个网络监听器。
- 监听自身的request请求事件。
http.createServer()方法:
- 要启动HTTP服务器,需要使用http.createServer()方法创建一个http.Server对象。
- 该方法的用法:
http.createServer([options][, requestlistener])
http.Server类实现的事件:
request
:当有请求时会触发该事件,提供两个参数request和response,分别为http.IncomingMessage对象和http.ServerResponse对象,表示请求和响应的信息。connect
:客户端请求HTTP的CONNECT方法时触发该事件。connection
:当TCP建立连接时触发该事件,提供参数socket通常表示net.Socket对象。close
:当服务器关闭时触发该事件。clientError
:如果客户端连接触发error事件,则会在此处转发。
http.Server类提供的方法:
server.listen()
:启动HTTP服务器监听连接。server.close()
:停止服务器接受新连接。
http.IncomingMessage类——传入的信息
- http.IncomingMessage对象(该类实例)由http.Server或http.ClientRequest创建。
http.IncomingMessage类实现的事件:
aborted
:当请求中止时被触发。close
:表明底层连接已关闭。
http.IncomingMessag属性:
http.ServerResponse类——响应
- 指定要发送到客户端的响应
http.ServerResponse类实现的事件:
close
:调用response.end()方法,或者能够刷新之前已终止的底层连接。finish
:响应发送后触发该事件。
关于响应头的额外方法:
response.setHeader(name, value)
:设置一个特定的响应头。getHeader(name)
:获取已在响应中设置的某个响应头。removeHeader(name)
:移除已在响应中设置的某个响应头。response.addTrailers(headers)
:将HTTP尾部响应头添加到响应中。response.writeHead(statusCode,[reasonPhrase],[headers])
:将某个响应头写入请求。
关于响应体的方法:
response.write(data,[encoding])
:发送响应体。response.writeContinue()
:向客户端发送HTTP/1.1 100 Continue消息,表示应发送请求体。response.end([data][,encoding][,callback])
:结束响应,向服务器表明已发送所有的响应头和响应体。
属性:
response.statusCode
:刷新响应头时将发送到客户端的状态码。response.statusMessage
:刷新响应头时将发送到客户端的状态消息。
http.ClientRequest类
- http.ClientRequest类提供了实现HTTP客户端的基本框架。
- 通过http.request()方法创建并返回一个http.ClientRequest对象,作为HTTP客户端,启动、监控和处理来自服务器的响应。
http.request()方法:
- 构建一个HTTP客户端,需使用http.request()方法创建一个ClientRequest对象。
- 该方法的两种用法:
http.request(options[, callback])
http.request(url[, options][, callback])
实现的事件:
abort
:当请求被客户端中止时被触发。connect
:每次服务器使用CONNECT方法响应请求时都会触发该事件。continue
:当服务器发送“100 Continue”HTTP响应时被触发。response
:当收到此请求的响应时被触发。此事件仅触发一次。socket
:将Socket分配给此请求后被触发。timeout
:当底层Socket因处于不活动状态而超时时被触发。
用于请求头的方法:
request.setHeader(name, value)
:设置一个特定的请求头。request.getHeader(name)
:读取请求中的一个请求头。request.flushHeaders()
:刷新请求头。
用于请求体的方法:
request.write(chunk[, encoding][, callback])
:发送一个请求体的数据块。request.end([data[, encoding]][, callback])
:完成发送请求。request.abort()
:终止当前的请求。request.setTimeout(timeout,[callback])
:为请求设置Socket超时时间。
Node实现http
服务器:
// 导入http模块
const http = require('http');
/*
创建http服务器:参数'req'是请求对象request,参数'res'是响应对象response
'req':获取客户端的请求数据
'res':向客户端发送响应数据
*/
const server = http.createServer((req,res) => {
/*
设置响应头信息:
状态码:200 表示请求-响应成功
content-type:响应信息的格式和字符集(编码格式)
*/
res.writeHead(200,{'content-type':'text/html;charset=utf8'});
res.write('<h2>Hello World!</h2>');
res.end();
})
// 绑定端口并进行监听
server.listen(8080,() => {
console.log('服务器在8080端口上进行监听!')
})
客户端:
// 导入HTTP模块
const http = require('http');
// 导入querystring模块
const querystring = require('querystring');
// 创建请求数据:通过querystring模块的stringify方法将请求数据转换为JSON格式
const postData = querystring.stringify({'msg':'你好,我是http客户端'});
// 创建请求的配置信息:服务器地址、端口、请求资源地址、请求方式、请求头信息
const options = {
hostname: '127.0.0.1',
port: 8080,
path: '/upload',
method: 'post',
headers: {
'Content-type': 'application/x-www-form-urlencoded;charset=utf-8',
'Contnet-Length': Buffer.byteLength(postData)
}
}
// 向服务器发送请求:创建请求对象
const req = http.request(options,(res) => {
console.log(`状态码:${res.statusCode}`);
console.log(`响应头信息:${JSON.stringify(res.headers)}`);
res.setEncoding('utf-8');
res.on('data',chunk => {
console.log('响应体:'+chunk);
})
res.on('end',() => {
console.log('请求结束');
})
res.on('error',(err) => {
console.log(err);
})
})
// 将请求数据写入请求体
req.write(postData);
// 请求结束
req.end();
HTTP服务器获取并解析GET请求内容
服务器:
const http = require('http');
const url = require('url');
const queryString = require('querystring');
// 创建get服务器
var server = http.createServer((req,res) => {
// 对客户端的url进行解码
let reqUrl = decodeURI(req.url);
// 将url字符串转换为url对象
reqUrl = url.parse(reqUrl);
let params = reqUrl.query.split('&');
console.log(params[0]);
res.end('提交成功!');
});
// 绑定并监听端口
server.listen(8080);
客户端:
const http = require('http');
// 设置请求的配置信息
const options = {
hostname: '127.0.0.1',
port: 8080,
path: encodeURI('/index.html?age=12&name=张三')
}
// 创建get请求对象向服务器发送get请求
var geter = http.get(options,res => {
res.setEncoding('utf8');
res.on('data',chunk => {
console.log('响应信息:'+chunk.toString());
})
})
geter.on('error',err => {
console.log(err.message);
})
HTTP服务器获取并解析POSt请求内容
服务器:
const http = require('http');
const queryString = require('querystring');
// 创建post请求的服务器对象
const server = http.createServer((req,res) => {
// 'data'变量用来接收客户端向服务器发送的请求数据
let data = '';
// 参数'chunk'中存放的是客户端向服务器发送的请求数据,是二进制格式
// 参数'chunk'在和变量'data'连接时自动转换为字符串
req.on('data',chunk => {
data += chunk;
})
// 给请求对象'req'绑定'end'事件,当请求结束时触发该事件
req.on('end', () => {
data = decodeURI(data); //对请求数据进行解码
console.log('请求数据:'+data);
// 将请求数据转换为对象
let dataObj = queryString.parse(data);
console.log('姓名:',dataObj.name);
console.log('学历:',dataObj.title);
res.end('提交成功!');
})
})
server.listen(8080);
客户端:
const http = require('http');
const queryString = require('querystring');
// 将要提交给服务器的数据转换为JSON格式
var postData = queryString.stringify({
'name': '关羽',
'title': '博士'
})
// 创建配置信息
const options = {
hostname: '127.0.0.1',
port: 8080,
method: 'post'
}
// 创建post请求对象
const req = http.request(options,res => {
res.setEncoding('utf-8');
res.on('data',chunk => {
console.log('响应数据:'+chunk.toString());
})
})
// 将请求数据写入到请求对象中
req.write(postData);
// 结束请求
req.end();
通过Node的http模块访问服务器端的静态资源
静态资源(css文件、图像、html文件)
-
创建一个Node项目
-
项目目录结构
-
main.css
body{
background-color: pink;
text-align: center;
} -
index.html
首页 首页
-
loginhtml
登录 登录
-
404.html
404 404 资源不能在
-
server.js
const http = require(‘http’);
const fs = require(‘fs’);
const path = require(‘path’);//1.创建http服务器
var server = http.createServer();//2.给http服务器绑定’request’事件
server.on(‘request’,(req,res)=> {
//2.1 获取客户端的url
let url = req.url;console.log(__dirname); //2.2 对客户端的url进行判断,根据判断的结果读取不同的文件 if(url === '/'){ fs.readFile(path.join(__dirname,'/html/login.html'),'utf8',(err, data) =>{ if(err){ throw err; } res.end(data); }) }else if(url === '/login'){ fs.readFile(path.join(__dirname,'/html/login.html'),'utf8',(err, data) =>{ if(err){ throw err; } res.end(data); }) }else if(url === '/css/main.css'){ fs.readFile(path.join(__dirname,'/css/main.css'),'utf8',(err, data) =>{ if(err){ throw err; } res.end(data); }) }else if(url === '/images/7.png'){ fs.readFile(path.join(__dirname,'/images/7.png'),(err, data) =>{ if(err){ throw err; } res.end(data); }) }else{ fs.readFile(path.join(__dirname,'/html/404.html'),'utf8',(err, data) =>{ if(err){ throw err; } res.end(data); }) }
});
//3.绑定端口,并进行监听
server.listen(3000,()=> {
console.log(‘服务器在3000端口上进行监听’);
})
WebSocket
WebSocket概述
为什么需要WebSocket协议:
- HTTP存在的问题:
- 每次客户端和服务器端的交互都是一次HTTP的请求和应答的过程,增加了每次传输的数据量,浪费带宽资源。
- 不是真正的实时技术,只是模拟实时的效果,轮询会造成同步延迟。
- 编程比较复杂,尤其是要模拟比较真实的实时效果时。
- WebSocket协议突破HTTP的局限性,节省服务器资源和带宽,解决两端的实时数据传输问题。
WebSocket协议的实现机制:
- 由客户端发起握手,建立连接阶段必须依赖HTTP进行一次握手。两端之间的WebSocket连接建立的同时服务器完成了协议升级,由HTTP升级为WebSocket。连接会一直保持,直到客户端或者服务器任何一方主动关闭。
- 进入数据交换阶段,客户端与服务器可以互相主动发送消息。此阶段数据直接通过TCP通道传输,不再依赖HTTP。WebSocket的数据传输是以帧(Frame)的形式传输的。
- 关闭连接,可以由任意一端发起关闭连接的命令。
WebSocket协议的应用场合:
- 实时通信:聊天应用。
- 实时展示和分析:典型的有实时计数器、图表、日志客户端等。
- 文档协同:允许多个用户同时编辑一个文档,且用户能够看到每个用户做出的修改。
- WebSocket协议不适合那些不支持HTML5或对HTML5支持不够充分的浏览器。
ws库
WebSocket库:ws、WebSocket-Node、faye-websocket-node和socket.io。
导入库
const WebSocket = require('ws')
WebSocket服务器
创建一个WebSocket服务器实例:
new WebSocket.Server(options[, callback])
WebSocket服务器内置事件:
close
:服务器关闭时被触发connection
:成功握手连接时触发error
:发生错误时被触发,可注入一个Error对象headers
:握手前被触发,允许在发送HTTP头之前检查和修改标题listening
:绑定端口时被触发
WebSocket客户端
创建一个WebSocket客户端实例:
new WebSocket(address[, protocols][, options])
WebSocket客户端内置事件:
close
:连接关闭时被触发,有两个参数code(状态码)和reason(原因)error
:发生错误时被触发,有一个参数error(错误)message
:接收到服务器消息时被触发,有一个参数data,表示接收到的数据,类型可以是字符串、Buffer、ArrayBufferopen
:连接建立时被触发
Node实现WebSocket
-
server.js
// 1. 安装ws模块 npm install ws
// 2. 导入ws模块
const WebSocket = require(‘ws’);// 3. 创建WebSocket服务器并在8080端口监听
const server = new WebSocket.server({port:8080});// 4. 绑定open事件
server.on(‘open’,() => {
console.log(‘建立连接’);
})// 5. 绑定close事件
server.on(‘close’,() => {
console.log(‘断开连接’)
})// 6. 绑定’connection’事件 回调函数中’ws’参数表示客户端 'req’表示客户端的请求
server.on(‘connection’,(ws,req) => {
// 6.1 获取客户端的ip、port
const ip = req.connection.remoteAddress;
const port = req.connection.remotePort;
const clientName = ip + port;console.log('%s is connect',clientName); // 6.2 向客户端发送消息 ws.send('欢迎,'+clientName); ws.on('message',(msg) => { console.log('消息:%s 来自于 %s',msg,clientName); // 6.3 把消息广播给所有的客户端:'clients'代表所有客户端 server.clients.forEach(function(client){ if(client.readyState === WebSocket.OPEN) { client.send(clientName+'--->'+msg); } }) })
})
-
client.html
聊天室