TCP协议-握手与挥手

 

认识TCP协议

TCP全称为“传输控制协议”,这是传输层的一个协议,对数据的传输进行一个详细的控制。 
特点:

  • 面向字节流
  • 安全可靠
  • 面向连接

TCP协议段格式

  • 源端口号与目的端口号:这里与UDP的一样,每个数据都要知道从哪个进程来,要到哪个进程去。
  • 32位序号与32位确认序号:这里的序号与确认信号可以理解成两个通信进程在收发数据的时候互相答复的信息。比如说:A进程从序列号1000开始给B进程发送数据,发送五个数据。那么在B收到数据回复的时候,这里A的确认序列号应该是从1006,如果不是1006,比如说是1003,那就意味着1004、1005数据包B没有收到,于是A启动重发机制。这也就保证了数据的可靠性,也是TCP的特点之一。序列号是进程发送消息的号码,而确认是期望目的进程返回的号码。进行比对,从而验证数据包是否到达。
  • 4位TCP报头长度:这里的四位TCP报头长度,可以理解成四个比特位表示长度,四位比特位表示的值乘以四就是该TCP头部的长度。由图可知,报头最短长度为20字节,也就是说这里的四位TCP报头长度默认为0101。并且TCP报头长度不可超过15*4=60个字节。
  • 6位标志位:①URG:紧急指针是否有效②ACK:确认号是否有效③PSH:提示接收端应用程序立刻从TCP缓冲区内部把数据读走④RST:对方要求重新建立连接,我们把携带RST标识的称为复位报文段⑤SYN:请求建立连接,我们把携带SYN标识的称为同步报文段⑥FIN:通知对方,本端要关闭了,我们把携带FIN标识的称为结束报文段。
  • 16位窗口大小:这里的窗口大小可以看做一个标志,标志着TCP缓冲区内部剩余空间的大小。起到一个流量控制的作用。如果16为窗口满了,那么这个时候是不允许数据接收的。后面到达的数据会被丢失。
  • 16位校验和:这里的校验和由发送端填充,CRC校验。接收端校验数据的时候如果校验不通过,那么认为数据有问题。此处的校验和不仅仅校验TCP首部,还校验数据部分。
  • 16位紧急指针:标识哪部分的数据为紧急数据。

    连接过程

    我们知道,TCP协议是面向连接的,也就是说它需要客户端与服务器成功连接之后才可使用。那么客户端与服务器的连接过程是怎样的呢?简单的来说就是三次握手,四次挥手,大意为客户端连接服务器需要三次握手,通信完毕过后断开连接需要四次挥手。

  • 三次握手

 

客户端与服务器在握手之前都做了一些前期的准备。服务器在开始先分配一个描述符,然后填充一下sockaddr_in结构体,绑定创建的文件描述符及服务器端口,接着listen监听,使得刚才的文件描述符成为一个监听描述符,最后阻塞至accept等待客户端的连接。而客户端相较来说简单一些,就是分配文件描述符,填充sockaddr_in结构体,最后进行connect请求服务器的连接,直至服务器响应。

在客户端经过connect请求服务器响应的时候,向服务器发送同步报文段也就是SYN请求,发送完毕后等待服务器响应。服务器如果收到了SYN同步报文段,那么就会给客户端发送ACK响应,意为收到了客户端发送的同步报文段,在此同时,服务器也会发送SYN同步报文段,请求客户端的响应。客户端在接收到SYN同步报文段后也会发送ACK响应来回复服务器。这个过程就是三次握手的过程。

这样看来,客户端与服务器的连接是双方的,两者都得发送请求同样两者也都得响应。图上来看,SYN_SENT就是请求连接状态,SYN_RCVD就是等待连接状态。在三次握手成功后,服务器与客户端都会进入ESTABLISHED状态,也就是TCP连接成功态,这个时候就可以进行数据的传送了。

这个过程中如果客户端的SYN请求如果丢包,服务器不会响应,而客户端会有一个等待时间,等待时间到达,未收到ACK响应,这个时候客户端会发起再次请求。如果多次请求都未成功,此时客户端可能会判断网络异常,不会再次请求。同样再服务器接收到客户端的SYN请求之后也会发送ACK响应同时发送SYN请求。如果客户端迟迟不给服务器ACK响应,服务器也会进行重发,直至判断网络异常。所以说,三次握手任意一个缺失都不会连接成功,也就无法通信。所以三次握手也是确保TCP可靠的一种方式。

三次握手的目的是什么?
答:个人理解简单易懂的就是同步连接双方的序列号和确认号,并交换tcp窗口的大小信息。
为什么需要二次握手就能完成解决反而需要三次呢?
答:需要三次握手是为了防止已经失效的连接请求报文段又突然传送到服务端,因而产生错误。

四次挥手

在客户端与服务器数据传输完毕之后,客户端没有请求了,所以此时调用close关闭文件描述符,开始进入FIN_WAIT_1状态,同时向服务器发送FIN结束报文段。等待服务器的响应。当服务器这里收到了FIN结束报文段时,这个时候,服务器进入CLOSE_WAIT状态。并给客户端进行应答发送ACK。当客户端收到服务器的ACK响应时,进入FIN_WAIT_2状态。当服务器调用close时,会向客户端发送FIN结束报文段。此时进入LAST_ACK状态。此时的客户端收到服务器发送的FIN时,会向服务器进行响应ACK,并且客户端进入TIME_WAIT状态,TIME_WAIT结束之后,进入CLOSED,断开连接成功。当服务器收到客户端最后的ACK时,进入CLOSED状态。断开连接成功。

CLOSE_WAIT与LAST_ACK状态

在三次握手的时候服务器可以将SYN与ACK同时发送,但是为什么这里服务器发送的FIN与ACK是分开的发送的呢??其实是这样的。 

首先FIN信号是由于调用close所以才发送的。而客户端调用close时,发送FIN结束报文段并进入FIN_WAIT_1状态。而这个报文段在服务器中用户态其实是无法感知的,内核会自己处理这个报文段,也就是说由内核进行ACK响应。这个过程中不是由用户代码决定的,服务器的FIN是由用户代码调用close发送的,所以内核与服务器不一定是同时处理这个信息的。所以FIN与ACK不一定是同时发送出去的。注意:这里是不一定!!!但是三次握手的时候发送SYN是由内核直接完成的,所以这就可以达到一个同步发送的情况。

如果服务器的代码没有调用close,那么意味着并没有发送FIN结束报文段。那么也就是说,此连接的服务器长期保持在CLOSE_WAIT状态,这会有什么影响? 
服务器长期保持在CLOSE_WAIT状态,也就是说分配的文件描述符并没有关闭并归还。那么大量的CLOSE_WAIT存在的话,就会导致一种资源的泄漏。可能到最后就没有可分配的文件描述符了,那么就会使一些客户端无法连接,从而造成不可估量的影响。

TIME_WAIT

在客户端最后一次发送ACK响应后,进入TIME_WAIT状态,而这个状态的时候客户端在做什么呢? 
答案就是等待!在客户端最后发送ACK响应后,进入TIME_WAIT状态,这是为了防止最后发送的ACK响应丢包。在这里,TIME_WAIT状态会等待2MSL的时间。

这里的单位 MSL就是 Max Segment Life意思就是报文的最大生存时间,这里的生存时间指的是一个报文从发生到被接收到的整个过程,这个过程的时间就是MSL。 
在Linux下可以利用cat /proc/sys/net/ipv4/tcp_fin_timeout来查看MSL的值。 

而客户端最后一次发送ACK响应后,为什么要等待2MSL呢?

这是为了确保最后一条ACK消息的到达。因为客户端在发送最后一条ACK响应后进入TIME_WAIT状态,如果这条ACK报文丢失,那么服务器在等待一个MSL的时间过后发现没有收到ACK响应,那么它会重新发送一条FIN报文。这样一条ACK响应的时间加上重发的FIN的时间正好就是2MSL。如果客户端等待2MSL后没有收到FIN报文,那么意味着服务器收到了客户端发送的ACK报文,这样就断开连接。 

这里可以看到客户端退出后,进入到了TIME_WAIT状态。

也就是说,在TIME_WAIT的时候,客户端与服务器之间的TCP连接还是存在的。 
在某些情况下服务器也有可能请求断开连接,由服务器先进入FIN_WAIT_1,这样的话最终就是服务器进入TIME_WAIT状态。那么在这个状态下会有什么问题呢?

