【问题标题】:Write to file descriptor and immediately read from it写入文件描述符并立即从中读取
【发布时间】:2020-04-18 05:20:21
【问题描述】:

今天我遇到了一些看起来很奇怪的代码,乍一看我并不清楚它的作用。

  send(file_desc,"Input \'y\' to continue.\t",0x18,0);
  read(file_desc,buffer,100);
  iVar1 = strcmp("y",(char *)buffer);
  if (iVar1 == 0) {
    // some more code
  }

似乎正在将文本字符串写入文件描述符。紧接着,它从该文件描述符读取到缓冲区。它会比较写入缓冲区的文本是否为"y"

我的理解(如果我错了请纠正我)是它会将一些作为文本字符串的数据写入文件描述符,然后文件描述符充当您写入的任何内容的临时存储位置。之后,它将文件描述符中的数据读取到缓冲区中。它实际上是相同的文件描述符。这似乎是使用文件描述符将数据从文本字符串复制到缓冲区的原始方式。为什么不直接使用strcpy()

写入文件描述符然后立即从中读取的用例是什么?使用文件描述符复制数据似乎是一种复杂的方式。或者我对这段代码理解得不够好,send()read() 的序列是做什么的?

假设这段代码是使用文件描述符将文本字符串"Input \'y\' to continue.\t" 复制到缓冲区中,他们为什么要将它与字符串"y" 进行比较?每次都应该是假的。

我假设写入文件描述符的任何数据都保留在该文件描述符中,直到它被读取。所以这里似乎send() 用于将字符串写入,read() 用于将其读回。

man send 中写道:

 The only difference between send() and write(2) is the presence of flags.  With a zero
       flags argument, send() is equivalent to write(2).

他们为什么要使用send() 而不是write()?这段代码真是令人难以置信。


编辑:这是这段代码的完整功能:

void send_read(int file_desc)

{
  int are_equal;
  undefined2 buffer [8];
  char local_28 [32];

                    /* 0x6e == 110 == 'n' */
  buffer[0] = 0x6e;
  send(file_desc,"Input \'y\' to continue.\t",0x18,0);
  read(file_desc,buffer,100);
  are_equal = strcmp("y",(char *)buffer);
  if (are_equal == 0) {
    FUN_00400a86(file_desc,local_28);
  }
  else {
    close(file_desc);
  }
  return;
}

【问题讨论】:

  • 您需要包含更多代码。“文件描述符”并不总是意味着磁盘上的实际文件。它更可能是一个套接字、管道或其他一些伪文件。如果代码实际上是磁盘上的文件,则代码将没有意义。
  • @JonathanLeffler 这让我明白了一点。那么它是否使用相同的文件描述符来将消息写入另一个服务器,并且还使用它来检索服务器的响应?这将解释发送的文本字符串和以下strcmp()。我应该提一下,这段代码确实与 somewhere 的服务器通信。

标签: c buffer system-calls file-descriptor


【解决方案1】:

send()recv() 函数用于套接字(send:在套接字上发送消息 - recv:从连接的套接字接收消息)。另请参阅一般Sockets 的 POSIX 描述。

套接字文件描述符是双向的——您可以在它们上读写。与管道文件描述符不同,您无法阅读您编写的内容。使用管道,写入管道写入端的进程可以从管道的读取端读取它写入的内容——如果另一个进程没有先读取它的话。当一个进程在套接字上写入时,该信息会传递给对等进程,并且无法被写入者读取。

【讨论】:

  • 你认为这段代码send()s 文本字符串到服务器,然后read()s 服务器响应?
  • 我的印象是,在与服务器通信时,您通常需要有两个单独的套接字:一个用于发送信息,另一个单独的套接字用于读取服务器发送给您的数据。
  • 这基本上就是这个想法——不确定这是客户端,但很可能是。它发送给它的对等体,然后接收响应。
  • @Galaxy:我不确定您从哪里得到的印象是您需要两个套接字来在客户端和服务器之间进行通信。匿名管道确实如此,但套接字却不是——无论是 Unix 域还是网络连接的套接字。
  • 是的,我可能在考虑管道,因为除了常规文件之外,管道是我学习的第一种文件描述符。
【解决方案2】:

send(2) 是一个只能与套接字一起使用的系统调用。套接字是一个描述符,允许您使用它来发送数据或从远程点(远程套接字)接收数据,该点可以在不同的计算机上或与您相同的计算机上。但它就像电话线一样,您发送的内容由您的伴侣接收,他/她发送的内容由您接收。 read(2) 系统调用可以被套接字使用,而send(2) 不能被文件使用,因此您的示例代码将与文件相关的调用与与套接字相关的调用混合在一起(这并不罕见,read(2)write(2) 可以都与套接字一起使用)

您在上面发布的代码是错误的,因为它盲目地将接收到的缓冲区与strcmp 函数进行比较,假设它接收到一个以空值结尾的字符串。可以是这样,但也不能。

即使发送者(在连接的另一端)同意发送完整的消息,nul 终止的字符串。接收方首先要获取接收到的数据量(这是read(2)调用的返回值,可以是:

  • -! 表示接收错误。连接可以由对方重置,或者对方可以在您发送数据时重新启动。
  • 0 表示没有更多数据或数据结束(对方关闭了连接)如果对方超时并且您响应时间过长,可能会发生这种情况。它关闭连接而不发送任何内容。你什么也得不到。
  • n 一些数据,小于缓冲区大小,但包括对等方发送的完整数据包(以及与它一起发送的约定 nul 字节)。这是您可以安全地strcmp 数据的唯一情况。
  • n 一些数据,小于缓冲区大小,小于传输的数据。这可能是由于多个数据包中数据的某些数据碎片造成的。然后你必须再做一次read,直到你的对等方发送了所有数据。例如,数据包碎片在 TCP 中是很自然的事情。
  • n 一些数据,小于缓冲区大小,并且大于传输的数据。发送者在您收到的那个之后又进行了一次传输,并且两个数据包都进入了内核缓冲区。您必须调查这种情况,因为您有一个完整的数据包,并且必须将接收到的其余数据保存在缓冲区中,以供以后处理,否则您将丢失收到的数据。
  • n 一些数据,缓冲区已满,没有空间来存储完整传输的数据。你已经填满了缓冲区并且没有\0 char 来了......数据包比缓冲区大,你用完了缓冲区空间并且必须决定做什么(分配其他缓冲区来接收其余的,丢弃数据,或者无论您决定做什么)这不会发生在您身上,因为您期望一个包含 1 或 2 个字符的数据包,并且您有 100 个缓冲区,但谁知道...

至少,作为最低限度的安全网,您可以这样做:

  send(file_desc,"Input \'y\' to continue.\t",0x18,0);
  int n = read(file_desc,buffer,sizeof buffer - 1);  /* one cell reserved for '\0' */
  switch (n) {
  case -1: /* error */
      do_error();
      break;
  case 0: /* disconnect */
      do_disconnect();
      break;
  default: /* some data */
      buffer[n] = '\0';  /* append the null */
      break;
  }
  if (n > 0) {
      iVar1 = strcmp("y",(char *)buffer);
      if (iVar1 == 0) {
        // some more code
      }
  }

注意:

由于您没有发布完整且可验证的示例,因此我无法发布完整且可验证的回复。

对此我深表歉意。

【讨论】:

  • 好点。谢谢亲爱的先生。我应该提到,这段代码是某种内部协议实现,这意味着同一个人编写了应用程序的客户端和服务器,因此他们就发送和接收数据的格式达成了一致。
  • 哈哈哈……等我完成回答后再点赞吧:) 感谢您的评论。
  • send(X, Y, Z, 0) 完全等同于 write(X, Y, Z)recv(X, Y, Z, 0) 完全等同于 read(X, Y, Z)。在 Linux 上,它们调用内核中完全相同的代码。
  • @BrianSilverman,如果您对非套接字执行 send() 调用,您将收到错误,而 write() 系统调用将正确执行。所以这两个调用并不完全相等,尽管正确传递的参数都执行相同的代码。我刚刚用简单的write(1, "Hello, world\n", 13);send(1, "Hello, world\n", 13, 0); 进行了测试。
  • @BrianSilverman,如果程序是在标准输出中使用套接字执行的,那么两个调用都可以正常执行,并通过执行相同的内核代码通过套接字发送消息,但这并不意味着它们完全等价。
猜你喜欢
  • 1970-01-01
  • 2011-08-16
  • 1970-01-01
  • 2019-04-13
  • 1970-01-01
  • 1970-01-01
  • 2023-03-17
  • 2019-06-20
  • 1970-01-01
相关资源
最近更新 更多