linux网络编程之socket编程(七)
今天继续学习socket编程,北京在持续几天的雾霾天之后久违的太阳终于出来了,心情也特别特别的好,于是乎,在这美好的夜晚,该干点啥事吧,那当然就是继续坚持我的程序学习喽,闲话不多说,进入正题:
通过这个状态的学习,进一步复习一下“连接建立三次握手、连接终止四次握手【下面会分别来介绍】”,下面首先来看一张图:
从图中可以数一下,总共有“LISTEN、SYN_SENT、SYN_RCVD、ESTABLISHED、FIN_WAIT_1、CLOSE_WAIT、FIN_WAIT_2、LAST_ACK、TIME_WAIT、CLOSED”十个状态,那为啥标题上说有十一个呢?其实还有一个状态叫CLOSING,这个状态产生的原因比较特珠,我们之后再来看它,下面先来分别梳理一下连接建立三次握手和连接终止四次握手状态流程:
连接建立三次握手:
LISTEN:
首先服务端创建一个socket,这时它的状态实际上是CLOSED状态,也就是最后一种状态,虽然没有标识出来:
一旦我们调用bind、listen函数:
这时就处于LISTEN状态,如下:
这时候的套接口就称为被动套接口,这意味着这个套接口不能用于发起连接,只能用来接受连接,这个之前都有介绍,
而这时回到客户端来说:
SYN_SENT:
当创建套接口时,也是一个CLOSED状态,这里也未标明,接着再调用connect进行主动打开,这时的套接口就称为主动套接口,它可以用来发起连接的:
这时的状态为SYN_SENT,这时TCP会传输一个发起连接的TCP段"SYN a"这个段给服务器端,如下:
而此时服务端调用了accept方法处理阻塞的状态:
但是TCP协议栈会收到“SYN a”TCP段,这时就处于SYN_RCVD状态:
当收到SYN_RCVD之后,TCP会对序号“SYN a”进行确认,发起一个"ACK a+1"TCP段给客户端,并且也有一个"SYN b"序号,如下:
对于客户端来说,收到"ACK a+1"TCP段之后,就处于"ESTABLISHED"连接的状态,这时connect函数就能够返回;
而对于服务端来说并未处理连接的状态,它需要等到客户端再次发送"ACK b+1"TCP段,这就是连接建立的三次握手,服务端收到这个TCP段之后,则也会处于"ESTABLISHED"状态,如下:
它实际上会将未连接队列当中的一个条目移至已连接队列当中,这时accept就可以返回了,因为它可以从已连接队列的队头返回第一个连接,如下:
连接终止四次握手:
当客户端发起关闭请求,这时会向服务器端发起一个"FIN x ACK y"的TCP段给对方,这时客户端的状态就叫作FIN_WAIT_1,如下:
这时服务器端收到一个终止的TCP段,这时read就会返回为0,实际上当服务端收到这个TCP段之后,服务端会对它进行确认,则会向客户端发送"ACK+1"的TCP段,这时服务端的状态为CLOSE_WAIT:
客户端的状态为FIN_WAIT_2:
之后服务端也可以选择发起一个终止的FIN TCP段给客户端,调用close,这时候就处于等待对方的最后一个确认的状态,称为LAST_ACK:
这时候,客户端就处于TIME_WAIT状态:
注意:这个状态要保留2倍的MSL(tcp最大的生命期)时间,为什么呢,这个可以简单说明一下,是由于最后一个"ACK y+1"发送过去,不能确定对方收到了,这个ACK可能会丢失,有了这个时间的存在就确保了可以重传ACK,当然还有其它的原因,这里先了解一下既可,当服务器收到了最后一个确认以后,则就处于CLOSED状态了:
注意:服务端处于CLOSED状态,并不代表发送关闭的这一端(就是客户端)就处于CLOSED状态,需等到2倍的MSL时间消失以后才处理CLOSED状态。
以上是TCP的十点状态,还有一个特珠状态叫CLOSING,它产生的原因是:双方同时关闭,如下图:
具体流程是这样的:客户端和服务端同时调用close,这时客户端和服务端都处于FIN_WAIT_1状态
这时,双方都会发起FIN TCP段,
这时需要对其进行段确认:
这时状态则称为CLOSING状态,这时就不会进行到FIN_WAIT_2这种状态了。
一旦收到对方的ACK,则会处于TIME_WAIT状态:
可见TIME_WAIT状态是主动关闭的一方才产生的状态。
说了这么多理论,下面用代码来进行论证,以便加强理解,还是用之前的服务端/客户端回显的例子,首先启动服务端,这时查看下状态:
接着启动一个客户端,发起连接:
由于目前做实验是在同一台机器上进行的,所以打印了三个状态,实际上应该是服务端的状态和客户端的状态是分开的。
【注意】:由于在运行时这两个状态SYN_SENT、SYN_RCVD过快,所以看不到。
下面来看下连接终止的状态,先关闭服务端,首先找到服务端的进程,通过kill掉的办法来关闭服务端:
杀掉服务端进程来模拟服务端的close:
【注意】:这里的服务端进程是指与客户端通讯的进程。
这时查看一下状态:
为什么不会处于TIME_WAIT状态呢?
原因在于,read函数没有机会返回0:
这时应该查看一下客户端的程序才知道问题,客户端此时是阻塞在fgets函数来键盘的消息:
这就意味着客户端这个进程没有机会调用close,所以服务器端无法进入TIME_WAIT,它只能保留在FIN_WAIT_2状态了
这时候再来看一下状态:
只有LISTEN状态了,这是为什么呢?
还是得回到客户端的程序来分析,由于从键盘敲入了字符,所以:
这时就会走如下流程:
而由于服务器的进程已经杀掉了,所以说不会显示TIME_WAIT状态了。
【注意】:该实验在最后会阐述一个SIGPIPE的信号问题。
如果先关闭客户端,这时就会看到这个状态了,如下:
另外这个状态上面也提到了,会保留2倍的MSL时间才会消失,如果服务器端保留两倍的MSL时间,这时候就会导致服务器端无法重新启动,如果没有调用SO_REUSEADDR话(关于这个具体可以参考博文:http://www.cnblogs.com/webor2006/p/3932917.html),以上就是TCP的十一种状态的学习。
SIGPIPE信号产生的原因:
对于上面做的一个实验,就是服务端先关闭之后,然后客户端还可以向服务端发起数据,这是由于客户端收到FIN仅仅代表服务端不能发送数据了,如下图:
而如果发送数据给对方,但是对方进程又已经不存在,会导致对方发送一个RST重启TCP段给发送方(这里指的就是上面做实验的客户端),但是在收到RST段之后,如果再调用write就会产生SIGPIPE信号,而产生该信号默认就会终止程序,下面来修改一下客户端的代码,还是基于上面的实验,如下:
这时,再来看一下效果:
首先运行客户端与服务端:
然后将服务端与客户端的进程找到,并杀掉来模拟关闭服务端:
然后这时在客户端中敲入字符,并回车,看下结果:
结合代码来看一下:
为了证明确实是收到了SIGPIPE信号,我们捕捉一下该信号,修改代码如下:
再次编译运行,这一次运行步骤还跟上次一样,需要先杀掉父进程,然后再在客户端敲入一个字符,这里就不说明了,只看一下结果:
实际上,对于这个信号的处理我们通常忽略即可,可以加入这条语句:signal(SIGPIPE, SIG_IGN);
修改代码如下:
实际上,对于SIGPIPE信号在学习管道时有说过它的产生,如果没有任何读端进程,然后往管道当中写入数据,这时候就会出现段开的管道,而对于TCP我们可以看成是一个全双工的管道,当某一端收到FIN之后,它并不能确认对等方的进程是否已经消失了,因为对方调用了close并不意味着对进程就已经消失了,用图来理解:
这时候,就需要客户调用一次write,这次并不会产生断开的管道,发现对等方的进程不存在了,则对等方就会发送RST段给客户端,这就意味着全双工管道的读端进程不存在了,所以说如果再次调用write,就会导致SIGPIPE信号的产生,所以说可以利用管道来理解它。
好了,这节的学习有些难懂,需要多想,多做实验,下节见~
linux网络编程之socket编程(七)的更多相关文章
- linux网络编程之socket编程(四)
经过两周的等待,终于可以回归我正常的学习之旅了,表哥来北京了在我这暂住,晚上回家了基本在和他聊天,周末带他在北京城到处乱转,几乎剥夺了我自由学习的时间了,不过,亲人之情还是很难得的,工作学习并不是生活 ...
- linux网络编程之socket编程(一)
今天开始,继续来学习linux编程,这次主要是研究下linux下的网络编程,而网络编程中最基本的需从socket编程开始,下面正式开始学习: 什么是socket: 在学习套接口之前,先要回顾一下Tcp ...
- linux网络编程之socket编程(六)
经过一个国庆长假,又有一段时间没有写博文了,今天继续对linux网络编程进行学习,如今的北京又全面进入雾霾天气了,让我突然想到了一句名句:“真爱生活,珍惜生命”,好了,言归正传. 回顾一下我们之间实现 ...
- linux网络编程之socket编程(八)
学习socket编程继续,今天要学习的内容如下: 先来简单介绍一下这五种模型分别是哪些,偏理论,有个大致的印象就成,做个对比,因为最终只会研究一个I/O模型,也是经常会用到的, 阻塞I/O: 先用一个 ...
- linux网络编程之socket编程(十六)
继续学习socket编程,今天的内容会有些难以理解,一步步来分解,也就不难了,正入正题: 实际上sockpair有点像之前linux系统编程中学习的pipe匿名管道,匿名管道它是半双工的,只能用于亲缘 ...
- linux网络编程之socket编程(十五)
今天继续学习socket编程,这次主要是学习UNIX域协议相关的知识,下面开始: [有个大概的认识,它是来干嘛的] ①.UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍. ...
- linux网络编程之socket编程(三)
今天继续对socket编程进行学习,在学习之前,需要回顾一下上一篇中编写的回射客户/服务器程序(http://www.cnblogs.com/webor2006/p/3923254.html),因为今 ...
- linux网络编程之socket编程(二)
今天继续对socket编程进行研究,这里会真正开如用socket写一个小例子,进入正题: TCP客户/服务器模型: 关于这个模型的流程这里就不多说了,比较容易理解,下面则利用这种模型来编写一个实际 ...
- linux网络编程之socket编程(十二)
今天继续学习socket编程,期待的APEC会议终于在京召开了,听说昨晚鸟巢那灯火通明,遍地礼花,有点08年奥运会的架势,有种冲动想去瞅见一下习大大的真容,"伟大的祖国,我爱你~~~&quo ...
随机推荐
- AWS 消息服务(九)
松耦合架构 概述 使用独立的组件设计架构,降低相互依赖,当一个组件出现故障时,其他不受影响 利用ELB和SQS来打破传统服务器各层的关联,成为各层之间的中介,各层的故障和扩展均由中介自助处理 系统的耦 ...
- STM32 MDK摘记
题记:这人是越懒越懒,记性也也来越差,前段时间改了个链接文件,今天想用,竟然忘了咋写....还是勤记记吧... 随时更新,笔记帖. 不喜勿喷! 1,关于MDK链接文件宏的定义 #! armcc -E ...
- CSS float属性
表示向左浮动,比如多个div在一个页面上,默认情况是:一行一个div,但是只要在div的css中使用float:left,可以使一行有多个div,这样可以把网页划分成很多块,但是使用该属性会影响后面的 ...
- .net字符串内存的分配
几次面试中遇到都有类似的问题,就是 string str = "aa" + "bb" + "ccc";进行了几次内存分配? 1 class ...
- pytorch1.0实现AutoEncoder
AutoEncoder (自编码器-非监督学习)神经网络也能进行非监督学习, 只需要训练数据, 不需要标签数据. 自编码就是这样一种形式.自编码能自动分类数据, 而且也能嵌套在半监督学习的上面, 用少 ...
- scrapy工具创建爬虫工程
1.scrapy创建爬虫工程:scrapy startproject scrape_project_name >scrapy startproject books_scrapeNew Scrap ...
- 【转帖】linux内存管理原理深入理解段式页式
linux内存管理原理深入理解段式页式 https://blog.csdn.net/h674174380/article/details/75453750 其实一直没弄明白 linux 到底是 段页式 ...
- C++11<functional>深度剖析:背景、原理、接口与实现
自C++11以来,C++标准每3年修订一次.C++14/17都可以说是更完整的C++11:即将到来的C++20也已经特性完整了. C++11已经有好几年了,它的年龄比我接触C++的时间要长10倍不止吧 ...
- React Hooks中父组件中调用子组件方法
React Hooks中父组件中调用子组件方法 使用到的hooks-- useImperativeHandle,useRef /* child子组件 */ // https://reactjs.org ...
- 安装Nginx报错“Cannot retrieve metalink for repository: epel. Please verify its path and try again”
CentOS 6.5中通过yum安装nginx报错. 搜了一下,很多都是修改某个配置文件的.但是在StackOverFlow的某个问题下,有人回答说修改配置文件并不是一个好的方法,虽然我采用了这个人的 ...