【问题标题】:fgets unpredicted behavior after reading '\n'读取 '\n' 后 fgets 出现意外行为
【发布时间】:2016-01-23 16:42:13
【问题描述】:

尝试基于套接字在 C 中编写 echo server\client 代码。 我无法确定 fgets() 是如何工作的。

如果读取了换行符,则将其存储到缓冲区中。终止空值 字节 ('\0') 存储在缓冲区中的最后一个字符之后。

 while (fgets(sendline, MAXLINE, stdin) != NULL) {
  sendline[strlen(sendline)-1] = '\0';
  send(sockfd, sendline, strlen(sendline), 0);}

但是我在服务器上得到了什么:

String received from and resent to the client:1234567890

String received from and resent to the client:abc
567890

如您所见,'\n' 字符添加到第二行,并尝试先用新行覆盖。但是在客户端,我看到缓冲区在使用 send() 时没有'\n'。

按 ctld+D (EOF) 按预期工作。

如何预防?并使用 Enter 键发送?

这张图片解释了我的意思。注释某些代码行后没有变化(对于@PCLuddite)

【问题讨论】:

  • 使用 '\0' 而不是 '0' — 数字 0 和空字节之间有很多区别。
  • 警告:如果输入超过 MAXLINE,则 fgets 将分割输入以适应 buff,然后是唯一最后一个以 '\n' 字符结尾的部分。
  • 现在我认为这不是 fgets(),而是 recv()。因为即使在 memset(&sendline, 0, strlen(sendline));
  • 因为您只发送 strlen(sendline) 空字符不包括在内。在客户端的接收中,您必须自己包含它,或发送 strlen(sendline)+1。
  • 部分问题可能出在接收代码中,发布有助于解决问题。

标签: c sockets newline system-calls fgets


【解决方案1】:

“0”和“\0”是有区别的。 '0' 表示将其作为整数 0。因此,您必须给出 '\0' 或者您必须给出不带单引号的 0。在 char 中,0(没有单引号)是 NULL。

所以,如果你想删除输入中的新行,你可以使用下面的例子。

例子:-

char Buf[BUFSIZ];
while(fgets(Buf,BUFSIZ,stdin)!=NULL) {
   if(Buf[strlen(Buf)-1]=='\n') { //Prevent from EOF
      Buf[strlen(Buf)-1]='\0'; // Remove newline from Buf
      send(sockfd, Buf, strlen(Buf), 0);
   }
}

【讨论】:

  • 这是在开玩笑吗?你的回答和我的问题有什么代码区别?
  • @kAldown 这不是您的原始代码。您的原始代码包含与您遇到的问题相关的错误。
  • 注意:Buf[strlen(Buf)-1]=='\n'Buf[0]== 0 的罕见情况下是未定义的行为。最好使用Buf[strcspn(Buf, "\n")] = '\0';
  • @PCLuddite 你确定吗?这不是解决方案。甚至关闭一个来解决问题,此外,这正是我在项目中的代码块,这没有任何意义,因为我谈论的是按 Enter 键,这会导致换行符,仍然可以通过替换最后一个字符来处理'\n' 为 0,还是我错了?
  • @kAldown 我确定这不是您的原始代码(这就是我回滚您的编辑的原因)。这可能不是一个完整的解决方案(我没有仔细研究过你的问题),但这绝对不是一个笑话。
【解决方案2】:

当然,接收端没有形成一个字符串,只是一个char 的数组,没有空字符。发送 +1 以包含空字符。 @milevyo

while (fgets(sendline, sizeof sendline, stdin) != NULL) {
  size_t length = strlen(sendline);
  if (length > 0 && sendline[length-1] == '\n') {
     sendline[--length] = '\0';
  }
  send(sockfd, sendline, length + 1, 0);  // + 1
}

【讨论】:

  • 或使用 sizeof 代替 strlen()
  • @kAldown 不,sizeof 将返回缓冲区的大小,而不是缓冲区中包含的字符串。
【解决方案3】:

好的,问题出在服务器接收块中。 我应该在 puts() 之后使用 memset(); 甚至不使用 puts(),会读取不同 sys.calls 之间的差异。

在 server.c 中:

while ( (n = recv(connfd, buf, MAXLINE,0)) > 0)  {
   printf("%s","String received from and resent to the client:");
   puts(buf);
   memset(&buf, 0, strlen(buf));
}

或者只在client.c中交换sizeof和strlen():

while (fgets(sendline, MAXLINE, stdin) != NULL) {
  if (sendline[(strlen(sendline)-1)] == '\n') {

      sendline[strlen(sendline)-1] = '\0';

      //send(sockfd, sendline, strlen(sendline)+1, 0);
      send(sockfd, sendline, sizeof sendline, 0);
  }

现在字符串不会覆盖自己。

【讨论】:

  • 几个问题。 #1 “我应该在 puts() 之后使用 memset();”是不够的。在调用puts() 之前 需要在connfd 的末尾附加空字符。 memset() 为时已晚。在puts() 之前简单使用buf[n] = 0;。无需致电memset()
  • #2 更微妙。如果用户发送一个空行,即 0 char,那么该例程将不会执行打印 ’\n’puts("")。对此和#1 的简单解决方案是发送’\0’ 作为回答elsewhere
  • #3 次要:在memset(&buf, 0, ... 中不需要&
  • memset() 不是正确的解决方案,而是解决问题的解决方法。如果遵循@milevyo 的建议(在问题的 cmets 中),则根本不需要 memset()。
  • 注意:sendline[(strlen(sendline)-1)] 可以被黑客利用。可以从键盘输入空字符。条目可以是'\0''a''b''c''\n',然后fgets() 附加尾随'\0'。现在 strlen(sendline) 返回 0 并且 sendline[(strlen(sendline)-1)] 正在访问 sendline 范围之外导致未定义的行为。
猜你喜欢
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 2020-05-16
相关资源
最近更新 更多