这里,我们终止掉服务器,发现服务器进入了TIME_WAIT状态。这时候接着再次启动服务器,发现启动不了。此时启动不了的原因的端口号绑定失败。这是为什么?

其实这就是因为在TIME_WAIT状态的时候,TCP连接还是存在的,那么刚才的端口号仍旧被绑定着。再次启动服务器的时候,此时的端口号并未被释放。所以才提示绑定失败。

如果服务器需要处理大量的客户端连接,每个连接的生存时间很短,但是每秒都有大量的客户端请求。这个时候如果服务器主动关闭连接,就会产生大量的TIME_WAIT连接。由于我们的需求量很大,就会导致TIME_WAIT的连接数很多,从而导致服务器的端口不够用,无法处理新的连接。这个时候如何解决呢?

这时候可以利用函数setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)来解决这个问题。这个函数的作用就是允许创建端口号相同但是IP地址不同的多个socket描述符。在socket()与bind()之间调用即可。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384495.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ASOC注册过程

一、什么是ASOC 在嵌入式系统里面的声卡驱动为ASOC(ALSA System on Chip) ,它是在ALSA 驱动程序上封装的一层,分为3大部分,Machine,Platform和Codec ,三部分的关系如下图所示:其中Machine是指我…

ASOC调用过程

上一篇文章我们将了嵌入式系统注册声卡的过程:https://blog.csdn.net/qq_37659294/article/details/104748747 这篇文章我们以打开一个声卡的播放节点为例,讲解一下在APP调用open时,最终会如何调用到硬件相关的函数。 在上一篇文章最后我们说…

进程上下文与中断上下文的理解

一.什么是内核态和用户态 内核态:在内核空间执行,通常是驱动程序,中断相关程序,内核调度程序,内存管理及其操作程序。 用户态:用户程序运行空间。 二.什么是进程上下文与中断上下文 1.进程上下文&#xf…

内核的Makefile与Kconfig关系解析

