我做了自己的Socket类,能够发送和接收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()接收完整数据包?
我不能使用字符串,但必须使用字符。
感谢您的帮助。
答案 0 :(得分:7)
您没有初始化temp
,最重要的是,您对realloc
的呼叫是错误的。它应该是:
temp = realloc (temp, recvsize+1);
当您按原样拨打realloc
时,会丢弃新地址,现在旧地址已被释放的可能性很大。当你试图取消引用时,所有的赌注都会被取消。
realloc
返回新地址的原因是因为如果当前块被包含在内存区域中,则缓冲区的扩展可能需要移动缓冲区(换句话说,它不能只扩展为空闲块跟着它)。在这种情况下,将在场地中创建一个新块,从旧块传输的内容和旧块被释放。如果发生这种情况,您必须从realloc
获取返回值。
请记住,realloc
没有有来返回一个新指针,例如,如果块之后有足够的可用空间,它可能会给你相同的指针满足新尺寸或缩小尺寸。
如果它不能扩展块,它也可以返回NULL,你应该注意这一点,特别是因为:
temp = realloc (temp, newsize);
返回NULL时会导致内存泄漏(它不会释放旧块)。
其他一些事情:
calloc
,尤其是在这种情况下,因为您无论如何都在复制内存。memset
,则不需要将memcpy
内存块设为0。temp
初始化为NULL
,则可以在不对其进行测试的情况下使用realloc
。这是因为realloc(NULL,7)
与malloc(7)
相同 - realloc
完全能够以空指针开头。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);
}
答案 1 :(得分:0)
我最近遇到了这个问题。
Realloc很慢。 Recv很快。几百次reallocs第二次WILL崩溃。
calloc()不仅仅是recvsize + 1,还有一个几千字节的缓冲区。 realloc()仅在缓冲区被填充/溢出时,并在每个realloc()上额外提供几千字节。
下面是我用来将数据附加到输出流的一段代码,但输入应该非常相似。 (注意,buf_out_size是分配缓冲区的大小,buf_out_len是当前缓冲区中的数据量。)
void Netconsole::ParseOutput(int sock, std::string raw)
{
//if too small, realloc with raw.size() + BUFFSIZE.
if (s[sock]->buf_out_len + raw.size() > s[sock]->buf_out_size)
{
s[sock]->buf_out_size += raw.size() + BUFFSIZE;
s[sock]->buf_out = (char*) realloc( s[sock]->buf_out, s[sock]->buf_out_size);
}
// append new data to the end of the buffer.
if(s[sock]->buf_out != NULL)
{
memcpy(s[sock]->buf_out + s[sock]->buf_out_len, raw.c_str(), raw.size());
s[sock]->buf_out_len += raw.size();
}
else
{
s[sock]->ending = true;
#if DEBUG_PRINT_TCP
printf("%s TCP[%d] dies from out of memory, realloc error\r\n",Debug::MTimestamp(),sock);
#endif
}
}