限制C标准I / O以及为什么我们不能将C标准I / O与套接字一起使用

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

(1)标准I / O的限制

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

限制2:输入function后的输出function。 除非输入函数遇到文件结尾,否则输出函数无法在没有对fseek,fsetpos或rewind进行干预调用的情况下跟随输入函数。

(2)在套接字上使用lseekfunction是违法的。

问题1 :如果违反限制会怎样? 我写了一个代码片段,它工作正常。

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

File *fpin, *fpout; fpin = fdopen(sockfd, "r"); fpout = fdopen(sockfd, "w"); /* Some Work Here */ fclose(fpin); fclose(fpout); 

在教科书中,它说,

在线程程序中关闭已经关闭的描述符是灾难的一个方法。

为什么?

由于您引用的双重错误,您的解决方法无法按照书面forms运行。 双线程在单线程程序中是无害的,只要没有可以打开新文件描述符的中间操作(第二次关闭将无法使用EBADF无害),但它们是multithreading程序中的关键错误。 考虑这种情况:

  • 线程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. 您只是偶然得到了您期望的结果,通常是在您正在使用的实现上如何在内部实现相关function的副作用。 在这种情况下,不能保证它没有不能按预期运行的极端情况,或者它将在未来的版本中继续以相同的方式工作,等等。