网络|学习一下tcp三次握手

TCP报文结构

我们想要介绍TCP/IP三次握手,需要有个前置条件,我们得补充下相关背景,目前我们所使用的的网络协议为TCP/IP4层协议,从下往上即: 数据链路层、网络层、传输层 以及 应用层。

图示如下:

在使用网络进行数据发送过程中,会将数据进行封包操作,到了对方机器,会逐步解包。我们使用的TCP协议,则是在传输层上,我们一般将其数据称之为报文段。

在介绍TCP如何通过三次握手建立连接之前,还需要先看看其TCP报文段结构,图示如下:

TCP报文段由2部分组成,首部 (黄色+绿色)和数据报文(蓝色),其中首部又包含基本首部20字节(上图黄色部分) 和 选项部分(上图绿色部分) 最多40字节。

上述报文段中,

  • 源端口 和 目的端口

这2个没啥特别的意思,就是记录双方端口的,分别是2个字节,所以说,服务器的端口最大为65535,因为这是2的16次方还要减去一个数(这个数为0),所以是65535。

  • 序号 和 确认序号

分别占了4个字节,我们常听见的seqack以及seq_ack等都是指的它两。

  • 首部长度

首部长度,也称之为数据偏移,用来指定首部长度的,如前面所述,报文段固定首部20个字节,还有40个字节的选项部分,如该首部长度为5,则证明首部就20个字节(5*4个字节),该值最多为15,即60个字节。

  • 保留

占了3个字节,现在还没有启用。

  • 状态标志位

URGACKPSHRSTSYNFIN这些都是占用了一个位,用以标志该报文段的状态,这些标志位在启用的时候会置为1,不启用的时候会置为0。其中

    • SYN表示新建连接。
    • FIN表示释放连接。
    • ACK表示确认收到数据。
    • RST表示请求重发。
    • PSH表示紧急数据,需要尽快交付至应用层。
    • URG表示紧急数据,需优先发送。
  • 接收窗口:2个字节,用于告知对方接收窗口的大小,做流量控制使用。
  • 校验和:2个字节,会校验伪首部、首部以及数据三个部分。
  • 紧急指针:2个字节,当URG置为1的时候才有效,该数据会指出有多少字节需要紧急传输。
  • 可选项:最大为40字节,包括窗口缩放、MSSSACK、时间戳等。

以上就是整个tcp报文段的内容了,只是做了一个简单的概述。

tcp如何通过三次握手建立连接

假设主机A和主机B想要建立连接,在实际发送报文数据之前,需要先发送3个空包用于建立连接,建立连接和TCP的状态图示如下:

首先,在建立握手之前,服务器必须要优先于客户端启动服务,而后客户端才能进行连接。

当客户端第一次发起请求的时候,需将报文段SYN标志位置为1,并且指定一个随机数作为seq,此时客户端的状态为SYN SEND

当服务器收到客户端请求后,需将报文段中的SYNACK标志位置为1,并且指定一个随机数作为seqack_seq值为x+1,此时服务器的状态为SYN REVD

当客户端收到请求服务器请求后,此时客户端的状态为ESTABLISHED需将报文段中的ACK标志位置为1,seq值为x+1,ack_seq值为x+1。服务器接收到后,此时服务器的状态也为ESTABLISHED

至此,客户端和服务器已经成功建立连接。

使用tcpdump进行抓取一个会话包

上面已经简单介绍了什么tcp的前置只是 和 为什么需要三次握手,而不是两次,本段落就来介绍一下如何使用tcpdump进行抓包,抓包工具有很多,在windows下,你可以选择Wireshark,在linux你可以选择tcpdump。也可以在linux抓包后保存到文件中,再导入Wireshark打开。

前置条件:若我们想使用tcpdump抓取信息,需要提前建立一个tcp服务器才行,这里搭建了一个nginx服务器,index.html简单设置为了hello pdudo

如果你的操作系统没有安装tcpdump,那么需要使用yum来安装一下:

yum install tcpdump -y 

使用man tcpdump可以获取更加详细的帮助文档,本篇文章需要用到的参数为如下几个:

-A: 以ascii码来显示报文。

-S: 输出TCP握手的全部信息,而非缩略信息。

-s num: 从每个数据包中抓取num字节的数据。

port: 指定从哪个端口抓取。

-i interface: 指定抓取的网口。

例如: 我们想抓取本地回环地址(lo)的80端口,以ASCII码的方式输出:

