【问题标题】:how to receive the large data using recv()?如何使用recv() 接收大数据?
【发布时间】:2012-04-04 12:28:38
【问题描述】:

我使用 C++ 开发了客户端服务器程序,所以我想接收超过 500kb ,我的客户端消息以“!”终止,所以我想接收直到我的最后一个字节(!)接收, 这是我的代码,它不起作用。它有什么问题。

do
  {
  int num = recv(*csock,  buf, bytesLeft,0);

if (num == 0)
{
  break;
}
else if (num < 0 && errno != EINTR)
{
  fprintf(stderr, "Exit %d\n", __LINE__);
  exit(1);
}
else if (num > 0)
{
  numRd += num;
  buf += num;
  bytesLeft -= num;
  fprintf(stderr, "read %d bytes - remaining = %d\n", num, bytesLeft);
}

  }

    while (bytesLeft != 0);
   fprintf(stderr, "read total of %d bytes\n", numRd);

【问题讨论】:

  • 1) 不要在非 NUL 终止的缓冲区上使用 strcat() strlen()。 2) 不要混合 C 和 C++ 3) 诊断输出应该到 stderr,而不是 stdout,添加 \n 有时会有所帮助。 4) 谢谢。
  • 您的套接字是阻塞还是非阻塞?默认情况下,所有套接字都是阻塞的,这意味着如果没有接收到数据,那么对recv 的调用将在等待更多数据到达时阻塞
  • 经验法则:阅读您使用的所有命令的手册。
  • 那么问题是什么?你有什么问题?你得到什么输出?请编辑您的问题并提供更多详细信息。

标签: c++ sockets tcp client-server tcpsocket


【解决方案1】:

虽然由于您问题的措辞,我不确定您的问题到底是什么,但您通常不能使用 strcat 附加通过网络接收的原始缓冲区,除非您明确知道它们将以 NULL 结尾,即使这样,如果您收到意外的数据传输,这也不是真正的“安全”。 c-strings 的假设是它们是 NULL 终止的,但原始网络缓冲区可能不是,如果输入缓冲区不是 NULL 终止的,使用strcat 将导致您过度运行输入缓冲区。代替strcat,使用大小为N 字节的已知固定大小缓冲区来接收数据,并在缓冲区中增加一个临时指针,直到到达缓冲区末尾或数据包传输结束。这样,您将始终从网络读取最多 N 个字节,并且不会发生缓冲区溢出的情况。

例如,您可以执行以下操作(由于所有复制,这不是最快或更有效的解决方案,但它有效):

unsigned char buf[10000];  //10Kb fixed-size buffer
unsigned char buffer[MAXRECV];  //temporary buffer

unsigned char* temp_buf = buf;
unsigned char* end_buf = buf + sizeof(buf);

do
{       
    iByteCount = recv(GetSocketId(), buffer,MAXRECV,0);   

    if ( iByteCount > 0 )
    {
        //make sure we're not about to go over the end of the buffer
        if (!((temp_buf + iByteCount) <= end_buf))
            break;

        fprintf(stderr, "Bytes received: %d\n",iByteCount);
        memcpy(temp_buf, buffer, iByteCount);
        temp_buf += iByteCount;
    }
    else if ( iByteCount == 0 )
    {
        if(temp_buf != buf)
        {
            //do process with received data
        }
        else
        {
            fprintf(stderr, "receive failed");
            break;
        }
    }
    else
    {
        fprintf(stderr, "recv failed: ");
        break;
    }
} while(iByteCount > 0 && temp_ptr < end_buf);  //check for end of buffer

【讨论】:

  • 如果我发送了 10 kb 数据意味着,但我无法一次接收到全部数据,因为 10kb 数据分为 6 个数据包,所以我想调用 recv() 6 次,那么只有我会得到整个数据,我用的是 strcat
  • @Mr.Cool - 是的,我们知道您为什么要连接缓冲区数据,但 Jason 等人说,(正确) strcat() 无法完成这项工作!
  • @Martin James - 那么我怎样才能获得全部数据?
  • @Jason 实际上我的数据大小高达 10 kb 我的消息格式是“EXT#my data#0!”这里“!”我的消息是否已终止,我想收到“!”但是当我发送消息服务器无法接收全部数据时,可能是我的数据大小或我的代码错误
  • buf、buffer等有哪些类型?空间分配在哪里?我能看到的唯一确定的事情是,如果您需要连续字节中的所有消息,那么您最终会复制它,也许是使用 memcpy() - 一些不关心空字节的东西。
【解决方案2】:

您是否需要一个连续字节缓冲区中的所有 1MB+ 数据?如果是这样,并且您坚持使用终止“!”的协议并且没有包含长度的标头,那么您会经常使用 memcpy() 和 realloc() 或其他一些缓冲区类型,例如 std::vector ,它们实际上只是做同样的事情。

如果您不需要一个字符串中的所有这些字节,您可以以其他方式存储它们,例如。 *buffer 的向量,因此避免复制。

【讨论】:

    【解决方案3】:

    假设您使用的是阻塞套接字(这是套接字的默认模式),那么recv() 将阻塞等待完整的MAXRECV 字节数到达。如果客户端发送的字节数少于该字节数,recv() 将阻塞等待未到达的数据。

    要解决这个问题,您需要:

    1) 使用 1 字节缓冲区调用 recv(),调用 recv() 直到遇到 ! 字节。

    2) 在调用recv() 之前调用select() 以检测套接字何时真正有数据要读取,然后调用ioctlsocket(FIONREAD) 以确定使用recv() 实际可以读取多少字节而不阻塞,然后有@987654331 @读取那个字节数。

    【讨论】:

      猜你喜欢
      • 2016-07-23
      • 2012-04-23
      • 2014-01-14
      • 2015-12-27
      • 2016-08-10
      • 2018-04-21
      • 1970-01-01
      • 2020-05-07
      • 2013-04-03
      相关资源
      最近更新 更多