网络通信方式
网络层次模型
OSI 七层模型
- 应用层: 用户与网络的接口
 
- 表示层: 数据加密,转换,压缩
 
- 会话层: 控制网络连接建立与中止
 
- 传输层: 控制数据传输可靠性
 
- 网络层: 确定目标网络
 
- 数据链路层: 确定目标主机
 
- 物理层: 各种网络屋里设备
 
TCP 四层模型
数据封装与解封装
TCP 三次握手与四次挥手
TCP 协议
- TCP 属于传输层协议
 
- TCP 是面向连接的协议
 
- TCP 用于处理实时通信
 
本质上请求连接或请求断开都是四次连接,但是请求断开中服务端请求不能合并成一条.
常见控制字段
- SYN=1: 请求建立连接
 
- FIN=1: 请求断开连接
 
- ACK=1: 数据信息的确认
 
本来应该是两次一来一回,共四次,但是服务端这里可以将请求和确认合并发送,就是三次握手连接.

断开连接:
变成四次是因为确保服务端已经将数据全部传输完毕,所以不能进行合并.
创建 TCP 通信
通信过程
- 创建服务端: 接收和诙谐客户端数据
 
- 创建客户端: 发送接收服务端数据
 
- 数据传输: 内置服务事件和方法读写数据
 
通信事件
- listening 事件: 调用 server.listen 方法后触发
 
- connection 事件: 新的连接建立时触发
 
- close 事件: 当 server 关闭时触发
 
- error 事件: 当错误出现时触发
 
通信事件&方法
- data 事件: 当接收到数据时触发
 
- write 方法: 在 socket 上发送数据,默认 UTF-8
 
- end 操作: 当 socket 的一端发送 FIN 包时触发,结束可读端
 
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
   |  const net = require("net");
 
  const server = net.createServer(); const PORT = 1234; const HOST = "localhost";
  server.listen(PORT, HOST);
 
  server.on("listening", () => {   console.log(`服务端已开启在 ${HOST}: ${PORT}`); });
 
  server.on("connection", (socket) => {   socket.on("data", (chunk) => {     const msg = chunk.toString();     console.log(msg);
           socket.write(Buffer.from("您好" + msg));   }); });
  server.on("close", () => {   console.log("服务端关闭"); }); server.on("error", (err) => {   if (err.code === "EADDRINUSE") {     console.log("地址占用");   } else {     console.log(err);   } });
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
   |  const net = require("net");
  const client = net.createConnection({   port: 1234,   host: "127.0.0.1", });
  client.on("connect", () => {   client.write("edu"); });
  client.on("data", (chunk) => {   console.log(chunk.toString()); });
  client.on("close", () => {   console.log("客户端断开连接"); }); client.on("error", (err) => {   console.log(err); });
 
  | 
 
TCP 数据粘包
发送端和接收端都是等待缓冲数据之后再消费.
出现问题的原因: 发送间隔太短导致数据堆积.
数据的封包和拆包
将消息头拆分为序列号和消息长度
数据传输过程
- 进行数据编码,获取二进制数据包
 
- 按规则拆解数据,获取指定长度数据
 
Buffer 数据读写
- writeInt16BE: 将 value 从指定位置写入
 
- readInt16BE: 从指定位置开始读取数据
 
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 41 42 43 44 45 46 47 48 49 50
   |  class myTransform {   constructor() {          this.packageHeaderLen = 4;          this.serialNum = 0;     this.serialLen = 2;   }
       encode(data, serialNum) {          const body = Buffer.from(data);          const headerBuf = Buffer.alloc(this.packageHeaderLen);          headerBuf.writeInt16BE(serialNum || this.serialNum);          headerBuf.writeInt16BE(body.length, this.serialLen);           if (serialNum === undefined) {       this.serialNum++;     }     return Buffer.concat(headerBuf, body);   }
       decode(buffer) {     const headerBuf = buffer.slice(0, this.packageHeaderLen);     const bodyBuf = buffer.slice(this.packageHeaderLen);
      return {       serialNum: headerBuf.readInt16BE(),       bodyLength: headerBuf.readInt16BE(this.serialLen),       body: bodyBuf.toString(),     };   }
       getPackageLen(buffer) {     if (buffer.length < this.packageHeaderLen) {       return 0;     } else {       return this.packageHeaderLen + buffer.readInt16BE(this.serialLen);     }   } }
  module.exports = myTransform;
 
  | 
 
数据粘包解决
利用上面的编解码工具类
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
   |  const myTransform = require("./myTransform.js");
  let overageBuffer = null; let ts = new myTransform();
 
  server.on("connection", (socket) => {   socket.on("data", (chunk) => {          if (overageBuffer) {              chunk = Buffer.concat([overageBuffer, chunk]);     }
      let packageLen = 0;          while ((packageLen = ts.getPackageLen(chunk))) {              const packageCon = chunk.slice(0, packageLen);              chunk = chunk.slice(packageLen);
        const ret = ts.decode(packageCon);              socket.write(ret.body, ret.serialNum);     }          overageBuffer = chunk;   }); });
 
  | 
 
Http 协议
获取 http 请求信息
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
   | const http = require("http"); const url = require("url");
  const server = http.createServer((req, res) => {   const { pathname, query } = url.parse(req.url, true);      console.log(req.method);      console.log(req.httpVersion);      console.log(req.headers);      let arr = [];      req.on("data", (data) => {     arr.push(data);   });
    req.on("end", () => {     console.log(Buffer.concat(arr).toString());   }); });
  server.listen(1234, () => {   console.log("server start ..."); });
  | 
 
设置 http 响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | const http = require("http"); const url = require("url");
  const server = http.createServer((req, res) => {            
       res.statusCode = 200;   res.setHeader("Content-type", "text/html;charset=utf-8");   res.end("你好"); });
  server.listen(1234, () => {   console.log("server start ..."); });
  | 
 
客户端代理
相当于服务端,客户端与不同端口域名协议的服务端通信有跨域,那就采用客户端代理,它与服务端就不存在跨域.