【问题标题】:Restriction of C standard I/O and why we can't use C standard I/O with socketsC 标准 I/O 的限制以及为什么我们不能将 C 标准 I/O 与套接字一起使用
【发布时间】:2013-12-16 08:00:10
【问题描述】:

我最近在读 CSAPP。在 10.9 节中,它说标准 I/O 不应该与 socket 一起使用,原因如下:

(1)标准I/O的限制

限制 1:输入函数跟随输出函数。输入 如果没有介入调用,函数不能跟随输出函数 fflush、fseek、fsetpos 或倒带。 fflush 函数清空 与流关联的缓冲区。后三个函数使用 Unix I/O lseek 函数重置当前文件位置。

限制 2:输出函数遵循输入函数。输出 如果没有介入调用,函数不能跟随输入函数 fseek、fsetpos 或 rewind,除非输入函数遇到 文件结束。

(2) 在socket上使用lseek函数是非法的。

问题 1:如果我违反了限制会怎样?我写了一个代码sn-p,效果很好。

问题 2:绕过限制 2,一种方法如下:

File *fpin, *fpout;

fpin = fdopen(sockfd, "r");
fpout = fdopen(sockfd, "w");

/* Some Work Here */

fclose(fpin);
fclose(fpout);

教科书上说,

在线程程序中关闭已经关闭的描述符是 灾难的秘诀。

为什么?

【问题讨论】:

    标签: c sockets standard-library


    【解决方案1】:

    由于您引用的双重关闭错误,您的解决方法无法正常工作。双重关闭在单线程程序中是无害的,只要没有可以打开新文件描述符的干预操作(第二次关闭将在EBADF 下无害地失败),但它们是多线程程序中的关键错误。考虑这种情况:

    • 线程 A 调用 close(n)
    • 线程 B 调用 open 并返回 n 并存储为 int fd1
    • 线程 A 再次调用 close(n)
    • 线程 B 再次调用open 并再次返回n,并存储为fd2
    • 线程 B 现在尝试写入 fd1,实际上写入的是第二次调用 open 打开的文件,而不是第一次打开的文件。

    这可能导致大量文件损坏、信息泄露(想象一下将密码写入套接字而不是本地文件)等。

    但是,问题很容易解决。不要使用相同的文件描述符调用fdopen 两次,只需使用dup 复制它并将副本传递给fdopen。通过这个简单的修复,stdio 可以完美地与套接字一起使用。它仍然不适合异步事件循环使用,但是如果您使用线程进行 IO,它会很好。

    编辑:我想我跳过了回答你的问题 1。如果你违反了关于如何在 stdio 流上切换输入和输出的规则会发生什么情况是未定义的行为 .这意味着测试它并看到它“有效”是没有意义的;它可能意味着:

    1. 您正在使用的 C 实现为在这种情况下发生的情况提供了一个定义(作为其文档的一部分),并且它与您想要的行为相匹配。在这种情况下,您可以使用它,但您的代码将无法移植到其他实现。出于这个原因,这样做被认为是非常糟糕的做法。或者,

    2. 您只是偶然获得了预期的结果,通常是您正在使用的实现内部如何实现相关功能的副作用。在这种情况下,不能保证它不会出现无法按预期运行的极端情况,或者它会在未来的版本中继续以相同的方式工作,等等。

    【讨论】:

    • 感谢您的详细解释。但是我不太明白“异步事件循环的用法”。
    猜你喜欢
    • 2012-08-15
    • 2012-01-20
    • 1970-01-01
    • 1970-01-01
    • 2015-05-24
    • 2016-02-20
    • 1970-01-01
    • 1970-01-01
    • 2017-09-20
    相关资源
    最近更新 更多