前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TCP:“哥哥(giegie)你真的懂TCP吗?”

TCP:“哥哥(giegie)你真的懂TCP吗?”

作者头像
才浅Coding攻略
发布2022-12-12 17:07:13
3730
发布2022-12-12 17:07:13
举报
文章被收录于专栏:才浅coding攻略

阿巩

别看标题不正经,内容可是相当硬核哦~

面向大厂jd(职位描述)学习一直是阿巩秉承的原则,技术岗位的jd通常有要求"熟悉TCP/IP等网络协议"这样的字样。我们普遍对TCP的了解是“TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议”。那么连接是如何创建的呢?是什么支持了TCP的可靠性呢?两主机性能差很多时,TCP又是如何做流量控制的呢?TCP做拥塞控制有哪些方式呢?等等问题需要我们去探索,事不宜迟,日拱一卒,让我们开始吧!

当用户在浏览器中输入一个url,通过DNS查询拿到ip地址,之后就是我们TCP建立连接的过程了,三次握手及四次挥手过程图示如下:

那么,为什么需要三次握手呢?

TCP连接的双方要确保各自的收发消息的能力都是正常的。

首先客户端第一次发送握手消息到服务端;服务端接收到握手消息后把ack和自己的syn一同发送给客户端,这是第二次握手;当客户端接收到服务端发送来的第二次握手消息后,客户端可以确认“服务端的收发能力OK,客户端的收发能力OK”,但是服务端只能确认“客户端的发送OK,服务端的接收OK”,同时也为了避免建立重复连接,所以还需要第三次握手,服务端收到客户端发送的第三次握手消息后,就能够确定“服务端的发送OK,客户端的接收OK”。至此,客户端和服务端都能够确认自己和对方的收发能力OK,TCP连接建立完成。

下面我们来用python写一个demo体会下客户端与服务端之间通信的过程。

服务端代码:

代码语言:javascript
复制
# echo_server.py
import socket

HOST = 'localhost'
PORT = 10001
def echo_server():
    """Echo Server的Server端"""
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 将对象绑定到指定的主机和端口上
    s.bind((HOST, PORT))
    # 只接受1个连接
    s.listen(1)
    while True:
        # accept表示接受用户端的连接
        conn, addr = s.accept()
        # 输出客户端地址
        print(f'Connected by {addr}')
        while True:
            data = conn.recv(1024)
            if not data:
                break
            conn.sendall(data)
        conn.close()
    s.close()


if __name__ == '__main__':
    echo_server()

客户端代码:

代码语言:javascript
复制
# echo_client.py
import socket

HOST = 'localhost'
PORT = 10001
def echo_client():
    """Echo Server的client端"""
    # 封装socket.socket,包括IPV4(AF_INET)、TCP协议(SOCK_STREAM)
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # 将socket对象建立连接,包含主机名HOST和端口PORT
    s.connect((HOST, PORT))

    while True:
        # 接收用户输入的数据并发送给服务端
        data = input('input > ')
        # 设定退出条件
        if data == 'exit':
            break
        # 发送数据到服务端
        s.sendall(data.encode())
        # 接受服务端数据
        data = s.recv(1024)
        # 判断输入
        if not data:
            break
        else:
            print(data.decode('utf-8'))
    s.close()


if __name__ == '__main__':
    echo_client()

我们再来看开篇词提到的第二个问题:TCP的哪些机制保证了可靠性呢?

保证可靠性的机制有:校验和、建立连接时的三次握手和断开连接时的四次挥手、确认应答和序列号(ACK+SYN)、超时重传、流量控制、拥塞控制。

确认应答和序列号(ACK+SYN):三次握手建立连接的首要目的是同步序列号。只有同步了序列号才有可靠的传输。因此SYN 的全称即为 Synchronize Sequence Numbers。

超时重传:TCP 必须保证每一个报文都能够到达对方,它采用的机制就是:报文发出后,必须收到接收方返回的 ACK 确认报文,如果在一段时间内(称为 RTO,retransmission timeout)没有收到,这个报文还得重新发送,直到收到 ACK 为止。

