【问题标题】:Sending char buffer through TCP socket incomplete通过 TCP 套接字发送字符缓冲区不完整
【发布时间】:2014-08-19 15:33:58
【问题描述】:

我只是在学习如何在 C 中处理套接字和 TCP 连接。我有一个应用程序(一个很长的应用程序),它基本上通过系统调用 write 从服务器到客户端发送和接收 char 数组,反之亦然(两个当然是单独的 C 应用程序)。只要我将它与本地连接一起使用,在同一台 PC 上,在终端上运行服务器,在另一台上运行客户端,一切正常,数据到达目的地。但是,如果我在一台计算机上的服务器和另一台计算机上的客户端但在同一条互联网线路上尝试它,则在连接建立后,我收到一个错误,告诉我预期的字节数(我在发送真正的 char[] 之前传递的)没有到达。如果我在我的 PC 上尝试服务器,而客户端在另一台计算机上使用不同的线路在不同的提供商上尝试,同样的事情。

我缺少一些东西,按顺序发送一堆字节有什么限制吗?

弹出错误的代码。

服务器端:

r=htonl(lghstr);
w=write(myFd,&r,sizeof(int));//writes the number of incoming bytes
if(w<0) perror("writeServer4"),exit(-1);
w=write(myFd,tmp->string,lghstr);
if(w<0) perror("writeServer5"),exit(-1);
if(w!=lghstr) perror("ERROR");

客户端

rC=read(fdc,&cod,sizeof(int));//read incoming number of bytes
lghstr=ntohl(cod);
if(rC<0) perror("readClient3"),exit(-1);
rC=read(fdc,dest,lghstr);
if(rC<0) perror("readClient4"),exit(-1);
if(rC!=lghstr) perror("error : "), printf("didn't read the right number of bytes"),exit(-1);

现在这基本上是重复很多次,甚至说300次,而且数字很大,程序不起作用。

【问题讨论】:

  • 这似乎更适合Stack Overflow
  • 对于他们发给我的类似问题。顺便说一句,即使我发布了一些 C 代码,但问题在某种程度上与网络有关

标签: unix tcp sockets c


【解决方案1】:

你在一侧而不是另一侧做ntohl()。那可能是用错误的值解释字节。

您应该 printf() 两边的字节,看看 int 被评估为什么。

编辑:我确信这是一个编程错误。

如果我不得不猜测,我会说由于某种原因你与对方不同步。你说这运行了“大约 300 次”。

尝试在协议中添加一个魔术整数。

以下是按此顺序发送的客户端示例。

  1. 一个始终不变的神奇整数。
  2. 即将发送的字节长度。
  3. 要发送的字节数。

这使用了分散收集机制(它更适合序列化),但除了它有效地做你正在做的事情之外,作为一个客户端,它只是添加一个神奇的值。

当接收方接收到数据时,它可以通过检查传入的幻数来验证数据的顺序是否正确。如果幻数错误,则意味着客户端或服务器在位置上迷失了方向流。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <err.h>

#include <time.h>

#define MAGIC 0xDEADBEEFLU
#define GARBAGE_MAX 65536

const int iterations = 3000;

char * create_garbage_buf(
    void)
{
  int rc = -1;
  int fd = -1;
  char *buf = NULL;

  buf = malloc(GARBAGE_MAX);
  if (!buf)
    err(1, "Cannot allocate buf");

  fd = open("/dev/urandom", O_RDONLY);
  if (fd < 0)
    err(1, "Cannot open urandom");

  rc = read(fd, buf, GARBAGE_MAX);
  if (rc < 0)
    err(1, "Cannot read from urandom");
  else if (rc != GARBAGE_MAX)
    errx(1, "Expected %d bytes, but got %d reading from urandom",
         GARBAGE_MAX, rc);

  close(fd);
  return buf;
}