在子目录下的Kconfig里添加make menuconfig的选项(如图一),并默认设置为y,make menuconfig的菜单里就会有该项并默认为选上状态,make menuconfig配置完之后在.config文件里就有该选项,并等于y(如…

Linux信号之signal函数

1. 信号概述 何为信号:信号就是由用户、系统或进程发送给目标进程的信息,以通知目标进程中某个状态的改变或是异常。 信号产生:总体来说,其产生的条件有两种,分别是:硬件和软件原因,又称为&…

Linux中wait()函数及waitpid()函数

编程过程中&#xff0c;有时需要让一个进程等待另一个进程&#xff0c;最常见的是父进程等待自己的子进程&#xff0c;或者父进程回收自己的子进程资源包括僵尸进程。这里简单介绍一下系统调用函数&#xff1a;wait() 函数原型是 #include <sys/types.h> #include <…

学习笔记 --- DM9000网卡原理与基地址设置

前面有文章分析了网卡也是属于类内存总线的设备&#xff0c;类内存总线的设备有地址总线和数据总线&#xff0c;先来看下DM9000的管脚&#xff1a; 从上面可以看出DM9000的地址总线就一根&#xff0c;它不像CS8900那样地址总线和数据总线都齐全。而这里只有一根地址线(CMD)&…

静态VLAN的配置

在一台交换机上连接3台PC机&#xff0c;然后创建两个VLAN&#xff0c;分别为VLAN 10 和VLAN 20&#xff0c;把第一台PC机分配给VLAN 10&#xff0c;把其他两台分配给VLAN 20.然后测试他们的互通情况。 在这里命令我用的都是简化命令&#xff0c;想卡完整版命令&#xff0c;请到…

静态路由原理

1、路由器的工作原理 路由工作简单原理图 1&#xff09;主机1.1要发生数据包给主机4.1.因为IP地址不在同一网段&#xff0c;所以主机会将数据包发送给本网段的网关路由器。 2&#xff09;路由器A 接收到数据包&#xff0c;先查看数据包IP首部中的目标IP地址。再查找自己的路由表…

静态路由和默认路由

一、静态路由的配置 下边实验对该拓扑图进行配置 实验目标&#xff1a;配置静态路由&#xff0c;实现全网互通 1、配置路由器R1 进入接口f0/0&#xff0c;配置IP&#xff0c;并开启。 进入接口f0/1&#xff0c;配置IP&#xff0c;并开启。 设置静态路由。 查看PC1的路由表 2、配…

IP地址192.168.1.1/24中的/24是什么意思

/24是指子网掩码的位数。 子网掩码的位数总共有32个&#xff0c;写的的/24个就是24个1&#xff0c;其它8位都是0。 /24就可以写成子网掩码是&#xff1a;11111111 11111111 11111111 00000000 例如&#xff1a; /25&#xff0c;就代表有25个1&#xff0c;7个0&#xff0c;…

IP地址中A类、B类、C类地址的区别

区别如下&#xff1a; 1、IP地址表示方法不同&#xff1a; 一个A类IP地址是指&#xff0c; 在IP地址的四段号码中&#xff0c;第一段号码为网络号码&#xff0c;剩下的三段号码为本地计算机的号码。如果用二进制表示IP地址的话&#xff0c;A类IP地址就由1字节的网络地址和3字…

原始套接字简介

一 原始套接字概述 原始套接字&#xff0c;指在传输层下面使用的套接字。流式套接字和数据报套接字这两种套接字工作在传输层&#xff0c;主要为应用层的应用程序提供服务&#xff0c;并且在接收和发送时只能操作数据部分&#xff0c;而不能对IP首部或TCP和UDP首部进行操作&am…

TCP socket心跳包示例程序

TCP socket心跳包示例程序_xqhrs232的专栏-CSDN博客_setsockopt 心跳包 原文地址::TCP socket心跳包示例程序_神奕的专栏-CSDN博客_tcp心跳包 相关文章 1、Linux网络编程--服务端判断客户端断开的经验方法 ----Linux网络编程--服务端判断客户端断开的经验方法_志存高远-CSDN博…

sizeof()计算结构体的大小

原文链接&#xff1a;sizeof()计算结构体的大小_海月汐辰-CSDN博客_结构体的sizeof怎么计算 简要说明&#xff1a;结构体成员按照定义时的顺序依次存储在连续的内存空间&#xff0c;但是结构体的大小并不是简单的把所有成员大小相加&#xff0c;而是遵循一定的规则&#xff0c…

select、poll、epoll使用小结

转载&#xff1a;http://blog.csdn.net/kkxgx/article/details/7717125 Linux上可以使用不同的I/O模型&#xff0c;我们可以通过下图了解常用的I/O模型&#xff1a;同步和异步模型&#xff0c;以及阻塞和非阻塞模型&#xff0c;本文主要分析其中的异步阻塞模型。 一、select使用…

Qt报错:undefined reference to xxxxx

报错信息&#xff1a; 首先&#xff0c;要区分与undefined reference to xxxxx和 "xxxx was not declared in this scope"两种报错信息的差别&#xff0c;前者是因为编译器能找到函数的声明&#xff0c;但是找不到函数的定义&#xff0c;从而报错&#xff1b;而后者是…

对象的浅拷贝和深拷贝

对象的浅拷贝和深拷贝简要介绍代码实现简要介绍 浅拷贝&#xff1a;python拷贝一般都是浅拷贝。拷贝时&#xff0c;对象包含的子对象内容不拷贝。因此&#xff0c;源对象和拷贝对象引用同一个对象 深拷贝&#xff1a;使用copy模块的deepcopy函数&#xff0c;递归拷贝对象中包含…

用模板写单链表

转载自&#xff1a;http://blog.csdn.net/itcastcpp/article/details/39081953 为了加深对模板的理解&#xff0c;我们今天一起用模板写一个单链表&#xff0c;希望通过这个例子&#xff0c;能够帮助大家加深对模板的体会&#xff0c;具体如下&#xff1a; SList.hpp内容&#…

QT事件事件之一:Qt中的事件处理与传递

QT事件事件之一&#xff1a;Qt中的事件处理与传递前言一、简介二、QT中的事件三、事件的实现的方法前言 在QT中&#xff0c;事件是我们很常用的东西&#xff0c;以下是我用事件时总结和做法 一、简介 在QT中&#xff0c;事件作为一个对象&#xff0c;继承QEvent类&#xff0c…