流量控制:如果我们发送一个报文,收到 ACK 确认后,再发送下一个报文,发送每个报文都需要经历一个 RTT 时延,但是这样的发送方式速度非常慢。提速的方式很简单,并行地批量发送报文,再批量确认报文即可。然而当接受方性能不如发送方,或者系统繁忙资源紧张时,报文无法得到及时处理,只能被丢掉。因此引入了滑动窗口,即接收方把它的处理能力告诉发送方,限制其发送速度即可。

接收主机的处理能力很强时,也无法通过增加发送方的发送窗口来提升发送速度,因为网络的传输速度是有限的,它会直接丢弃超过其处理能力的报文。发送方只有在重传RTO时间超时后才会发现报文被丢弃然后重传报文。 然而解决这个问题办法便是拥塞控制。

拥塞控制:TCP拥塞控制是TCP协议的核心,它大致分为四个阶段:慢启动、拥塞避免、快速重传和快速恢复。

慢启动:TCP连接会穿过许多网络,由于不清楚网络传输能力,为了避免发送超过网络负载的报文,TCP 只能先调低发送窗口。让发送速度变慢是通过拥塞窗口(cwnd)实现的,它用于避免网络出现拥塞。拥塞窗口一开始是一个很小的值,然后每 RTT 时间翻倍。

接收方的处理能力同样会反馈给发送方,这个处理是通过 rwnd 来表示的。如果不考虑网络拥塞,发送窗口就等于对方的接收窗口,而考虑了网络拥塞后,发送窗口则应当是拥塞窗口(cwnd)与对方接收窗口(rwnd)的最小值。

导致慢启动阶段结束的3种场景:

  • 通过定时器明确探测到了丢包
  • 拥塞窗口的增长到达了慢启动阈值
  • 接收到重复的 ACK 报文,可能存在丢包

当拥塞窗口的增长到达了慢启动阈值,很可能出现网络拥塞,为了避免拥塞,TCP 拥塞控制就进入了下一个阶段,拥塞避免。

拥塞避免:拥塞避免阶段,此时拥塞窗口不能再以指数方式增长,而是要以线性方式增长。

快速重传和快速恢复:TCP传输的是字节流,是有序的,这也就意味着当接收方收到不连续的报文时,就可能发生了报文丢失或者延迟。等待超时重传太耗时,这便到了快速重传和快速恢复的工作阶段。

当连续收到 3 个重复 ACK 时,发送方便得到了网络发生拥塞的明确信号,通过重复 ACK 报文的序号,我们知道丢失了哪个报文,这样,不等待定时器的触发,立刻重发丢失的报文,可以让发送速度下降得慢一些,这就是快速重传算法。


如何分析常见的TCP问题?

dstat检查工具

代码语言:javascript
复制
$ dstat

dstat显示了CPU、磁盘 I/O、 网络和内存的整体使用情况以及中断次数(int)和上下文切换次数(csw)两个关键指标。

针对TCP,可以用dstat --tcp查看相关指标

代码语言:javascript
复制
$ dstat --tcp
  • lis 处于listen的连接数
  • act 处于ESTABLISHED的连接数
  • syn 处于三次握手阶段的连接数
  • tim 处于TIME_WAIT状态的连接数
  • clo 处于CLOSE_WAIT状态的连接数(注意:如果太多往往意味着应用程序有bug,没有主动调用close(2)来关闭连接)

了解整体状况后,使用ss命令查看各个TCP连接:

我们能查看到每个 TCP 连接的状态(State)、接收队列大小(Recv-Q)、发送队列大小(Send-Q)、本地 IP 和端口(Local Address:Port )、远端 IP 和端口(Peer Address:Port)以及打开该 TCP 连接的进程信息。

查看系统的网络状态,比如说系统中是否存在丢包,以及是什么原因引起了丢包,这时候我们就需要 netstat -s或者它的替代工具 nstat。

详细内容我们将通过tcpdump抓包来看。

END

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 才浅coding攻略 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档