网络编程-从TCP三次握手说起

前言

网络编程几乎是每一门编程语言都会涉及到的内容,虽然各种语言调用的方式可能不一样,但它们背后的原理支持都是一样的。最近一直在恶补计算机网络方面的知识,之前对TCP的三次握手和四次挥手还是模糊的,对于其中的细节浑然不知。最近看了很多这方面的知识。也在系统的学习计算机网络,加深自己的网络编程功底。本文将从TCP的三次握手说起。根据自己看过的网络方面的书籍上和自己的一些理解进行二次加工组织一下然后交流分享,共同进步。

什么是TCP协议

TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。为了提供可靠性传输,实行顺序控制和重发控制等机制。

面向连接:两个使用TCP的应用在交换数据之前必须先建立一个TCP连接。
可靠的:对于丢包,TCP 的解决方案很简单,就是超时重传。对于错包,TCP 通过检查出错误后直接丢弃(丢包)。
字节流:不区分是ASCll字符还是二进制数据,数据解释交给应用层。

为什么要理解三次握手

用不用理解三次握手,完全看你做什么工作?

一个做综合布线的, 不理解三次握手, 无所谓。

一个专注路由器交换的。不理解三次握手,那真的得好好复习复习了。

事实上不理解TCP背后的基本原理,当代码出现问题的时候,通过API却找不到问题时。会觉得花点时间学习背后的原理是很有用的 。

TCP三次握手

大家都知道TCP是三次交互完成连接的建立,四次交互来断开一个连接。

要弄清TCP建立连接需要几次交互才行,我们需要弄清建立连接进行初始化的目标是什么?TCP进行握手初始化一个连接的目标是:分配资源、初始化序列号,知道初始化连接的目标,那么要达成这个目标的过程就简单了。TCP连接的建立,也就是三次握手的流程如下:

在这里插入图片描述

(1) 服务端启动,并监听等待,处于LISTEN状态    
(2) Client端首先发送一个SYN包告诉Server端,我的初始序列号是X,处于SYN_SENT状态。
(3) 服务端收到后,并回应ACK=X+1和seq=Y,处于SYN_RCVD状态,客户端发送能力,服务端接收能力正常。
(4) 客户端收到服务端的ACK,连接建立,同时向服务端回复ACK,处于ESTABLISHED状态。
(5)服务端收到ACK,连接建立,处于ESTABLISHED状态,客户端接收能力正常。

至此三次握手完成。需要注意的是,这是正常流程下的三次握手。

但是,细心的同学会发现两个问题:[1]Server发送SYN包是作为发起连接的SYN包,还是作为响应发起者的SYN包?怎么区分?比较容易引起混淆 ;

Server的ACK确认包和接下来的SYN包可以合成一个SYN ACK包一起发送的,没必要分别单独发送,这样省了一次交互同时也解决了问题[1]。 这样TCP建立一个连接,三次握手在进行最少次交互的情况下完成了两端的资源分配和初始化序列号的交换。

netstat命令查看状态

netstat命令用于查看网络连接,路由表,网络接口统计数据, 虚拟连接等信息。

查看处于监听状态的连接

在这里插入图片描述以此种方式,可以看到所有的TCP连接,而对于UDP连接,只需要使用-u(UDP):

在这里插入图片描述
查看路由信息

在这里插入图片描述
问题

tcp为什么要三次握手?

众所周知tcp传输层协议在建立连接的时候需要三次才能建立起一个真正的可靠连接,可是为什么是三次呢,不可以是两次,四次等等呢?

一个TCP连接是全双工的,即数据在两个方向上能同时传输。因此,建立连接的过程也就必须确认双方的收发能力都是正常的。

如果采用两次握手,那么服务端就会认为这个报文是新的连接请求,于是建立连接,等待客户端发送数据,但是实际上客户端根本没有发出建立请求,也不会理睬服务端,因此导致服务端空等而浪费资源。通信双方协商好一个初始 seq,至少需要一次 SYN 和 一次 ACK。

