可能的错误 - 将流与已经与之关联的FD关联起来?

时间:2010-02-06 18:28:19

标签: c unix networking sockets

你好。我是一个网络类,我们正在使用我们迄今为止在课堂上学到的套接字函数创建自己的“网络API”。

对于作业,教授提供了已经完整的聊天和服务器程序,我们要填写一个空白的.c文件,该文件有一个相关的头文件,描述了我们的小“网络API”中的函数调用,即聊天和服务器使用的程序。

我已接近完成但遇到了问题。我会进去搞乱我的代码,但是我的“网络API”的所有其他部分都工作,所以当我如此接近完成并且稍微改变导致其他部分没有时,我不想搞砸它上班。另外,我并非100%确定我确实知道我的错误是什么。

我的问题是我的函数recv_line应该模仿recv()。

我的函数recv_line从客户端获取文件描述符,用于与服务器建立的连接。然后我继续使用fdopen将流与文件描述符相关联。代码如下:

  // Associate a stream with the sock_fd
  if (NULL == (fdstream = fdopen(sock_fd, "r"))) {
    return -1;
  }

现在这是我相信我的错误的地方,但正如我所说,我不是100%肯定。首先,我第一次使用我的recv_line函数时效果很好。事实上它确实按预期方式对行进行了recv_line。但问题出现了第二次。

我使用GDB完成了我的功能,上面的代码确实执行没有问题。然后我继续从我的流中剥离一个字符(使用fgetc)并检查它是否等于EOF字符。如果是我返回,否则我会处理它。我第二次调用recv_line它返回cuse,每次都读取EOF字符。

我假设发生了这种情况,因为发送到客户端的数据是在原始流(第一次创建的数据)中,第二次调用fdopen时,我创建的第二个流中没有任何数据?当我第二次打电话给我时,我不完全确定fdopen是如何工作的?

在我看来,我以这种方式阅读我的数据,因为我们应该继续读取数据,直到我们读取EOF或字符序列\ r \ n。我在流中使用getc和ungetc来提供一个预检机制,用于检查EOF或\ r \ n序列。

所以尽管如此,有没有人知道我的代码和程序到底发生了什么?我用Google搜索一个方法来检查FD是否有一个关联的流,并且没有成功找到/看到任何东西。我做了一些研究,看看是否有一些fstat时间功能,让我看到相关的蒸汽,并没有任何结果。我可以复制传入函数recv_line的FD,然后复制FD上的fdopen并避免所有这些问题吗?

感谢所有帮助。如果我不够清楚,我很抱歉。如果你要求我这样做,我会尽力澄清任何事情。抱歉,我的问题太长了。 = *(

再次感谢。我非常感谢你的帮助。

3 个答案:

答案 0 :(得分:2)

这是来自fdopen手册页:

  

fdopen()函数将流与现有文件相关联   描述符,fd。流的模式(值“r”,“r +”之一,   “w”,“w +”,“a”,“a +”)必须与文件的模式兼容   描述。新流的文件位置指示器设置为   属于fd,错误和文件结束指标   清除。模式“w”或“w +”不会导致文件截断。的的   文件描述符没有重复,并且当fdopen()创建的流关闭时将关闭。 [emph。矿]

换句话说,正如我所理解的那样(这不是我的专业领域),你一遍又一遍地“劫持”套接字文件描述符的想法实际上是问题,因为当你完成了它是第一次,你不仅关闭你的流,而且还关闭套接字连接。

答案 1 :(得分:2)

  

我假设发生了这种情况,因为发送到客户端的数据是在原始流(第一次创建的数据)中,第二次调用fdopen时没有任何数据 的文件描述符第二个流读取 我创建了

这非常正确(有一些小修正)。流对象的主要职责是从内核读取文件描述符数据流到用户区,这样您就不需要系统调用的开销。但是一旦流对象读取数据流,内核就会使它的'读指针'前进,所以下次当你尝试从文件描述符中读取数据时,它就会从你离开的地方开始。

您只能为给定的描述符调用fdopen 一次。如果您可以重新构建代码,以便有一个地方可以轻松调用fdopen一次,那么就可以了。

一旦你调用了fdopen,你就不应再对文件描述符做任何事情了 - 甚至不关闭它(当你调用fclose时,底层描述符将被关闭)。

根据Jonathon Leffler的评论,我猜教授的代码也将关闭描述符。如果是这样,则不能使用fdopen。

一些想法:

  1. 只需逐字节读取fd。性能不是很好但是会起作用。
  2. 您是否可以假设您的代码一次只处理一个连接?如果是这样,只需在recv_line中有一个缓冲fd数据的静态缓冲区。当缓冲区为空时填满缓冲区,然后从中读取数据。
  3. 如果需要处理多个连接,可以扩展#2以使每个文件描述符有一个缓冲区。你需要某种地图来处理这个问题。
  4. 根据评论进行更新

    @Chris - 让我解释一下使用fdopen的风险。在文件描述符上使用fdopen之后,必须调用fclose (或者冒着泄漏为FILE分配的资源*的风险),并且无法在描述符上调用close (因为fclose描述符可能会遇到多次关闭描述符的问题)。因此,除非你的代码已被委派了关闭描述符的责任(在这种情况下你可以调用fclose),你不能使用fdopen。

    没有内置方法来检测是否已在描述符上调用fdopen。您需要自己添加逻辑。你需要保留fdopen返回的FILE *的集合;如果你已经有一个给定描述符的FILE *,你会使用它,否则你会调用fdopen。

答案 2 :(得分:1)

我不确定我是否真的理解你的问题,但我建议在创建套接字时直接打开输入/输出流,只要套接字有效,就将它们与套接字一起保存在结构中(连接的)。这样您就不需要为每一行打开新的流。然后recv_line函数将输入流作为参数而不是套接字。