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