| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> C++Socket编程--探讨一些边界条件 -> 正文阅读 |
|
[网络协议]C++Socket编程--探讨一些边界条件 |
本篇文章接上一节《C++Scoket编程–堵塞式IO相关函数介绍》,在上一节中主要介绍了TCP程序中几个出现的必要程序,例如scoket、bind、listen、accept函数,但是并没有介绍在接受发送数据时的写入写出函数,我觉得这些虽然在堵塞IO中时堵塞的关键之一,但是并不打算介绍这些流函数,在书中是对这些函数有介绍的。本节主要是第五章的学习笔记,会探讨一些边界条件:
正常启动service.cpp
Client.cpp
当程序正常启动时,首先启动服务端,服务器启动之后调用socket、bind、listen和accept,并且堵塞于accept调用,并且使用netstat工具,可以查看到相关的状态
正是我们需要的,在服务器端游一个8899端口处于监听状态,当客户调用socket和connect,则发生三次握手,三次握手完成之后,客户中的connect和服务器中的accept均返回,连接于是建立 正常终止当连接建立的时候,服务端和客户端都处于ESTABLISHED状态,使用netstat可以查看相关的状态 当我们在client端输入exit时,当前连接的客户端进入time_wait状态,目前的端口号是57180,监听服务器仍然还是堵塞在accept调用上 另外我们需要注意的一点是,我们在服务端程序中使用fork来处理一个客户的请求,当客户端连接输入exit时,该服务器的子进程终止,但是父进程并没有调用wiat或者waitpid获取子进程的状态信息,导致进程中的状态描述符一直保存在系统中,也就是会产生僵尸进程
在服务器子进程终止的时候,会给父进程发送一个SIGCHLD信号,只需要处理该信号,捕获到该信号,然后调用wait或者waitpid,信号就是告知某个进程发送了某个事件的通知,也被称为软件中断,而且信号是异步发生的,也就是说进程预先不知道信号发生的时刻 在本程序中使用signal函数去处理SIGCHLD信号,接收到该信号之后我们有两个选择,使用wait或者waitpid函数:
两个函数的区别是,在一个子进程终止前,wait使其调用者堵塞,waitpid有一个选项,可以选择不堵塞或者堵塞,options参数选项如下:
wait只是waitpid的简化版,在考虑使用wait或者是waitpid的时候,我们先将客户端程序修改为,也就是客户建立五个与服务器的连接 当客户终止的时候,所有打开的描述符由内核自动关闭,且五个进程基本在同一时间终止,这就导致客户端发送五个FIN,每个连接一个,使得服务端程序的5个子进程基本在同一个时刻终止,也就是说在这一时刻五个SIGCHLD信号递交给父进程,如下图: 如果我们使用wait函数,只建立一个信号处理函数并且调用wait是不足以防止出现僵尸进程的,问题是所有的5个信号都在信号处理函数执行之前产生,但是信号处理函数只处理一次,那么就会留下4个僵尸进程 所以正确的做法是使用waitpid,我们在一个循环里面调用waitpid,因为waitpid是不堵塞的,无法在循环中调用wait,因为wait是会堵塞于第一个终止的子进程
同时启动这里同时启动的概念是,两个应用程序彼此执行主动打开的情况,也就是说两个主机同时发送SYN请求报文,这种情况是由可能存在的,但是在上面的代码中无法模拟,因为客户端只是作为客户,服务端只是作为服务。 当每一方发送一个SYN,并且SYN必须传递给对方,例如主机A的一个应用程序使用本地端口7777,并与主机B的端口8888执行主动打开,主机B的应用程序则使用8888端口与主机A的7777端口执行主动打开。TCP的特意设计为了可以处理同时打开,对于同时打开它仅建立一条连接,而不是两条连接,当出现同时打开时,状态变迁图如下所示 两端几乎同时在发送SYN,并且进入到SYN_SENT状态,每一端收到SYN时,状态变为SYN_RECV,同时他们都在发送SYN确认报文,当双方都收到SYN及相应的及相应的ACK时,状态都变为ESTABLISHED,一个同时打开的连接需要交换4个报文,同时我们没有将任何一端称为客户或者服务器,因为他们每一端即是客户又是服务器。 同时关闭TCP协议允许双方都同时执行主动关闭,当执行主动关闭时,状态变迁图如下: 当应用层发出关闭命令时,两端均从 ESTABLISHED变为FIN_WAIT_1。 这将导致双方各发送一个FIN ,两个 F I N 经过网络传送后分别到达另一端。收到FIN后 , 状态由FIN_WAIT_1变迁到 CLOSING,并发送最后的ACK。当收到最后的ACK时,状态变化为 TIME_WAIT 服务器进程关闭首先正确启动客户/服务器,然后找到子进程的pid,最后将该子进程kill掉,发生的步骤如下:
通过上面的步骤,总结为当服务端的FIN到达客户套接字的时候,客户正在堵塞recv读取一行上。所以客户在同时处理两个进程描述符:套接字和用户输入,它不能单纯的堵塞在这两个源的特定源的输入上,而是应该堵塞在其中任何一个源的输入上,这也就是后来由select和poll函数的目的之一 服务器主机崩溃当服务器主机崩溃的时候,已有的网络连接上不发出任何东西,当我们在客户上输入一行文本,它将写入内核,再由TCP作为一个数据分节发出,因为服务器主机崩溃,所以客户TCP持续重传数据分节,并且试图从服务器上获取一个ACK,当客户TCP最后放弃的时候,给客户进程返回一个错误,服务器主机已经崩溃,从而对客户的数据分节根本没有任何响应,那么返回的错误的ETIMEDOUT,假如在某个中间路由器判定服务器主机不可达,那么会响应一个错误的ICMP消息。当然我们不想主动发送数据也想检测到服务器主机的崩溃,那么需要采用另外一个技术,就是在套接字中使用SO_KEEPALIVE字段 结语在这两篇文章中,介绍了TCP的堵塞IO,所谓堵塞主要是指,当用户进行IO请求的时候,内核要去查看相应的缓冲区数据是否已经准备好,如果没有准备好就会一直等待,同时使用了Socket编程的一些重要函数socket、bind、listen、accept等等完成了一个TCP回射程序,通过这个程序来说明一些比较重要的问题,例如正常启动TCP完成三路握手;正常关闭完成四次挥手并且需要等待time_wait时间;当两端同时发送SYN请求时,只是产生一个连接需要发送四次报文;同时关闭也是被TCP标准所允许的;当服务器进程关闭时,由于客户端要同时处理套接字和io进程描述符,可能会接收不到服务器端发送来的FIN,这就需要我们后来介绍的select和poll函数 参考
|
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/26 4:24:03- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |