likes
comments
collection
share

前端应该了解的tcp知识

作者站长头像
站长
· 阅读数 34

tcp 的特点及组成格式

(tcp 是一个全双工的 单工、半双工(同时只有一个人在说话)、全双工) 三次握手和四次断开 缺陷和优点

  • 三次握手的目的 就是建立双向的链接 (实现了双方可以建立连接)
  1. 我可以和你发短信么
  1. 好的, 那我可以给你发消息么
  1. 可以呀
  • 四次断开是如何来的?
  1. 我们分手吧!
  1. 我收到了, (女生不能立刻发起分手的动作)

。。。。 正在发送其他消息

  1. 我们分手吧
  1. 对方确认 分手吧~

缺陷:tcp 每次传输一个数据段的时候 都要增加一个 20 字节的头部 (粘包)

名词解释

  • URG:紧急标志位,说明紧急指针有效;
  • ACK:确认标志位,多数情况下空,说明确认序号有效;
  • PSH:推标志位,置位时表示接收方应立即请求将报文交给应用层;
  • RST:复位标志,用于重建一个已经混乱的连接;
  • SYN:同步标志,该标志仅在三次握手建立TCP连接时有效
  • FIN:结束标志,带该标志位的数据包用于结束一个TCP会话。

node使用tcp通信

// server.js 模仿服务端接受消息
const net = require('net');
const server = net.createServer(function (socket) {
  socket.on('data', function (data) {
    // 客户端和服务端
    socket.write('hi'); // 服务端和客户端说 hi
  });
  socket.on('end', function () {
    console.log('客户端关闭');
  });
});
server.on('error', function (err) {
  console.log(err);
});
server.listen(8080); // 监听8080端口
// client.js 模仿客户端发送消息
const net = require("net"); // net 就是tcp模块
const socket = new net.Socket(); // 套接字  双工流
// 连接8080端口
socket.connect(8080, "localhost");
// 连接成功后给服务端发送消息
socket.on("connect", function (data) {
  //   socket.write("hello"); // 浏览器和客户端说 hello
  socket.end();
});
socket.on("data", function (data) {
  // 监听服务端的消息
  console.log(data.toString());
});
socket.on("error", function (error) {
  console.log(error);
});

使用wireshark监听8080端口

前端应该了解的tcp知识

1.握手的流程

客户端和服务端分别有一个序列号 seq = 0

客户端我的序列号是 0, seq = 0, 服务端要表示我收到了这个数据做应答 ack = 客户端 seq + 1, 并且告诉客户端服务端的 seq 是多少 seq = 0, ack = 1

客户端会收到 客户端的 ack = 服务端 seq + 1 , 服务端响应给客户端的 ack 或作为客户端的 seq

握手后 ack = 1 seq = 1 (两个序号分别是客户端的序号和服务端序号的)

前端应该了解的tcp知识

前端应该了解的tcp知识

使用wireshark抓包工具可以清楚的看到整个握手过程

  • 客户端(端口号:63835)发送请求链接请求,此时客户端Seq = 0;
  • 服务端(端口号:8080)接受到消息,并做出应答,此时服务端Seq = 0,Ack = 0 + 1;
  • 客户端接受到消息,并作出应答,此时客户端Seq = 1, Ack = 1。

2.发送消息的过程

客户端和服务端说 hello -> 服务端要立刻响应

客户端的 seq=1 , ack 的值也是 1 长度为 5 个字节的大小的内容

服务端响应 ack = 客户端的 seq + len, 我的序号还是 seq = 1

服务端和客户端说 hi -> 客户端要响应

服务端的序列号是 seq= 1 , 我的 ack = 6, len = 2 我要发送两个字节的消息 客户端就收到消息要响应 ack = 服务端的 seq + 服务端的 len, 我的序号是是上次服务端 aqck 的值 seq = 6

tcp 如果发现对方没有响应,会认为数据丢失了 ,会重新发送

前端应该了解的tcp知识

前端应该了解的tcp知识

通信过程

  • 客户端向服务端发送“holle”共五个字节的内容,标志位添加PSH,Seq = 1,Ack = 1,Len = 5(这里是holle的字节长度);
  • 服务端接受到内容,发送确认标志,此时Seq = 1(客户端发送的Ack),Ack = 6 (客户端发送的Seq + Len);
  • 服务端向客户端发送“hi”共两个字节内容,标志位添加PSH,Seq = 1,Ack = 6,Len = 2(这里是hi的字节长度)
  • 客户端收到消息并作出应答,发送确认标志,此时Seq = 6(客户端发送的Ack),Ack = 3 (客户端发送的Seq + Len);

3.断开也是双方的基础上+1

前端应该了解的tcp知识

前端应该了解的tcp知识

断开过程,其中红框内容为上次服务端发送的hi内容,其实这也代表着,即使是服务端发出端口的请求且服务端应答之后,但是在服务端发出断开请求之前,二者之间仍然可以进行通信。

  • 客户端请求断开链接,标志位添加FIN,Seq = 6,Ack = 3;
  • 服务端确认请求,Seq = 3,Ack = 7(客户端Seq + 1);
  • 发送其它请求······
  • 服务端请求断开链接标志位添加FIN,Seq = 3,Ack = 7;
  • 客户端收到请求,Seq = 7, Ack = 4。

其实到了最后一步是有点儿问题的,就是客户端发送确认消息的时候,存在时丢包的可能性,如果丢包,服务端就会重复发送FIN请求,但是如果此时客户端关闭了就会导致服务端解析错误,从而导致在高并发非长链接的场景下会有大量的端口被占用。所以在客户端发送Ack请求之后,会等待一段时间关闭,以保证没有丢包。

转载自:https://juejin.cn/post/7182158024163721275
评论
请登录