《TCP IP网络编程》第十六章
第 16 章 关于 I/O 流分离的其他内容
16.1 分离 I/O 流
「分离 I/O 流」是一种常用表达。有 I/O 工具可区分二者,无论采用哪种方法,都可以认为是分离了 I/O 流。
2次 I/O 流分离:
- 第一种是第 10 章的「TCP I/O 过程」分离。通 过调用 fork 函数复制出一个文件描述符,以区分输入和输出中使用的文件描述符。虽然文件描述符本身不会根据输入和输出进行区分,但我们分开了 2 个文件描述符的用途,因此,这也属于「流」的分离。
shutdown(sock,SHUT_WR); - 第二种分离是在第 15 章。通过 2 次 fdopen 函数的调用,创建读模式 FILE 指针(FILE 结构体指针)和写模式 FILE 指针。换言之,我们分离了输入工具和输出工具,因此也可视为「流」的分离。下面是分离的理由。
分离「流」的好处:
首先是第 10 章「流」的分离目的:
- 通过分开输入过程(代码)和输出过程降低实现难度
- 与输入无关的输出操作可以提高速度
下面是第 15 章「流」分离的目的:
- 为了将 FILE 指针按读模式和写模式加以区分
- 可以通过区分读写模式降低实现难度
- 通过区分 I/O 缓冲提高缓冲性能
「流」分离带来的 EOF 问题:
第 7 章介绍过 EOF 的传递方法和半关闭的必要性。有一个语句:
shutdown(sock,SHUT_WR);
当时说过调用 shutdown 函数的基于半关闭的 EOF 传递方法。第十章添加了半关闭的相关代码。但是还没有讲采用 fdopen 函数怎么半关闭。那么是否是通过 fclose 函数关闭流呢?我们先试试:
服务端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024int main(int argc, char *argv[])
{int serv_sock, clnt_sock;FILE *readfp;FILE *writefp;struct sockaddr_in serv_adr, clnt_adr;socklen_t clnt_adr_sz;char buf[BUF_SIZE] = {0,};serv_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr));listen(serv_sock, 5);clnt_adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);readfp = fdopen(clnt_sock, "r");writefp = fdopen(clnt_sock, "w");fputs("FROM SERVER: Hi~ client? \n", writefp);fputs("I love all of the world \n", writefp);fputs("You are awesome! \n", writefp);fflush(writefp);fclose(writefp);fgets(buf, sizeof(buf), readfp);fputs(buf, stdout);fclose(readfp);return 0;
}
客户端代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024int main(int argc, char *argv[])
{int sock;char buf[BUF_SIZE];struct sockaddr_in serv_addr;FILE *readfp;FILE *writefp;sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port = htons(atoi(argv[2]));connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr));readfp = fdopen(sock, "r");writefp = fdopen(sock, "w");while (1){if (fgets(buf, sizeof(buf), readfp) == NULL)break;fputs(buf, stdout);fflush(stdout);}fputs("FROM CLIENT: Thank you \n", writefp);fflush(writefp);fclose(writefp);fclose(readfp);return 0;
}
运行结果:
从运行结果可以看出,服务端最终没有收到客户端发送的信息。
原因是:服务端代码的 fclose(writefp); 这一句,完全关闭了套接字而不是半关闭。这才是这一章需要解决的问题。
16.2 文件描述符的的复制和半关闭
终止「流」时无法半关闭原因:
下面的图描述的是服务端代码中的两个FILE 指针、文件描述符和套接字中的关系:

从图中可以看到,两个指针都是基于同一文件描述符创建的。因此,针对于任何一个 FILE 指针调用 fclose 函数都会关闭文件描述符,如图所示:

那如何进入可以进入但是无法输出的半关闭状态呢?如下图所示:

只需要创建 FILE 指针前先复制文件描述符即可。复制后另外创建一个文件描述符,然后利用各自的文件描述符生成读模式的 FILE 指针和写模式的 FILE 指针。这就为半关闭创造好了环境,因为套接字和文件描述符具有如下关系:
销毁所有文件描述符候才能销毁套接字。
也就是说,针对写模式 FILE 指针调用 fclose 函数时,只能销毁与该 FILE 指针相关的文件描述符,无法销毁套接字,如下图:

那么调用 fclose 函数候还剩下 1 个文件描述符,因此没有销毁套接字。那此时的状态是否为半关闭状态?不是!只是准备好了进入半关闭状态,而不是已经进入了半关闭状态。仔细观察,还剩下一个文件描述符。而该文件描述符可以同时进行 I/O 。因此,不但没有发送 EOF ,而且仍然可以利用文件描述符进行输出。
复制文件描述符:
与调用 fork 函数不同,调用 fork 函数将复制整个进程,此处讨论的是同一进程内完成对描述符的复制。如图:

复制完成后,两个文件描述符都可以访问文件,但是编号不同。
dup 和 dup2:
下面给出文件描述符的复制方法:
#include <unistd.h>
int dup(int fildes);
int dup2(int fildes, int fildes2);
/*
成功时返回复制的文件描述符,失败时返回 -1
fildes : 需要复制的文件描述符
fildes2 : 明确指定的文件描述符的整数值。
*/
dup2 函数明确指定复制的文件描述符的整数值。向其传递大于 0 且小于进程能生成的最大文件描述符值时,该值将成为复制出的文件描述符值。下面是dup的代码示例:
#include <stdio.h>
#include <unistd.h>int main(int argc, char *argv[])
{int cfd1, cfd2;char str1[] = "Hi~ \n";char str2[] = "It's nice day~ \n";cfd1 = dup(1); //复制文件描述符 1cfd2 = dup2(cfd1, 7); //再次复制文件描述符,定为数值 7printf("fd1=%d , fd2=%d \n", cfd1, cfd2);write(cfd1, str1, sizeof(str1));write(cfd2, str2, sizeof(str2));close(cfd1);close(cfd2); //终止复制的文件描述符,但是仍有一个文件描述符write(1, str1, sizeof(str1));close(1);write(1, str2, sizeof(str2)); //无法完成输出return 0;
}
运行结果:

复制文件描述符后「流」的分离 :
下面更改sep_clnt.c和sep_serv.c 可以使得让它正常工作,正常工作是指通过服务器的半关闭状态接收客户端最后发送的字符串。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define BUF_SIZE 1024int main(int argc, char *argv[])
{int serv_sock, clnt_sock;FILE *readfp;FILE *writefp;struct sockaddr_in serv_adr, clnt_adr;socklen_t clnt_adr_sz;char buf[BUF_SIZE] = {0,};serv_sock = socket(PF_INET, SOCK_STREAM, 0);memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);serv_adr.sin_port = htons(atoi(argv[1]));bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr));listen(serv_sock, 5);clnt_adr_sz = sizeof(clnt_adr);clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);readfp = fdopen(clnt_sock, "r");writefp = fdopen(dup(clnt_sock), "w"); //复制文件描述符fputs("FROM SERVER: Hi~ client? \n", writefp);fputs("I love all of the world \n", writefp);fputs("You are awesome! \n", writefp);fflush(writefp);shutdown(fileno(writefp), SHUT_WR); //对 fileno 产生的文件描述符使用 shutdown 进入半关闭状态fclose(writefp);fgets(buf, sizeof(buf), readfp);fputs(buf, stdout);fclose(readfp);return 0;
}
运行结果:
运行结果证明了 服务器端在半关闭状态下向客户端发送了EOF。
相关文章:
《TCP IP网络编程》第十六章
第 16 章 关于 I/O 流分离的其他内容 16.1 分离 I/O 流 「分离 I/O 流」是一种常用表达。有 I/O 工具可区分二者,无论采用哪种方法,都可以认为是分离了 I/O 流。 2次 I/O 流分离: 第一种是第 10 章的「TCP I/O 过程」分离。通 shutdown(soc…...
HTML5 基础标签
目录 前言 标题标签 段落标签 换行标签和水平线标签 文本格式化标签 图像标签 超链接标签 多媒体标签 列表标签 无序列表 有序列表 表格 合并单元格 表单 无语义的布局标签 字符实体 前言 当今互联网时代,网页是我们获取信息、交流和展示自己的重要渠…...
二、Qt的安装(Linux系统下安装Qt6过程)
一、Qt资源下载网址 网址:点击下载http://download.qt.io/ 下载:点击下载(CSDN) 二、下载二进制安装包 进入Qt资源下载网址,进入对应的资源目录,找对应系统平台的二进制安装包选择进行下载,在这这里我们选择Linux下的二…...
Python 中被忽视的核心功能
这篇文章主要介绍了一些在 Python 编程中可能被忽视的核心功能,包括默认参数、海象运算符、*args 和 **kwargs 的使用、变量交换、str 与 repr 的区别、可迭代对象的扩展解包、多个上下文管理器的使用、Python 调试器、collections.Counter 的使用、itertools 的使用…...
Java+Excel+POI+testNG基于数据驱动做一个简单的接口测试【杭州多测师_王sir】
一、创建一个apicases.xlsx放入到eclipse的resource里面,然后refresh刷新一下 二、在pom.xml文件中加入poi和testng的mvn repository、然后在eclipse的对应目录下放入features和plugins,重启eclipse就可以看到testNG了 <!--poi excel解析 --><d…...
2023.8.12号论文阅读
文章目录 TriFormer: A Multi-modal Transformer Framework For Mild Cognitive Impairment Conversion Prediction摘要本文方法实验结果 SwIPE: Efficient and Robust Medical Image Segmentation with Implicit Patch Embeddings摘要本文方法实验结果 TriFormer: A Multi-mod…...
R语言中的函数24:Combinat:combn(), permn()
介绍 combinat中的combn()和permn()函数可以得到所有的排列组合的情况 combn()函数 combn(x, m, funNULL, simplifyTRUE, …)x – 组合的向量源m – 要取的元素的数量fun – 应用于每个组合的函数(可能为空)simplify – 逻辑的,如果是FALSE,返回一个列…...
C++隐式调用和explicit关键字
隐式类型转换 #include <iostream> using namespace std;class Point { public:int x, y;Point(int x 0, int y 0): x(x), y(y) {} };void displayPoint(const Point& p) {cout << "(" << p.x << "," << p.y <&l…...
Git 清除所有本地修改
Git 清除所有本地修改 1. 上才艺 1. 上才艺 git reset --hard && git clean -dfreset 返回到某个节点,不保留修改(删除的是已跟踪的文件)clean 删除的是未跟踪的文件 谢谢...
快速获得图像中像素值的小工具
之前项目中为了做lka中获得rgb图像信息,网上大多方案是确定相关的区域然后输出像素值,这个方法太麻烦,做了一个简单的使用鼠标点击图片某区域,然后直接在终端输出该区域的像素值。下面是源码: import cv2 import matp…...
yolo数据增强
yolo数据增强 🔥 数据集说明 😂一. labelimg 😅1. labelimg数据增强2. labelimg转换为yolo数据集二. labelme 😆1. labelme 分割数据增强2. labelme分割数据集转换yolo分割数据集三. coco 数据集格式1. coco 数据集格式数据增强 并转换至labelme 格式2. coco 数据集格式…...
环保行业如何开发废品回收微信小程序
废品回收是近年来受到越来越多人关注的环保行动。为了推动废品回收的普及和方便,我们可以利用微信小程序进行制作,方便人们随时随地参与废品回收。 首先,我们需要注册并登录乔拓云账号,并进入后台。乔拓云是一个提供微信小程序制作…...
个人对哈希数据结构学习总结 -- 理论篇
个人对哈希数据结构学习总结 -- 理论篇 引言哈希表设计思考哈希冲突Hash Functions冲突解决开放地址法(Open Addressing)分离链表法(Separate Chaining)Two-way Chaining Dynamic Hash Tableschained Hashingextendible hashinglinear hashing说明 spiral storage 使用场景小结…...
在CMamke生成的VS项目中插入程序
在主文件夹的CMakeLists.tex中加入SET(COMPILE_WITH_LSVM OFF CACHE BOOL "Compile with LSVM") 再添加IF(COMPILE_WITH_LSVM) MESSAGE("Compiling with: LSVM") ADD_DEFINITIONS(-DCOMPILE_WITH_LSVM) ADD_SUBDIRECTORY(LSVM) LIST(APPEND SRC LSVM_wrap…...
198、仿真-基于51单片机函数波形发生器调幅度频率波形Proteus仿真(程序+Proteus仿真+原理图+流程图+元器件清单+配套资料等)
毕设帮助、开题指导、技术解答(有偿)见文未 目录 一、硬件设计 二、设计功能 三、Proteus仿真图 四、原理图 五、程序源码 资料包括: 需要完整的资料可以点击下面的名片加下我,找我要资源压缩包的百度网盘下载地址及提取码。 方案选择 单片机的选…...
Django 初级指南:创建你的第一个 Django 项目
Django 是一个强大的 Python Web 框架,它采用了“模型-视图-控制器”(MVC)的设计模式,能够帮助开发者快速、简洁地创建高质量的 Web 应用。这篇文章将引导你创建你的第一个 Django 项目。 一、安装 Django 首先,你需…...
【MySQL】使用C++连接数据库
目录 前置工作代码常用函数接口整体示例: 前置工作 创建数据库并选中 mysql> create database conn; Query OK, 1 row affected (0.01 sec)mysql> show databases; -------------------- | Database | -------------------- | information_schema…...
php代码审计,php漏洞详解
文章目录 1、输入验证和输出显示2、命令注入(Command Injection)3、eval 注入(Eval Injection)4、跨网站脚本攻击(Cross Site Scripting, XSS)5、SQL 注入攻击(SQL injection)6、跨网站请求伪造攻击(Cross Site Request Forgeries, CSRF)7、Session 会话劫持(Session Hijacking…...
uniapp 将标题背景更换背景图片 完美解决(附加源码+实现效果图)
问题描述 今天拿到小程序的设计效果图后,标题部分背景需要加背景图片,以往我做的都是标题背景更换颜色等,加背景图片还是第一次遇到,大家可以先看下我的效果图是否与你遇到的问题一致! 首页标题的背景是个背景图片。 …...
必备工具:Postman Newman 详解
目录 Postman Newman 是什么? Postman Newman 的作用 如何使用 Postman Newman? 第一步:安装 Node.js 第二步:全局安装 Newman 第三步:导出集合或环境变量为 JSON 格式 第四步:使用 Newman 运行测试…...
【Python】 -- 趣味代码 - 小恐龙游戏
文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...
微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】
微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来,Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...
Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...
