C++ C++;-TCP套接字类:接收问题

C++ C++;-TCP套接字类:接收问题,c++,c,networking,sockets,winsock,C++,C,Networking,Sockets,Winsock,我做了自己的套接字类,以便能够发送和接收HTTP请求。 但我还是有一些问题。下面的代码(我的接收函数)仍然有缺陷,有时会崩溃。 我试着调试它,但它一定在指针算术/内存管理的某个地方 int Socket::Recv(char *&vpszRecvd) { //vpszRecvd = NULL; int recvsize = 0; char TempBuf[1024]; int Result = 0; char* temp; do { memset(TempBuf

我做了自己的套接字类,以便能够发送和接收HTTP请求。 但我还是有一些问题。下面的代码(我的接收函数)仍然有缺陷,有时会崩溃。 我试着调试它,但它一定在指针算术/内存管理的某个地方

int Socket::Recv(char *&vpszRecvd)
{
 //vpszRecvd = NULL;
 int  recvsize = 0;
 char TempBuf[1024];
 int  Result = 0;
 char* temp;


 do
 {
  memset(TempBuf, 0, sizeof(TempBuf));

  Result = recv( this->sSocket, TempBuf, sizeof(TempBuf) -1, 0 );
  if (recvsize == 0)
   recvsize = Result;

  if ( Result > 0 )
  {
   if ( vpszRecvd != NULL )
   {
    if (temp == NULL)
    {
     temp = (char*)calloc(recvsize + 1, sizeof(char));
    }
    else
    {
     realloc(temp, recvsize + 1);
    }
    if (temp == NULL)
     return 0;

    memcpy(temp, vpszRecvd, recvsize);
    realloc(vpszRecvd, recvsize + Result);

    if (vpszRecvd == NULL)
     return 0;

    memset(vpszRecvd, 0, recvsize + Result);
    memcpy(vpszRecvd, TempBuf, Result);
    memcpy(vpszRecvd + recvsize, TempBuf, Result);
    recvsize += Result; 
   }
   else
   {
    realloc(vpszRecvd, Result);

    if (vpszRecvd == NULL)
     return 0;

    memset(vpszRecvd, 0, Result);
    memcpy(vpszRecvd, TempBuf, Result);
    recvsize += Result;
   }
  }
  else if (  Result == 0 )
  {
   return recvsize;

  }
  else //if (  Result == SOCKET_ERROR )
  {
   closesocket(this->sSocket);
   this->sSocket = INVALID_SOCKET;
   return SOCKET_ERROR;
  }
 }
 while( Result > 0 );

 return recvsize;
}
是否有人看到任何可能导致崩溃的因素,或者是否有人有更好/更快/更小和更稳定的示例如何通过recv()接收完整数据包

我不能使用字符串,但必须使用字符


谢谢你的帮助。

我最近遇到了这个问题

Realloc很慢。Recv速度很快。每秒几百个realloc将崩溃

calloc()不仅仅是recvsize+1,还有一个额外的几千字节的缓冲区。只有当缓冲区被填满/溢出时,才使用realloc(),并在每个realloc()上再多给它几KB

下面是我用来将数据附加到输出流中的一段代码,但输入应该非常相似。(请注意,buf_out_size是已分配缓冲区的大小,buf_out_len是缓冲区中当前的数据量。)


您没有初始化
temp
,除此之外,您对
realloc
的调用是错误的。应该是:

temp = realloc (temp, recvsize+1);
当您按原样调用
realloc
时,您会丢弃新地址,很有可能旧地址现在已被释放。当你尝试去引用它时,所有的赌注都被取消了

realloc
返回新地址的原因是,如果当前块在内存区域中被包围,则扩展缓冲区可能需要移动缓冲区(换句话说,它不能扩展为紧跟其后的空闲块)。在这种情况下,将在竞技场中创建一个新块,从旧块传输的内容将被释放。如果发生这种情况,您必须从
realloc
获取返回值

请记住,
realloc
不必返回新指针,例如,如果块后有足够的可用空间来满足新的大小,或者如果您正在减小大小,它可能会给您相同的指针

如果无法扩展块,它也可能返回NULL,您也应该注意这一点,特别是因为:

temp = realloc (temp, newsize);
当它返回NULL时将导致内存泄漏(它不会释放旧块)

还有几件事:

  • 您很少需要使用
    calloc
    ,尤其是在这种情况下,因为您仍然在内存上进行复制
  • 类似地,如果要立即在内存块上执行
    memcpy
    ,则不需要
    memset
    将内存块设置为0
  • 如果您将
    temp
    初始化为
    NULL
    ,则只需使用
    realloc
    ,而无需对其进行测试。这是因为
    realloc(NULL,7)
    malloc(7)
    -
    realloc
    完全能够以NULL指针开始
  • 由于您不需要
    calloc
    ,因此这仅用于教育-
    sizeof(char)
    根据定义始终为1
  • 您似乎正在进行大量不必要的数据复制

我们为什么不从简单一点的开始呢?现在,这完全是我的想法,所以可能会有一些错误,但它至少是从问题中移动内存的庞然大物中删除的:-),所以应该更容易调试