tcpdump -s 0 -S -A port 80 -i lo

抓取之后,结果为:

其中黄色线条框起来的地方就是三次握手的信息了。

如之前所述,可以清晰的看到,第一次握手信息是客户端发送给http服务器的,即: localhost.46452 > localhost.http

来解释一下请求的第一段报文信息:

  • Flags [S]: 表示报文的SYN标志位为1。
  • seq 532696697:表示seq为: 532696697 。
  • win 43690:表示滑动窗口为43690。
  • options存储的是TCP的选项信息,这是可变长信息,最大为40字节,内容为:
    • mss 65495mss表示每一个报文段所能支持的最大数据长度,这个只在握手的时候会进行协定,所以看后续的报文中,都没有出现mss相关信息。
    • sackOK
    • TS val 3302976138 ecr 0: 时间戳,用于测试报文往返时间。
    • wscale 7: 表示接收窗口
  • length:表示报文数据长度。

其他的,就不一一叙述了。

在结合之前的理论信息,我们可以知道:

当客户端第一次请求握手协议的时候,seq发送的是x2992020176

当服务器收到后,客户端发送上来的x+1作为ack返回,其值为2992020177,并且重新取了一个随机数y,其值为4127097599

当客户的接收到该数据后,向服务器发送了一个ack,并且还携带了seqseq_ack,只不过这里tcpdump给屏蔽了,使用其他抓包软件,例如 Wireshark 就可以正常显示出来。

在linux中如何查看连接状态

之前还介绍了tcp三次握手的状态信息,例如SYNC SENDSYNC REVD应该从哪儿看呢?注意,这个只是一个标准来规定它的状态,而不是存储在报文中,在linux中,可以使用netstatss等命令来查看当前服务器有哪些链接,这些链接分别是什么状态,例如:

ss -a | head -n 1 ; ss -a | grep ^tcp

上述命令表示展示当前机器所有的socket信息,并且使用grep过滤了一下以tcp开头的,我们能够得到如下截图:

上述截图中会显示当前机器的所有socket,包括服务器,如果是真实服务器,会有很多条信息,比如我们想赛选出当前服务器哪些状态分别有多少个,应该如何选择呢?

这里可以使用awk,将状态给存储到数组中,继而遍历出来即可,命令为:

ss -a | grep ^tcp | awk '{status[$2]=status[$2]+1} END{for (i in status) {print i,status[i]}}'

该命令为使用了awk将第二列的值当做数组的key给存上,当再次出现时,该数组key的值就+1,最后再遍历数组,就可以得到结果了,我们找台机器看下:

如果是使用的netstat命令,则需要取其最后一列用以awk计算。

linux对于tcp三次握手调整队列

对于tcp三次握手,linux中会用到2个队列,分别是 半连接队列 和 全连接队列。

其中半连接队列也称之为SYN队列,是指已经收到了客户端的请求,但是还没有建立完整连接,此时网络状态为SYN_REVD介于此linux会将该链接放入SYN队列中,并且等待客户端发送ACK报文以便建立连接。

而全连接队列也称之为Accept队列,该队列用于存储完成握手的连接,此时网络状态为ESTABLISHED,如果全连接队列满了,那么此时新链接上来会在SYNC_REVD处等待,等待全连接队列有空的出来,或者超时断开连接。

在linux中如何查看并且修改这2个值呢?

全连接队列的配置内核参数为net.core.somaxconn,而半连接内核参数为net.ipv4.tcp_max_syn_backlog ,使用sysctl可以查看并且修改2个值:

sysctl net.core.somaxconn
sysctl net.ipv4.tcp_max_syn_backlog

上述结果显示服务器全连接队列为最高为128,而半连接则为256

修改有2种方法:

  • 使用sysctl -w设置。
  • 将该值写入/etc/sysctl.conf中,并且使用sysctl -p使其重新加载。

例如: 修改全连接为1024,可以有2种方法:

方法1:

sysctl -w net.core.somaxconn=1024

方法2:

echo "net.core.somaxconn=1024" >> /etc/sysctl.conf && sysctl -p

总结

作为运维而言,网络和存储是必须且必要的知识,再结合linux调优,才能真正成为一名运维,在解决问题的过程中,特别是服务器性能过程中,是非常有意思的。运维不仅只是开关服务器以及背锅,还有其他更深层次有意义的事情等待运维去探索。

posted @ 2023-06-07 19:37  pdudos  阅读(0)  评论(0编辑  收藏  举报  来源