int main() {
  int fd, offset, i, rc;
  uint32_t magic = MAGIC;
  uint32_t blen = 0;
  char *buf = NULL;
  struct iovec vecs[3];

  /* Seed poor random number generator */
  srand(time(NULL));

  /* Use a file for demonstration, but a socket will do just fine */
  fd = open("/dev/null", O_WRONLY);

  /* Create some garbage to send */
  buf = create_garbage_buf();

  if (fd < 0)
    err(1, "Cannot open file");

  /* The first vector, is always the magic */
  vecs[0].iov_len = sizeof(uint32_t);
  vecs[0].iov_base = &magic;
  for (i=0; i < iterations; i++) {
    /* The second vector represents lengh of what we send
     * in this demonstration it is a number between 0 and
     * GARBAGE_MAX/2.
     */
     blen = rand() % (GARBAGE_MAX / 2);
     vecs[1].iov_len = sizeof(uint32_t);
     vecs[1].iov_base = &blen;
     /* The last record is the data to send. Its another random
      * number between 0 and GARBAGE_MAX which represents the offset
      * in our garbage data to send */
     offset = rand() % (GARBAGE_MAX / 2);
     vecs[2].iov_len = blen;
     vecs[2].iov_base = &buf[offset];

     rc = writev(fd, vecs, 3);
     if (rc < 0)
       err(1, "Could not write data");
     if (rc != (sizeof(uint32_t)*2 + blen))
       errx(1, "Did not write proper number of bytes to handle");

     printf("Wrote %u bytes from offset %u in garbage\n", blen, offset);
  }

  free(buf);
  printf("Done!\n");
  return 0;
}

【讨论】:

  • 对不起,我忘了一句,但不是这个
  • 我用不同的建议编辑了帖子,但我不相信这与服务器的东西有关。
  • 您在一侧而不是另一侧执行 ntohl()。" OP 是否在中间时间更改了代码?形成我目前看到的是 OP 的 确实在服务器端调用htonl()()
  • @alk,关注流位置的编辑是因为他说他忘记了一行,因为它不是因为那个。
  • 如果通过 TCP,OP 使用的应用程序级别协议(发送固定长度然后发送与之前发送的大小完全相同的数据块)是完全有效的。 TCP 负责所有正在传输的数据。 OP 代码中的错误在于 read()write() 的使用,分别是这些函数的返回值如何被解释(或不被解释)。
【解决方案2】:

仔细阅读read()/write() 的文档,并了解这两个函数不一定有read()/write() 的字节数,但很少。因此,循环计算此类调用,直到所有预期的数据都已被读取/写入是一个好主意,而不是说必要的必要性。

例如,您可能想看看这个答案:https://stackoverflow.com/a/24260280/694576 和阅读这个答案:https://stackoverflow.com/a/20149925/694576

【讨论】:

    【解决方案3】:

    这就是问题所在:

    rC=read(fdc,dest,lghstr);
    ...
    if(rC!=lghstr) perror("error : ")
    

    套接字编程的 #1 谬误认为 recv()read() 将返回与对方进行的写入/发送调用完全相同的字节数。

    实际上,部分数据极有可能和预期。简单的解决方法是循环读取/接收,直到获得预期的确切字节数:

    size_t count = 0;
    while (count < lghstr)
    {
        ssize_t readresult = read(fdc, dest+count, lghstr-count);
        if (readresult == -1)
        {
          // socket error - handle appropriately (typically, just close the connection)
        }
        else if (readresult == 0)
        {
          // The other side closed the connection - handle appropriately (close the connection)
        }
       else
        {
            count += readresult;
        }
    }
    

    循环的另一种替代方法是将 MSG_WAITALL 标志与套接字一起使用。这意味着,使用 recv() 而不是 read()。您仍然需要处理错误情况。

    rc = recv(fdc, dest, lghstr, MSG_WAITALL);
    if (rc == -1)
    {
      // socket error
    }
    else if (rc == 0)
    {
      // socket closed by remote
    }
    else if (rc < lghstr)
    {
       // the other side likely closed the connection and this is residual data (next recv will return 0)
    }
    

    【讨论】:

      猜你喜欢
      • 2014-05-10
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-29
      • 2018-11-08
      • 1970-01-01
      相关资源
      最近更新 更多