你好。我在网络类(class)中,我们将使用到目前为止在类里面学到的套接字函数来创建自己的“网络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函数效果很好。实际上,它确实按预期的那样完成了该行。但是问题是第二次出现。

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

我假设发生这种情况是因为发送到客户端的数据在原始流中(第一次创建的流),而第二次我调用fdopen时,我创建的第二个流中没有任何数据吗?我不确定第二次调用它时fdopen的工作方式吗?

另外,我应该以这种方式读取数据,因为我们应该继续读取数据,直到读取EOF或字符序列\r\n。我将getc和ungetc与流一起使用,以提供一种预检查机制,该机制将检查EOF或\r\n序列。

因此,话虽这么说,没有人知道我的大量代码和程序到底发生了什么吗?我在Google周围寻找一种方法来检查FD是否具有关联的流,并且没有成功找到/看到任何东西。我做了一些研究,看是否有一些fstat时间函数可以让我看到相关的 Steam ,但一无所获。我是否可以将传递给函数recv_line的FD复制,然后在复制的FD上进行fdopen并避免所有这些问题?

感谢您的所有帮助。很抱歉,如果我不够清楚。如果您要求我做的话,我会尽力澄清。抱歉,我的问题太冗长了。 = *(

再次感谢。非常感谢您的帮助。

最佳答案



这几乎是正确的(有一些小的更正)。流对象的主要职责是从内核到用户空间读取文件描述符数据流,因此您不需要系统调用的开销。但是,一旦流对象读取了数据流,内核便将其前进为“读取指针”,因此,下次您尝试从文件描述符读取数据时,它将在您中断的地方继续。

对于给定的描述符,您只能一次调用fdopen 。如果您可以重组代码,以便在某个地方可以轻松调用一次fdopen,那么这种方法就可以解决。

调用fdopen之后,就不再对文件描述符执行任何操作-甚至不要关闭它(当您调用fclose时,底层描述符将被关闭)。

基于乔纳森·莱夫勒的评论,我猜想教授的代码也将关闭描述符。如果是这样,则不能使用fdopen。

一些想法:

  • 只需逐字节读取fd。表现不佳,但可以工作。
  • 是否可以假定您的代码一次只处理一个连接?如果是这样,只需在recv_line中有一个静态缓冲区即可缓冲fd数据。当缓冲区为空时将其填满,然后从中读取数据。
  • 如果需要处理多个连接,则可以扩展#2以使每个文件描述符具有一个缓冲区。您将需要某种 map 来处理此问题。

  • 根据评论更新

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

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

    09-06 22:59