我最近在读CSAPP。在10.9节中,它说标准I/O不应该与socket一起使用,原因如下:
(1) 标准I/O的限制
限制1:输入函数在输出函数之后。一个输入
如果没有中间调用,函数无法跟随输出函数
fflush、fseek、fsetpos 或倒回。 fflush 函数清空
与流关联的缓冲区。后三个函数使用
Unix I/O lseek 函数重置当前文件位置。
限制2:输出函数跟随输入函数。一个输出
如果没有中间调用,函数无法跟随输入函数
fseek、fsetpos 或倒回,除非输入函数遇到
文件结尾。
(2) 在套接字上使用lseek函数是非法的。
问题1: 如果我违反限制会怎样?我写了一个代码片段,它工作得很好。
问题2:要绕过限制 2,一种方法如下:
File *fpin, *fpout;
fpin = fdopen(sockfd, "r");
fpout = fdopen(sockfd, "w");
/* Some Work Here */
fclose(fpin);
fclose(fpout);
课本上是这么说的:
关闭线程程序中已经关闭的描述符是
灾难的秘诀。
Why?
由于您引用的双重关闭错误,您的解决方法无法按书面方式工作。只要没有可能打开新文件描述符的干预操作,双重关闭在单线程程序中是无害的(第二次关闭将无害地失败,并显示EBADF
)但它们是多线程程序中的严重错误。考虑这种情况:
- 线程A调用
close(n)
.
- 线程B调用
open
它返回n
其存储为int fd1
.
- 线程A调用
close(n)
again.
- 线程B调用
open
再次返回n
再次,它被存储为fd2
.
- 线程 B 现在尝试写入
fd1
并实际写入第二次调用打开的文件open
而不是第一次打开的那个。
这可能会导致大量文件损坏、信息泄漏(想象一下将密码写入套接字而不是本地文件)等。
不过,这个问题很容易解决。而不是打电话fdopen
使用相同的文件描述符两次,只需使用dup
复制它并将副本传递给fdopen
。通过这个简单的修复,stdio 就可以完美地与套接字一起使用。它仍然不适合异步事件循环使用,但如果您使用线程进行 IO,它就非常有用。
Edit:我想我跳过了回答你的问题 1。如果你违反了关于如何在 stdio 流上的输入和输出之间切换的规则,会发生什么未定义的行为。这意味着测试它并看到它“有效”是没有意义的;它可能意味着:
您正在使用的 C 实现为这种情况下发生的情况提供了定义(作为其文档的一部分),并且它与您想要的行为相匹配。在这种情况下,您可以使用它,但您的代码将无法移植到其他实现。因此,这样做被认为是非常糟糕的做法。或者,
您只是偶然获得了预期的结果,通常是您正在使用的实现内部实现相关功能的副作用。在这种情况下,无法保证它不会出现无法按预期运行的极端情况,或者它会在未来的版本中继续以相同的方式工作,等等。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)