https://blog.csdn.net/a987073381/article/details/52206215
TCP的传输连接分为3个阶段:连接建立(三次握手)、数据传送和连接释放(四次挥手)。TCP传输连接的管理就是使传输连接的建立和释放都能正常地进行。
一、TCP的三次握手
准备工作:服务器必须准备好接受外来的连接。这通常通过调用socket,bind和listen这三个函数来完成。我们称之为被动打开。
第一次握手:客户端通过调用connect发起主动打开。客户端向服务器发出连接请求的TCP报文段,其TCP首部中的同步比特SYN置为1,并TCP首部中序号seq设置为x(TCP规定SYN报文段不能携带数据,但是要消耗一个序号),表明要转送数据时初始序列号是x。通常SYN分节不携带数据,其所在IP数据报只含有一个IP首部,一个TCP首部。
第二次握手:服务器收到数据报后,从TCP数据报首部的同步比特SYN位为1就知道这是一个建立连接的请求。服务器如果同意,会发回确认。在确认报文段中把同步比特位SYN设置为1,确认比特位ACK设置为1,由于TCP请求报文段中的序号是x,所以服务器在发送确认报文段中的确认号ack是x+1,同时把确认报文段中的序号seq设置为y,表明服务器发送数据的初始序列号为y。该报文段也不能携带数据(因为SYN=1,所以不携带任何数据)。
第三次握手:客户端收到服务器端的报文段后,要对服务器端中的SYN进行确认。在确认报文段中把确认比特位ACK设置为1,然后把确认号ack设置为y+1,自身的序号seq设置x+1。
注:客户的初始序列号为x,服务器的初始序列号为y,那么确认报文段中的确认号ack就是所期待的对方要发送的下一个序列号。
客户端调用connect将激发TCP的三次握手,仅在连接建立成功或者出错时才返回,上面介绍了建立成功的情况,下面列出了出错返回可能有的几种情况。
(1)客户端发送的建立连接的SYN报文丢失或者服务器回复的ACK报文丢失。客户端因为没有收到服务器的回复,会等待6s再发一次,若无响应则等待24s再发一次,总共等75s还未收到响应就返回本错误。
(2)服务器回复的TCP报文中复位标志RST置位1。表示服务器主机没有在指定的端口上监听或接受该连接或服务器程序根本没有运行。
(3)客户端发送的SYN报文不可达。还记IP数据报中有一个生存时间(TTL)的标志吗?它每进过一个路由器都会减1,当它减到0时还没到达目的地,会发送一个ICMP错误。然后客户端会和第一种情况一样每隔一段时间重发一次。
可以把TCP连接时的三次握手换成两次握手吗?(假设客户端主动,服务器端被动)
假如客户端发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达服务器。也就是说这是一个早已失效的报文段。但服务器端收到此失效的连接请求报文段后,就误认为是客户端再次发出的一个新的连接请求。于是就向客户端发出确认报文段,同意建立连接。假设不采用“三次握手”,那么只要服务器端发出确认,新的连接就建立了。由于现在客户端并没有发出建立连接的请求,因此不会理会服务器端的确认。经过三次握手,客户端和服务器都有应有答可以确保TCP正确连接。
二、TCP的四次挥手
数据传输完毕后,通信双方都可以释放连接。对于首先调用close的一端我们称该端为主动关闭,另外一端执行被动关闭。
第一次挥手:假设客户端执行主动关闭,那么它会向服务器端发出释放连接的报文段,这个TCP报文段中终止比特FIN置为1,序号seq设置为u(假设上一个发的数据序号是u-1)。并停止发送数据。主动关闭TCP连接。等待服务器的确认,这里需要注意,因为TCP是全双工的,所以TCP连接上有两条数据通路,发送FIN的一端就不能发送数据,也就是关闭了其中一条数据通路,对方还是可以继续发送数据。
第二次挥手:服务器端收到客户端的释放连接的报文段后会执行被动关闭,它要对客户端的数据报进行确认,服务器端会发送一个确认的数据报,确认比特ACK设置为1,确认号为u+1,自身的序号seq为v(假设上一个发的数据序号是v-1)。这个时候TCP处于半关闭状态,服务器依然可以向客户端发送数据(数据的序号为v+1 ~ w-1),客户端任要接受。
第三次挥手:服务器端已经没有要发送给客户端的数据,那么服务器端也会调用close关闭套接字,这样服务器端也会发送一个FIN的TCP报文段,序号是w(假设上一个发的数据序号是w-1)。这个时候服务器端不会再向客户端发送数据了。
第四次挥手:客户端接受到这个最终的FIN的释放连接报文段后必须对报文段进行确认。在确认的报文段中,ACK=1,确认序号ack=w+1,自己的序号seq=u+1(他的上一个序号的数据报就是申请释放连接的数据报,序号是seq=u)。
为什么TCP握手是三次,挥手却是四次?(假设客户端主动,服务器端被动)
在TCP三次握手中,服务器端的SYN和ACK是放在一个TCP报文段中向客户端发送的,而在断开连接的过程中,服务器端向客户单端发送的ACK和FIN是是分别在两个不同的TCP报文段中。这是因为在服务器端接收到客户端的FIN后,服务器端可能还有数据要传输,所以先发送ACK,服务器端把数据发完之后就可以发送FIN断开连接了。
三、TCP的11种状态转换
CLOSED:起始点,不在连接状态。可以主动打开连接,或者等待对端的连接。
-->收到“被动打开”报文,进入LISTEN状态。
-->收到“主动打开”报文,进入SYN_SENT状态。
-->收到任何报文段,发送RST报文段。
-->收到其它任何报文段,发出差错报文。
LISTEN:被动打开,TCP正在等待对端的连接请求。
-->收到“发送数据”报文,发送SYN报文段,进入SYN_SENT状态。
-->收到任何SYN报文段,发送SYN+ACK报文段,进入SYN_RECEIVED状态。
-->收到任何其它报文段或者报文,发送差错报文。
SYN_SENT:主动打开,发送完一个连接请求后等待回复。
-->超时,进入CLOSED状态。
-->收到SYN报文段,发送SYN+ACK报文段,进入SYN_RECEIVED状态。
-->收到SYN+ACK报文段,发送ACK报文段,进入ESTABLISHED状态。
-->收到任何其它报文段或者报文,发送差错报文。
SYN_RECEIVED:被动打开,接受连接请求以后进行确认同时也向对端发送连接请求发送,等待对方的回复。
-->超时,发送RST报文段,进入CLOSED状态。
-->收到ACK报文段,进入ESTABLISHED状态。
-->收到"关闭"报文,发送FIN报文段,进入FIN_WAIT_1状态。
-->收到RST报文段,进入LISTEN状态。
-->收到任何其它报文段或者报文,发送差错报文。
ESTABLISHED:三次握手完毕,TCP连接建立完成,可以传输数据。
-->收到FIN报文段,进入CLOSED_WAIT状态。
-->收到“关闭”报文,发送FIN报文段,进入FIN_WAIT_1状态。
-->收到RST或SYN报文段,发出差错报文。
-->收到数据或ACK报文段,调用输入模块。
-->收到“发送”报文,调用输出模块。
FIN_WAIT_1:四次挥手开始,主动关闭,发送断开连接请求,等待对端确认。
-->收到FIN报文段,发送ACK报文段,进入CLOSING状态(同时关闭)。
-->收到FIN+ACK报文段,发送ACK报文段,进入FIN_WAIT状态(?)。
-->收到ACK报文段,进入FIN_WAIT_2状态。
-->收到任何其它报文段或者报文,发送差错报文。
FIN_WAIT_2:接收对方确认,但未接受对方的断开连接请求。
-->收到FIN报文段,发送ACK报文段,进入TIME_WAIT状态。
CLOSING:主动关闭的一方本希望收到对方的ACK却收到了对方的断开连接请求。
-->收到ACK报文段,进入TIME_WAIT状态。
-->收到任何其它报文段或者报文,发送差错报文。
TIME_WAIT:对方确认后发起断开连接请求,需要等待2MSL保证正常关闭。
-->超时,进入CLOSED状态。
-->收到任何其它报文段或者报文,发送差错报文。
CLOSE_WAIT:被动关闭,确认对端的连接终止请求,但是未向对端发送连接终止请求(可能数据没传完)。
-->收到"关闭"报文,发送FIN报文段,进入LAST_ACK状态。
-->收到任何其它报文段或者报文,发送差错报文。
LAST_ACK:数据传完,向对端发起断开连接请求后等待确认。
-->收到ACK报文段,进入CLOSED状态。
-->收到任何其它报文段或者报文,发送差错报文。
CLOSED:终点,不在连接状态。可以主动打开连接,或者等待对端的连接。
(1)TIME_WAIT状态(假设客户端主动,服务器端被动)
从状态图中我们可以发现,执行主动关闭的那端(最终重传ACK的那端)会经历这个状态。这个状态停留是的时间是2MSL(MSL:最长报文段生存时间,1~4分钟)。
TIME_WAIT状态的作用:
1、可靠地实现TCP的连接终止。
在终止TCP连接时有4个报文需要交换,其中最后一个ACK报文是由客户端发往服务器。假设这个ACK报文在网络中被丢弃了,那么服务器端收不到这个确认ACK,服务器端会向客户端再次发送FIN。这就是为什么TIME_WAIT状态持续2倍的最长报文段生存时间:1MSL时间留给最后的ACK确认报文段到达服务器端,1MSL时间留给服务器端再次发送的FIN(这一段摘抄自《unix系统编程手册》P1046,我不明白,超过2MSL连接就超时关闭了,再次发送FIN后,即客户端刚收到,2MSL时间也就到了,连接就关闭了,再次发送FIN的意义何在?,希望有知道的同学告诉我)。
2、确保老的重复的报文段在网络中过期失效,这样建立新的连接时将不再接受它们。
TCP协议采用的是出错重传,也就是会生成重复的报文,并且根据路由器的选择,这些重复的报文可能在连接终止后才到达,如果客户端/服务器端收到这个老的报文会把它误认为一个同一连接的新的报文,然后对这个报文进行处理,这样就会出现错误。从状态转换图我们可以看到从TIME_WAIT到连接终止,中间有2MSL,这个时间足以让老的重复的报文段过期失效。
TIME_WAIT会出现在服务器吗?
会的,TIME_WATI存在首先执行主动关闭的那端,比如爬虫服务器他本身其实就是客户端,完成爬取任务后,执行关闭,那么所有的TCP连接都会处于TIME_WAIT状态。
TIME_WAIT的危害:Linux分配给一个用户的文件句柄是有限的,如果系统中存在大量的TIME_WAIT状态,一旦达到句柄数上限,新的请求就无法被处理了,而且大量TIME_WAIT连接占用资源影响性能。
如何关闭TIME_WAIT状态或者让TIME_WAIT状态过早终止?
对/etc/sysctl.conf文件内容进行修改:
- #表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭
- net.ipv4.tcp_tw_reuse = 1
- #表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭
- net.ipv4.tcp_tw_recycle = 1
虽然我们可以这么做,但是我们应该避免这样做,因为这样会阻碍TIME_WAIT状态提供的可靠性保证。
(2)CLOSE_WAIT状态(假设客户端主动,服务器端被动)
从上面的状态图可以看出,被动关闭的那端会经历这个状态。如果一直保持在CLOSE_WAIT状态,那么只有一种情况,就是在客户端关闭连接之后服务器程序自己没有进一步发出FIN报文,一般原因都是TCP连接没有调用关闭方法,或者对方连接关闭之后程序里没有检测到,或者程序压根就忘记了这个时候需要关闭连接,于是这个资源就一直被程序占着。这种情况,通过服务器内核参数也没办法解决,服务器对于程序抢占的资源没有主动回收的权利,除非终止程序运行,一定程度上,可以使用TCP的keepalive功能,让操作系统替我们自动清理掉CLOSE_WAIT连接。
- #表示当keepalive起用的时候,TCP发送keepalive消息的频度。缺省是2小时,改为300秒
- net.ipv4.tcp_keepalive_time=1200
参考:
《UNIX网络编程》
《UNIX系统编程手册 下》
《TCP/IP协议族》
- 浅谈TCP/IP网络编程中socket的行为
我认为,想要熟练掌握Linux下的TCP/IP网络编程,至少有三个层面的知识需要熟悉: 1. TCP/IP协议(如连接的建立和终止.重传和确认.滑动窗口和拥塞控制等等) 2. Socket I/O系统 ...
- 《TCP/IP网络编程》
<TCP/IP网络编程> 基本信息 作者: (韩)尹圣雨 译者: 金国哲 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9787115358851 上架时间:2014-6- ...
- TCP/IP网络编程系列之四(初级)
TCP/IP网络编程系列之四-基于TCP的服务端/客户端 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于流的 ...
- TCP/IP网络编程系列之三(初级)
TCP/IP网络编程系列之三-地址族与数据序列 分配给套接字的IP地址和端口 IP是Internet Protocol (网络协议)的简写,是为首发网络数据而分配给计算机的值.端口号并非赋予计算机值, ...
- TCP/IP网络编程系列之二(初级)
套接字类型与协议设置 我们先了解一下创建套接字的那个函数 int socket(int domain,int type,int protocol);成功时返回文件描述符,失败时返回-1.其中,doma ...
- TCP/IP网络编程之多线程服务端的实现(二)
线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...
- TCP/IP网络编程之优于select的epoll(二)
基于epoll的回声服务端 在TCP/IP网络编程之优于select的epoll(一)这一章中,我们介绍了epoll的相关函数,接下来给出基于epoll的回声服务端示例. echo_epollserv ...
- TCP/IP网络编程之进程间通信
进程间通信基本概念 进程间通信意味着两个不同进程间可以交换数据,为了完成这一点,操作系统中应提供两个进程可以同时访问的内存空间.但我们知道,进程具有完全独立的内存结构,就连通过fork函数创建的子进程 ...
- TCP/IP网络编程之多播与广播
多播 多播方式的数据传输是基于UDP完成的,因此,与UDP服务端/客户端的实现非常接近.区别在于,UDP数据传输以单一目标进行,而多播数据同时传递到加入(注册)特定组的大量主机.换言之,采用多播方式时 ...
- TCP/IP网络编程之多进程服务端(二)
信号处理 本章接上一章TCP/IP网络编程之多进程服务端(一),在上一章中,我们介绍了进程的创建和销毁,以及如何销毁僵尸进程.前面我们讲过,waitpid是非阻塞等待子进程销毁的函数,但有一个不好的缺 ...
随机推荐
- 【bzoj4419】[Shoi2013]发微博 STL-set
题目描述 刚开通的SH微博共有n个用户(1..n标号),在短短一个月的时间内,用户们活动频繁,共有m条按时间顺序的记录: ! x 表示用户x发了一条微博: + x y 表示用户x和用户y成为了好友 ...
- hdu 3354 Probability One
Probability One Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)T ...
- Codeforces Round #386 (Div. 2) 746F(set的运用)
题目大意 给出一个歌单(有n首歌),每个歌都有愉悦值和时间,你可以选择从第x首歌开始听(也就是选择连续的一段),并且你可以选择w首歌让它的时间减半,限制时间为k,求最大的愉悦值 首先我们需要贪心一下, ...
- 【转】oracle 删除重复记录
转至:http://blog.163.com/aner_rui/blog/static/12131232820105901451809/ 2.保留一条(这个应该是大多数人所需要的 ^_^) Delet ...
- BZOJ1483 [HNOI2009]梦幻布丁 【链表 + 启发式合并】
题目 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色. 例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 输入格式 第一行给出N,M表示 ...
- [转]Linux下阅读源代码:(g)vim+Taglist+ctags
Linux下阅读源代码的方法很多,聪明人从标题应该就可以知道,需要(g)vim+Taglist+ctags.3者配合,真是珠联璧合,功力无限啊! vim/gvim什么是vim/gvim,如果看官连 ...
- 论文笔记《Tracking Using Dynamic Programming for Appearance-Based Sign Language Recognition》
一.概述 这是我在做手势识别的时候,在解决手势画面提取的时候看的一篇paper,这里关键是使用了动态规划来作为跟踪算法,效果是可以比拟cameshift和kf的,但在occlusion,gaps或者离 ...
- 《c程序设计语言》读书笔记-第二个字符串任意一个在第一个字符串出现的位置,未出先返回-1
#include <stdio.h> #include <string.h> #define Num 1000 int main() { int c,i,j = 0,m = 0 ...
- python登录知乎
#coding:utf-8 import requests import urllib3 import time class Zhihu: def __init__(self): self.login ...
- linux下有的目录有可执行权限
在linux里面,目录也是一个文件.权限设置方法与普通文件一样.目录的可执行权限:目录无所谓执行不执行,目录只能否被开启(也称作“遍历”或“搜索”)对目录的执行权限不仅意味着查看目录下文件名还允许查看 ...