《TCP/IP网络编程》学习笔记 | Chapter 15:套接字与标准 I/O

《TCP/IP网络编程》学习笔记 | Chapter 15:套接字与标准 I/O

  • 《TCP/IP网络编程》学习笔记 | Chapter 15:套接字与标准 I/O
    • 标准 I/O 函数
      • 标准 I/O 函数的两个优点
      • 标准 I/O 函数和系统函数之间的性能对比
      • 标准 I/O 函数的几个缺点
    • 使用标准 I/O 函数
      • 利用 fdopen 函数转换为 FILE 结构体指针
      • 利用 fileno 函数转换为文件描述符
    • 基于套接字的标准 I/O 函数使用
    • 习题
      • (1)请说明标准I/O函数的2个优点。它为何拥有这2个优点?
      • (2)利用标准I/O函数传输数据时,下面的想法是错误的:“调用fputs函数传输数据时,调用后应立即开始发送!”为何上述想法是错误的?为了达到这种效果应添加哪些处理过程?

《TCP/IP网络编程》学习笔记 | Chapter 15:套接字与标准 I/O

标准 I/O 函数

标准 I/O 函数的两个优点

除了使用 read 和 write 函数收发数据外,还能使用标准 I/O 函数收发数据。下面是标准 I/O 函数的两个优点:

  • 标准 I/O 函数具有良好的移植性
  • 标准 I/O 函数可以利用缓冲提高性能

不仅是I/O函数,所有的标准函数都具有很好的移植性,为了支持所有的操作系统和编译器,这些函数都是按照ANSI C标准定义的。

创建套接字时,操作系统会准备 I/O 缓冲。此缓冲在执行 TCP 协议时发挥着非常重要的作用。此时若使用标准 I/O 函数,将得到额外的缓冲支持。如下图:

在这里插入图片描述

套接字中的缓冲区主要是为了实现TCP协议而设立的,TCP在传输数据的过程中,如果丢失了数据,将会再次进行传输,而再次发送数据,意味着数据保存在了某个地方,并没有丢失,保存的地方就是套接字的输出缓冲区。与之相反,使用标准IO函数缓冲的主要目的是为了提高性能。

实际上,缓冲区并非在所有情况下都能带来卓越的性能,但是如果传输的数据量越大,有无缓冲带来的性能差异就越大。

标准 I/O 函数和系统函数之间的性能对比

下面是利用系统函数的示例:

#include <stdio.h>
#include <fcntl.h>
#include <cstdlib>
#include <ctime>
#include <unistd.h>#define BUF_SIZE 3// 采用未提供缓冲区技术的read,write方法进行拷贝文件int main(int argc, char *argv[])
{int fd1, fd2; // 文件描述符int len;char buffer[BUF_SIZE];fd1 = open("news.txt", O_RDONLY);fd2 = open("cpy.txt", O_WRONLY | O_CREAT | O_TRUNC);clock_t start = clock();while ((len = read(fd1, buffer, BUF_SIZE)) > 0)write(fd2, buffer, len);close(fd1);close(fd2);clock_t end = clock();double timespan = ((double)(end - start)) / CLOCKS_PER_SEC;printf("Total time consume is %f ms.\n", timespan * 1000);return 0;
}

输出:

在这里插入图片描述

如果是采用上述代码,数据传输的时间需要很长。

下面采用标准I/O函数复制文件:

#include <stdio.h>
#include <fcntl.h>
#include <cstdlib>
#include <ctime>#define BUF_SIZE 3int main(int argc, char *argv[])
{FILE *fp1, *fp2;char buffer[BUF_SIZE];fp1 = fopen("news.txt", "r");fp2 = fopen("cpy.txt", "w");if (fp1 == NULL || fp2 == NULL){printf("Failed to open file.\n");return -1;}// 程序计数clock_t start = clock();while (fgets(buffer, BUF_SIZE, fp1) != NULL)fputs(buffer, fp2);fclose(fp1);fclose(fp2);clock_t end = clock();// 计算消耗时间double timespan = ((double)(end - start)) / CLOCKS_PER_SEC;printf("The total time consume is %f ms.\n", timespan * 1000);return 0;
}

输出:

在这里插入图片描述

该示例用标准IO函数fputs和fgets函数复制文件,是一种基于缓冲的复制,就很快。

标准 I/O 函数的几个缺点

标准 I/O 函数存在以下几个缺点:

  • 不容易进行双向通信。
  • 有时可能频繁调用 fflush 函数。
  • 需要以 FILE 结构体指针的形式返回文件描述符。

打开文件时,如果希望同时进行读操作,则应以 r+、w+、a+ 模式打开。但因为缓冲的缘故,每次切换读写工作状态时应调用fIush为数。这也会影响基于缓冲的性能提高。而且,为了使用标准IO函数,需要FILE结构体指针。而创建套接字时默认返回文件描述符,需要将文件描述符转化为FILE指针。

使用标准 I/O 函数

利用 fdopen 函数转换为 FILE 结构体指针

函数原型如下:

#include <stdio.h>FILE *fdopen(int fildes, const char *mode);

成功时返回转换的 FILE 结构体指针,失败时返回 NULL。

参数:

  • fildes:需要转换的文件描述符
  • mode:将要创建的 FILE 结构体指针的模式信息

示例程序:

#include <stdio.h>
#include <fcntl.h>int main()
{FILE *fp;int fd = open("data.dat", O_WRONLY | O_CREAT | O_TRUNC); // 创建文件并返回文件描述符if (fd == -1){fputs("file open error", stdout);return -1;}fp = fdopen(fd, "w"); // 返回写模式的 FILE 指针fputs("NetWork C programming \n", fp);fclose(fp);return 0;
}

编译,运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 15>gcc desto.c -o destoC:\Users\81228\Documents\Program\TCP IP Project\Chapter 15>destoC:\Users\81228\Documents\Program\TCP IP Project\Chapter 15>cat data.dat
'cat' 不是内部或外部命令,也不是可运行的程序
或批处理文件。C:\Users\81228\Documents\Program\TCP IP Project\Chapter 15>type data.dat
NetWork C programming

可以看出,fdopen 函数将文件描述符转换为 FILE 指针,并可以通过该指针调用标准 I/O 函数。

利用 fileno 函数转换为文件描述符

函数原型如下:

#include <stdio.h>int fileno(FILE *stream);

成功时返回文件描述符,失败时返回 -1。

示例程序:

#include <stdio.h>
#include <fcntl.h>int main()
{FILE *fp;int fd = open("data.dat", O_WRONLY | O_CREAT | O_TRUNC);if (fd == -1){fputs("file open error", stdout);return -1;}printf("First file descriptor : %d \n", fd);fp = fdopen(fd, "w"); // 转成 file 指针fputs("TCP/IP SOCKET PROGRAMMING \n", fp);printf("Second file descriptor: %d \n", fileno(fp)); // 转回文件描述符fclose(fp);return 0;
}

编译,运行结果:

C:\Users\81228\Documents\Program\TCP IP Project\Chapter 15>gcc todes.c -o todesC:\Users\81228\Documents\Program\TCP IP Project\Chapter 15>todes
First file descriptor : 3
Second file descriptor: 3

输出的文件描述符值相同,证明 fileno 函数争取转换了文件描述符。

基于套接字的标准 I/O 函数使用

把第四章的回声客户端和回声服务端的内容改为基于标准 I/O 函数的数据交换形式。

echo_stdserv.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE 1024void error_handling(char *message);int main(int argc, char *argv[])
{int serv_sock, clnt_sock;char message[BUF_SIZE];int str_len, i;struct sockaddr_in serv_adr, clnt_adr;socklen_t clnt_adr_sz;FILE *readfp;FILE *writefp;if (argc != 2){printf("Usage : %s <port>\n", argv[0]);exit(1);}serv_sock = socket(PF_INET, SOCK_STREAM, 0);if (serv_sock == -1)error_handling("socket() error");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]));if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)error_handling("bind() error");if (listen(serv_sock, 5) == -1)error_handling("listen() error");clnt_adr_sz = sizeof(clnt_adr);// 调用 5 次 accept 函数,共为 5 个客户端提供服务for (i = 0; i < 5; i++){clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);if (clnt_sock == -1)error_handling("accept() error");elseprintf("Connect client %d \n", i + 1);readfp = fdopen(clnt_sock, "r");writefp = fdopen(clnt_sock, "w");while (!feof(readfp)){fgets(message, BUF_SIZE, readfp);fputs(message, writefp);// 刷新文件流中的缓冲区,将缓冲区中的数据强制写入文件fflush(writefp);}fclose(readfp);fclose(writefp);}close(serv_sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

echo_client.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>#define BUF_SIZE 1024void error_handling(char *message);int main(int argc, char *argv[])
{int sock;char message[BUF_SIZE];int str_len;struct sockaddr_in serv_adr;FILE *readfp;FILE *writefp;if (argc != 3){printf("Usage : %s <IP> <port>\n", argv[0]);exit(1);}sock = socket(PF_INET, SOCK_STREAM, 0);if (sock == -1)error_handling("socket() error");memset(&serv_adr, 0, sizeof(serv_adr));serv_adr.sin_family = AF_INET;serv_adr.sin_addr.s_addr = inet_addr(argv[1]);serv_adr.sin_port = htons(atoi(argv[2]));if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr)) == -1)error_handling("connect() error!");elseputs("Connected...........");readfp = fdopen(sock, "r");writefp = fdopen(sock, "w");while (1){fputs("Input message(Q to quit): ", stdout);fgets(message, BUF_SIZE, stdin);if (!strcmp(message, "q\n") || !strcmp(message, "Q\n"))break;fputs(message, writefp);fflush(writefp);fgets(message, BUF_SIZE, readfp);printf("Message from server: %s", message);}fclose(writefp);fclose(readfp);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

此处接收数据的缓冲区没有在结尾增加0,与第四章的不同,原因:使用标准I/O函数后可以按字符串单位进行数据交换。

printf 函数以参数" % s"输出字符串时过程为:

  1. 从首地址开始逐字节寻址,把存储单元(一个字节)内的数据转换为ASCII字符格式输出。
  2. 直到某一个字节内存的元素为字符’\0’时,输出此字符并且寻址结束。

习题

(1)请说明标准I/O函数的2个优点。它为何拥有这2个优点?

  • 标准 I/O 函数具有良好的移植性。因为这些函数都是按照ANSI C标准定义的,支持所有的操作系统和编译器。
  • 标准 I/O 函数可以利用缓冲提高性能。因为使用标准 I/O 函数,将得到额外的缓冲支持。可以降低传输的数据量,减少数据向输出缓冲移动的次数,这两种都能提升性能。

(2)利用标准I/O函数传输数据时,下面的想法是错误的:“调用fputs函数传输数据时,调用后应立即开始发送!”为何上述想法是错误的?为了达到这种效果应添加哪些处理过程?

通过标准输出函数的传输的数据不直接通过套接字的输出缓冲区发送,而是保存在标准输出函数的缓冲中,然后再用fflush函数进行输出。因此,即使调用fputs函数,也不能立即发送数据。如果想保障数据传输的时效性,必须经过fflush函数的调用过程。

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

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

相关文章

[面试]-golang基础面试题总结

文章目录 panic 和 recover**注意事项**使用 pprof、trace 和 race 进行性能调试。**Go Module**&#xff1a;Go中new和make的区别 Channel什么是 Channel 的方向性&#xff1f;如何对 Channel 进行方向限制&#xff1f;Channel 的缓冲区大小对于 Channel 和 Goroutine 的通信有…

鸿蒙进阶-状态管理

大家好啊&#xff0c;这里是鸿蒙开天组&#xff0c;今天我们来学习状态管理。 开始组件化开发之后&#xff0c;如何管理组件的状态会变得尤为重要&#xff0c;咱们接下来系统的学习一下这部分的内容 状态管理机制 在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&a…

深度学习每周学习总结J6(ResNeXt-50 算法实战与解析 - 猴痘识别)

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 目录 0. 总结ResNeXt基本介绍 1. 设置GPU2. 导入数据及处理部分3. 划分数据集4. 模型构建部分5. 设置超参数&#xff1a;定义损失函数&…

IDEA 2024.3 版本更新主要功能介绍

IDEA 2024.3 版本提供的新特性 IntelliJ IDEA 2024.3 的主要新特性&#xff1a; AI Assistant 增强 改进的代码补全和建议更智能的代码分析和重构建议Java 支持改进 支持 Java 21 的所有新特性改进的模式匹配和记录模式支持更好的虚拟线程调试体验开发工具改进 更新的 UI/UX 设…

java基础概念37:正则表达式2-爬虫

一、定义 【回顾】正则表达式的作用 作用一&#xff1a;校验字符串是否满足规则作用二&#xff1a;在一段文本中查找满足要求的内容——爬虫 二、本地爬虫VS网络爬虫 2-1、本地爬虫 示例&#xff1a; 代码优化&#xff1a; public static void main(String[] args) {// 大…

AmazonS3集成minio实现https访问

最近系统全面升级到https&#xff0c;之前AmazonS3大文件分片上传直接使用http://ip:9000访问minio的方式已然行不通&#xff0c;https服务器访问http资源会报Mixed Content混合内容错误。 一般有两种解决方案&#xff0c;一是升级minio服务&#xff0c;配置ssl证书&#xff0c…

JavaWeb——Mybatis

6. Mybatis MyBatis是一款优秀的持久层框架&#xff0c;用于简化JDBC的开发 6.1. Mybatis入门 6.1.1. 入门程序 6.1.2. JDBC 6.1.3. 数据库连接池 6.1.4. Lombok 6.2. Mybatis基础操作 6.2.1. 删除 6.2.1.1. 根据主键删除 6.2.1.2. 预编译SQL #{id}在编译过程中会替换成?…

MongoDB数据备份与恢复(内含工具下载、数据处理以及常见问题解决方法)

一、工具准备 对MongoDB进行导入导出、备份恢复等操作时需要用到命令工具&#xff0c;我们要先检查一下MongoDB安装目录下是否有这些工具&#xff0c;正常情况下是没有的:)&#xff0c;因为新版本的MongoDB安装时不包含这些工具&#xff0c;需要我们手动下载安装。下载成功之后…

学习与理解LabVIEW中多列列表框项名和项首字符串属性

多列列表框控件在如下的位置&#xff1a; 可以对该控件右击&#xff0c;如下位置&#xff0c;即可设置该控件的显示项&#xff1a; 垂直线和水平线指的是上图中组成单元格的竖线和横线&#xff08;不包括行首列首&#xff09; 现在介绍该多列列表框的两个属性&#xff0c;分别…

【信息系统项目管理师】第2章:信息技术发展 考点梳理

文章目录 2.1 信息技术及其发展2.1.1 计算机软硬件2.1.2 计算机网络2.1.3 存储和数据库2.1.4 信息安全2.1.5 信息技术的发展 2.2 新一代信息技术及应用2.2.1 物联网2.2.2 云计算2.2.3 大数据2.2.4 区块链2.2.5 人工智能2.2.6 虚拟现实 2.1 信息技术及其发展 2.1.1 计算机软硬件…

《现代制造技术与装备》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答 问&#xff1a;《现代制造技术与装备》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的第二批认定学术期刊。 问&#xff1a;《现代制造技术与装备》级别&#xff1f; 答&#xff1a;省级。主管单位&#xff1a;齐鲁工业大学&#xff0…

QT:QListView实现table自定义代理

介绍 QListVIew有两种切换形式&#xff0c;QListView::IconMode和QListView::ListMode&#xff0c;通过setViewMode()进行设置切换。因为QListView可以像QTreeView一样显示树形结构&#xff0c;也可以分成多列。这次目标是将ListView的ListMode形态显示为table。使用代理&…

统计学常用的分析方法:T检验

T检验是一种用于比较数据集均值差异的统计方法&#xff0c;包括单样本t检验、配对样本t检验和独立样本t检验&#xff0c;可通过MATLAB、Python和R等工具实现t检验。如果数据不符合正态分布&#xff0c;可考虑使用非参数分析&#xff0c;多余两组数据时&#xff0c;可采用多重比…

win10中使用ffmpeg和MediaMTX 推流rtsp视频

在win10上测试下ffmpeg推流rtsp视频&#xff0c;需要同时用到流媒体服务器MediaMTX 。ffmpeg推流到流媒体服务器MediaMTX &#xff0c;其他客户端从流媒体服务器拉流。 步骤如下&#xff1a; 1 下载MediaMTX github: Release v1.9.3 bluenviron/mediamtx GitHub​​​​​…

网络安全-web架构-nginx配置

1. nginx访问&#xff1a; 访问的是index.html&#xff0c; 访问ip访问的资源就是在/usr/share/nginx/html中&#xff1b; 当nginx不认识&#xff0c;浏览器认识的话&#xff0c;浏览器会自动渲染。 当nginx认识&#xff0c;浏览器不认识的话&#xff0c;浏览器会把它加载成…

Python + 深度学习从 0 到 1(00 / 99)

希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持&#xff01; ⭐ 什么是深度学习&#xff1f; 人工智能、机器学习与…

Chinese SimpleQA:包含3000个高质量问题,覆盖6个主要主题,每个主题下有99个细分主题,用来评估大型语言模型中文事实性能力的基准测试.

2024-11-12, 由阿里巴巴集团旗下的淘宝和天猫团队创建的Chinese SimpleQA数据集&#xff0c;是首个全面评估语言模型回答简短问题事实性能力的中文基准测试。该数据集的创建&#xff0c;为理解和提升大型语言模型在中文环境下的事实性回答能力提供了重要的工具和标准。 数据集…

Kafka 生产者优化与数据处理经验

Kafka&#xff1a;分布式消息系统的核心原理与安装部署-CSDN博客 自定义 Kafka 脚本 kf-use.sh 的解析与功能与应用示例-CSDN博客 Kafka 生产者全面解析&#xff1a;从基础原理到高级实践-CSDN博客 Kafka 生产者优化与数据处理经验-CSDN博客 Kafka 工作流程解析&#xff1a…

Python中Tushare(金融数据库)入门详解

文章目录 Python中Tushare&#xff08;金融数据库&#xff09;入门详解一、引言二、安装与注册1、安装Tushare2、注册与获取Token 三、Tushare基本使用1、设置Token2、获取数据2.1、获取股票基础信息2.2、获取交易日历2.3、获取A股日线行情2.4、获取沪股通和深股通成份股2.5、获…

shell第一次作业

要求&#xff1a; 通过shell脚本分析部署nginx网络服务 1.接收用户部署的服务名称 2.判断服务是否安装 ​ 已安装&#xff1b;自定义网站配置路径为/www&#xff1b;并创建共享目录和网页文件&#xff1b;重启服务 ​ 没有安装&#xff1b;安装对应的软件包 3.测试 判断服务是…