在这里插入图片描述
由于 TCP 是全双工的,所以 TCP 要协商两个初始 seq,所以双方各需要一次 SYN 和一次 ACK。

在这里插入图片描述
四次握手是否可以呢?完全可以!但是没有必要!在服务端收到SYN之后,它可以先回ACK,再发送SYN,但是这两个信息可以一起发送出去,因此没有必要。简单优化,可以将中间的 ACK + SYN 合并。所以就变成了 TCP 建立连接的三次握手。

在这里插入图片描述
tcpdump命令和nc命令来观察一个正常的tcp连接建立过程

首先准备三个终端,终端1是获取抓包信息。在一个终端窗口使用管理员权限执行下面的命令进行抓包。
在这里插入图片描述

终端1

tcpdump port 8080   -i any -v -n

终端2

nc -l 8080

终端3

nc 127.0.0.1 8080

在终端1获取抓包信息输出内容:

tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
16:18:38.985694 IP (tos 0x0, ttl 64, id 30656, offset 0, flags [DF], proto TCP (6), length 60)
    127.0.0.1.46958 > 127.0.0.1.webcache: Flags [S], cksum 0xfe30 (incorrect -> 0xc93f), seq 2068995006, win 43690, options [mss 65495,sackOK,TS val 23211134 ecr 0,nop,wscale 7], length 0
16:18:38.985752 IP (tos 0x0, ttl 64, id 0, offset 0, flags [DF], proto TCP (6), length 60)
    127.0.0.1.webcache > 127.0.0.1.46958: Flags [S.], cksum 0xfe30 (incorrect -> 0x9553), seq 1223671051, ack 2068995007, win 43690, options [mss 65495,sackOK,TS val 23211134 ecr 23211134,nop,wscale 7], length 0
16:18:38.985791 IP (tos 0x0, ttl 64, id 30657, offset 0, flags [DF], proto TCP (6), length 52)
    127.0.0.1.46958 > 127.0.0.1.webcache: Flags [.], cksum 0xfe28 (incorrect -> 0x6798), ack 1, win 342, options [nop,nop,TS val 23211134 ecr 23211134], length 0

从上面抓包内容可以看到,总共有三个报文,分别是客户端发送到服务端的SYN,服务端回应给客户端的SYN和ACK,以及客户端回应给服务端的ACK。

连接到一个不存在的端口

准备两个终端,我们利用nc命令来抓包观察。

终端1窗口使用管理员权限执行下面的命令进行抓包。信息如下:

tcpdump port 8080   -i any -v -n

终端2

nc 127.0.0.1 8080 -v

信息打印:
在这里插入图片描述连接拒绝。

终端1 ,TCP抓包内容如下:

tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
16:35:33.698135 IP (tos 0x0, ttl 64, id 56384, offset 0, flags [DF], proto TCP (6), length 60)
    127.0.0.1.46960 > 127.0.0.1.webcache: Flags [S], cksum 0xfe30 (incorrect -> 0xea14), seq 3321590902, win 43690, options [mss 65495,sackOK,TS val 24225846 ecr 0,nop,wscale 7], length 0
16:35:33.698191 IP (tos 0x0, ttl 64, id 60154, offset 0, flags [DF], proto TCP (6), length 40)
    127.0.0.1.webcache > 127.0.0.1.46960: Flags [R.], cksum 0xa05a (correct), seq 0, ack 3321590903, win 0, length 0

从抓包内容中可以看到,首先nc客户端发送一个SYN(Flags为S),seq为3321590902。而后收到一个RST(Flags为R),seq为3321590903。

也就是说,如果连接到一个不存在的端口,服务端所在的系统会响应一个RST(复位),直接终止连接。

Flags字段含义如下:

F : FIN - 结束; 结束会话
S : SYN - 同步; 表示开始会话请求
R : RST - 复位;中断一个连接
P : PUSH - 推送; 数据包立即发送
A : ACK - 应答
U : URG - 紧急
E : ECE - 显式拥塞提醒回应
W : CWR - 拥塞窗口减少

