【问题标题】:C++ recv() problemC++ recv() 问题
【发布时间】:2011-03-15 01:53:39
【问题描述】:

我想发送一个字符串“Hello there”,但我只收到“re”。这是为什么呢?

void Accept()
{
    SOCKADDR_IN sock;
    int intsock = sizeof(sock);
    remoteSocket = ::accept(desc, (LPSOCKADDR)&sock,  &intsock);
    if(remoteSocket == -1)
    {
        cout << "Error in Accept()" << endl;
    }
    HandleConnection();
}

void HandleConnection()
{
    cout << "You are connected !!!" << endl;
    char* temp = new char[20];
    Recv(temp);
    cout << temp << endl;
}

void Send(const char* buffer)
{
    if((::send(remoteSocket, buffer, strlen(buffer), 0)) < 0)
    {
        cout << "Error in Send()" << endl;
    }
}

void Recv(char* buffer)
{
    int n = 0;
    while((n = ::recv(remoteSocket, buffer, strlen(buffer), 0)) 0)
    {
        buffer[n] = 0;
    }
}

~Server()
{
    WSACleanup();
}

};

int main()
{
    Server s;
    s.Initialize();
    s.Socket();
    s.Bind();
    s.Listen();
    while(1)
    {
        s.Accept();
    }
    return 0;
}

【问题讨论】:

  • 您发布的代码无法编译。例如,您在类定义之外有一个析构函数 (~Server)。
  • strlen 如果您忘记转义末尾带有 \0 的字符串,则会进入无限循环。另外,您是否尝试过使用 select()?使用它,您可以等到收到一些数据。在 TCP 套接字上,recv() 将返回提供的缓冲区可以容纳的数量
  • 另一个问题(除了 Marlon 的问题):HandleConnection() 使用 new 而没有 delete。再说一次,你没有理由需要在这里new。您可以改用堆栈变量。
  • @Antwan:不完全是无限的——内存中通常有一个零字节阻止它进一步读取。但你是对的 - OP 在该上下文中使用 strlen() 调用定义不明确或未定义的行为。
  • @Antwan:不,recv 将阻塞,直到它接收到一些数据或套接字关闭。如果它收到的数据少于您要求的数据,它将返回一个短读取而不是阻塞,直到您获得所需的数据为止。

标签: c++ sockets


【解决方案1】:

尽管@Billy ONeal 指出了析构函数问题,但您在循环中执行recv(),但每次您都在覆盖接收到的缓冲区。我相信你想要这样的东西:

// Pass buffer and its real size. This function takes care of NULL termination.
size_t Recv(char* buffer, size_t size) {
    size_t total = 0, n = 0;
    while((n = ::recv(remoteSocket, buffer+total, size-total-1, 0)) > 0) {
        total += n;
    }
    buffer[total] = 0;
    return total;
}

int main() {
    char buffer[128];
    // Connect or whatever (and set your global remoteSocket)
    Recv(buffer, sizeof(buffer));
    cout << buffer << endl;
    return 0;
}

【讨论】:

  • 不应该是if (total &gt; 0) buffer[total - 1] = 0;吗?
  • 他在每次循环调用strlen(buffer)的同时覆盖接收到的缓冲区,所以如果消息越来越小,每次调用strlen(buffer)都会越来越小
  • @bstn:如果套接字是非阻塞,则该条件是有意义的。 recv 函数返回它已读取的字节数。如果它读取了 10 个字节,那么我们将 10 个字节存储到缓冲区(缓冲区 [0] 到缓冲区 [9])中,并将第 10 个字节设置为 0。调用者负责将 buffer size - 1 传递给此函数。
  • 抱歉误会,我只是从服务器类发布了部分代码,这就是为什么析构函数出现在那里,但我的问题是如果我使用 sizeof(buffer) 发送,它总是发送 4 个字节
  • @vBx 它发送 4 个字节的问题是因为您在 32 位机器上,并且指针是 32 位长(32 位 = 4 个字节)。您必须将 20 传递给您的 Recv 函数并在 recv 中使用它。
【解决方案2】:

我认为问题出在while((n = ::recv(remoteSocket, buffer, strlen(buffer), 0)) 0)这行代码

您正在使用strlen(buffer) 获取不正确的缓冲区大小,您应该将sizeof(buffer) 传递给您的Recv 函数。

如果这不是问题,那么它就是问题之一:P

编辑:

正如 Kitsune 和 Mark 所指出的,sizeof(buffer) 将返回 4 或 8 个字节,因为它是在堆上分配的,并且只是一个指向内存块的指针。如果您选择使用堆栈(char buffer[20] 而不是new char[20]),您可以将 sizeof(buffer) 传递给您的 Recv 函数。否则,只需使用硬编码的 20。

你的代码应该是这样的:

void HandleConnection()
{
    cout << "You are connected !!!" << endl;
    char temp[20]; // <-- now we have an array
    Recv(temp, sizeof(temp)); // <-- sizeof(temp) will give us 20, not 4 anymore
    cout << temp << endl;
}

Recv(char* buffer, size_t buffer_size)
{
    recv(remoteSocket, buffer, buffer_size, 0);
}

【讨论】:

  • sizeof(buffer) 为 4 或 8 (32/64b)。您将需要放置已知大小的缓冲区。传入一个大小,在本地分配一个数组(在这种情况下 sizeof 将是实际大小),或者使用其他类型(一个 vector 可以工作......)
  • 我认为该上下文中的 sizeof(buffer) 将只是 4(如果 64 位架构,则为 8)。缓冲区大小可能需要从调用方法传入。但是 +1 用于发现问题。
【解决方案3】:

您需要指定接收数据的缓冲区有多大——不是strlen(buffer)

如果缓冲区数组在本地定义为数组(不在参数列表中),或者缓冲区是其定义在函数中可见的全局或文件范围数组,则可以使用sizeof(buffer)。否则,您需要使用传递给Recv() 函数的额外缓冲区大小参数——也就是说,如果缓冲区是在另一个函数中定义的,或者如果它是动态分配的。 (在代码中,数组定义在Recv() 中不可见,因此您需要确保Recv() 以某种方式知道大小 - 作为显式的额外参数,或者因为您将缓冲区包装在一个适当的类中,其中包括一种方法,告诉您为它所拥有的缓冲区分配了多少空间。

当然,显示的代码无法编译,因为buffer 实际上并没有在任何地方定义或声明。

【讨论】:

  • 缓冲其函数参数
  • @Jonathan Leffler:如果我使用 sizeof(argument),我仍然得到 4
  • 这是因为缓冲区是作为指针传递的,并且您正在使用指针大小为 4 字节的 32 位机器。您必须将分配的缓冲区的大小作为单独的参数传递。经典(想想 C),你会使用 void Recv(char *buffer, size_t buflen); (虽然我建议让 Recv() 返回一个状态)。在调用中,额外的参数将是 20(因为您为数据分配了 20 个字节)。或者,您可以使用一个 C++ 类,该类知道为其分配了多少空间,并从中获取长度。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-04
  • 2011-07-09
  • 1970-01-01
  • 2021-10-29
  • 2014-06-29
  • 1970-01-01
相关资源
最近更新 更多