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

时间:2013-12-16 08:00:11

标签: c sockets standard-library

我最近正在阅读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);

在教科书中,它说,

  

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

为什么?

1 个答案:

答案 0 :(得分:4)

由于您引用的双关闭错误,您的解决方法无法正常工作。双线程在单线程程序中是无害的,只要没有可以打开新文件描述符的中间操作(第二次关闭只会在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. 您刚刚得到了您预期的结果,通常是在您正在使用的实现内部如何实现相关功能的副作用。在这种情况下,不能保证它没有不符合预期的极端情况,或者它将在未来版本中以相同的方式继续工作等。