连接到一个不存在的服务器

还是准备两个终端。同样是利用nc和tcpdump命令。终端1窗口使用管理员权限执行下面的命令进行抓包。信息如下:
终端1

tcpdump port 8080  -i any -v -n

终端2
在另外一个窗口使用nc命令连接到一个不存在的或者无法连接的服务器地址。如下:

nc 121.11.12.31 8080 -v

信息输出:
在这里插入图片描述上面的打印信息为:连接超时。

终端1,输出内容为:

tcpdump: listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
16:45:18.173896 IP (tos 0x0, ttl 64, id 43668, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.0.107.36184 > 121.11.12.31.webcache: Flags [S], cksum 0x466c (incorrect -> 0x4e93), seq 3315808710, win 29200, options [mss 1460,sackOK,TS val 24810322 ecr 0,nop,wscale 7], length 0
16:45:19.175448 IP (tos 0x0, ttl 64, id 43669, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.0.107.36184 > 121.11.12.31.webcache: Flags [S], cksum 0x466c (incorrect -> 0x4aa9), seq 3315808710, win 29200, options [mss 1460,sackOK,TS val 24811324 ecr 0,nop,wscale 7], length 0
16:45:21.179178 IP (tos 0x0, ttl 64, id 43670, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.0.107.36184 > 121.11.12.31.webcache: Flags [S], cksum 0x466c (incorrect -> 0x42d5), seq 3315808710, win 29200, options [mss 1460,sackOK,TS val 24813328 ecr 0,nop,wscale 7], length 0
16:45:25.189602 IP (tos 0x0, ttl 64, id 43671, offset 0, flags [DF], proto TCP (6), length 60)
    192.168.0.107.36184 > 121.11.12.31.webcache: Flags [S], cksum 0x466c (incorrect -> 0x332b), seq 3315808710, win 29200, options [mss 1460,sackOK,TS val 24817338 ecr 0,nop,wscale 7], length 0

通过实际操作可以发现,当发送第一个SYN没有响应时,客户端会再次发送;如果还是没有响应,再隔更长一段时间,继续发送SYN,最终连接超时。

握手协议中的重要概念

半连接

在三次握手过程中,服务器发送SYN-ACK之后,收到客户端的ACK之前的TCP连接称为半连接(half-open connect).此时服务器处于SYN_RECV状态.当收到ACK后,服务器转入ESTABLISHED状态。

半连接队列

在服务器收到客户端的连接请求,并发送ACK之后,服务端处于SYN_RECV状态,此时的连接成为半连接,服务器会将半连接放到一个名为半连接队列的地方。

SYN攻击

SYN攻击属于DOS攻击的一种,它利用TCP协议缺陷,通过发送大量的半连接请求,耗费CPU和内存资源。

如果短时间内接收到的SYN太多,半连接队列就会溢出,操作系统会把这个连接信息丢弃造成不能连接,当攻击的SYN包超过半连接队列的最大值时,正常的客户发送SYN数据包请求连接就会被服务器丢弃。目标系统运行缓慢,严重者引起网络堵塞甚至系统瘫痪。

总结

总结到这里,也该结束了,但是对于TCP的学习远还没有结束。TCP是一个非常复杂的协议,这里稍微总结了一下TCP的连接过程,为后面的网络编程打下基础,其中还有很多的“坑”,让我们后续有时间再继续填吧。

本文难免有不足之处,欢迎各位提出建议或意见。

参考:《TCP/IP详解》

在这里插入图片描述
(微信公众号【程序猿编码】)

在这里插入图片描述
(添加本人微信号,备注加群,进入程序猿编码交流群,领取学习资料,获取每日干货)

微信公众号【程序猿编码】,这里Linux c/c++ 、Go语言、数据结构与算法、网络编程相关知识,常用的程序员工具。还有汇聚精炼每日时政、民生、文化、娱乐新闻简报,即刻知晓天下事!

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值