基本上分为:

  • 初始化空消息
  • 进入无限循环。
    • 获取片段
    • 如果发生错误,请释放所有内容并返回错误
    • 如果没有更多的段,则返回当前消息
    • 在消息末尾为新段创建空间
    • 如果无法创建空间,请释放所有内容并返回空消息
    • 将段附加到消息并调整消息大小
代码如下所示:

int Socket::Recv(char *&vpszRecvd) {
    int  recvsize = 0;
    char TempBuf[1024];
    int  Result = 0;
    char *oldPtr;

    // Optional free current and initialise to empty.

    //if (vpszRecvd != NULL) free (vpszRecvd);
    vpszRecvd = NULL;

    // Loop forever (return inside loop on end or error).

    do {
        Result = recv( this->sSocket, TempBuf, sizeof(TempBuf) -1, 0 );

        // Free memory, close socket on error.

        if (Result < 0) {
            free (vpszRecvd);
            closesocket(this->sSocket);
            this->sSocket = INVALID_SOCKET;
            return SOCKET_ERROR;
        }

        // Just return data and length on end.

        if (Result == 0) {
            return recvsize;
        }

        // Have new data, use realloc to expand, even for initial malloc.

        oldPtr = vpszRecvd;
        vpszRecvd = realloc (vpszRecvd, recvsize + Result);

        // Check for out-of-memory, free memory and return 0 bytes.

        if (vpszRecvd == NULL) {
            free (oldPtr);
            return 0;
        }

        // Append it now that it's big enough and adjust the size.

        memcpy (&(vpszRecvd[recvsize], TempBuf, Result);
        recvsize += Result;
    } while (1);
}
intsocket::Recv(char*&vpszRecvd){
int recvsize=0;
char TempBuf[1024];
int结果=0;
char*oldPtr;
//可选自由电流和初始化为空。
//如果(vpszRecvd!=NULL)空闲(vpszRecvd);
vpszRecvd=NULL;
//永远循环(结束时返回内部循环或错误)。
做{
结果=recv(此->sSocket,TempBuf,sizeof(TempBuf)-1,0;
//释放内存,错误时关闭套接字。
如果(结果<0){
免费(vpszRecvd);
闭合插座(此->闭合插座);
此->sSocket=无效的\u套接字;
返回套接字错误;
}
//只需在末尾返回数据和长度。
如果(结果==0){
返回recvsize;
}
//拥有新数据,使用realloc进行扩展,即使是初始malloc。
oldPtr=vpszRecvd;
vpszRecvd=realloc(vpszRecvd,recvsize+结果);
//检查内存是否不足,释放内存并返回0字节。
如果(vpszRecvd==NULL){
免费(oldPtr);
返回0;
}
//现在它已经足够大了,可以附加它并调整大小。
memcpy(&(vpszRecvd[recvsize],TempBuf,Result);
recvsize+=结果;
}而(1),;
}

听起来不错。有没有示例代码?我想我的代码有很多漏洞,有几个东西会崩溃。我甚至找不到一个接收代码,基于字符,可以接收整个数据包。为什么要让几百个realloc再次崩溃?呃,我不这么认为,@SF.TCP实际上会等你,如果你不够快的话。Recv也一样快不管怎样,我添加了一些额外的字节,它不再崩溃,而是返回空字符数组或纯b_llshit…@maxedmelon,它返回的是牛粪(正如你雄辩地说的那样),因为realloc-见我的答案。memcpy(vpszRecvd,TempBuf,Result);我现在就编辑它。它有一个
C++
标记。因此,您可以使用
std::vector
作为缓冲区来摆脱所有内存问题。使用其
resize()
成员函数来设置其大小并使用

int Socket::Recv(char *&vpszRecvd) {
    int  recvsize = 0;
    char TempBuf[1024];
    int  Result = 0;
    char *oldPtr;

    // Optional free current and initialise to empty.

    //if (vpszRecvd != NULL) free (vpszRecvd);
    vpszRecvd = NULL;

    // Loop forever (return inside loop on end or error).

    do {
        Result = recv( this->sSocket, TempBuf, sizeof(TempBuf) -1, 0 );

        // Free memory, close socket on error.

        if (Result < 0) {
            free (vpszRecvd);
            closesocket(this->sSocket);
            this->sSocket = INVALID_SOCKET;
            return SOCKET_ERROR;
        }

        // Just return data and length on end.

        if (Result == 0) {
            return recvsize;
        }

        // Have new data, use realloc to expand, even for initial malloc.

        oldPtr = vpszRecvd;
        vpszRecvd = realloc (vpszRecvd, recvsize + Result);

        // Check for out-of-memory, free memory and return 0 bytes.

        if (vpszRecvd == NULL) {
            free (oldPtr);
            return 0;
        }

        // Append it now that it's big enough and adjust the size.

        memcpy (&(vpszRecvd[recvsize], TempBuf, Result);
        recvsize += Result;
    } while (1);
}