首页 文章

TCP中的垃圾值和缓冲区差异

提问于
浏览
2

First question :我对TCP中的缓冲区感到困惑 . 我试图解释我的问题,我读了这篇文章TCP Buffer,作者说了很多关于TCP缓冲区,这很好,对初学者来说是一个非常好的解释 . 我需要知道的是,这个TCP缓冲区与我们在基本客户端服务器程序( Char *buffer[Some_Size] )中使用的缓冲区是相同的缓冲区,还是TCP内部保留的一些不同的缓冲区?

我的 second question 是我从客户端通过套接字向服务器发送前缀长度( This is data From me )的字符串数据,当我在控制台上打印我的数据时,我的字符串打印出一些垃圾值也像这样 "This is data From me zzzzzz 1/2 1/2....." ?.但是我通过右移 char *recvbuf = new char[nlength>>3]; nlength to 3 bits 修复了它,但为什么我需要这样做呢?

我的 third question 与第一个问题有关,如果没有像TCP缓冲区那样,它只涉及 Char *buffer[some_size] 那么我的程序会注意到使用这样的静态内存分配缓冲区和使用 char *recvbuf = new char[nlength]; 的动态内存分配缓冲区 . 总之,这是最好的,为什么?

客户代码

int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuf[200] = "This is data From me";

int  nBytes = 200, nLeft, idx;
nLeft = nBytes;
idx = 0;
uint32_t varSize = strlen (sendbuf);
bytesSent = send(ConnectSocket,(char*)&varSize, 4, 0);
assert (bytesSent == sizeof (uint32_t));
std::cout<<"length information is in:"<<bytesSent<<"bytes"<<std::endl;
// code to make sure  all data has been sent
  while (nLeft > 0)
{
    bytesSent = send(ConnectSocket, &sendbuf[idx], nLeft, 0);
    if (bytesSent == SOCKET_ERROR)
    {
      std::cerr<<"send() error: " << WSAGetLastError() <<std::endl;
      break;
    }
    nLeft -= bytesSent;
    idx += bytesSent;
}

 std::cout<<"Client: Bytes sent:"<< bytesSent;

服务器代码:

int bytesSent;
char sendbuf[200] = "This string is a test data from server";
int   bytesRecv;
int idx = 0;
uint32_t  nlength;
int length_received = recv(m_socket,(char*)&nlength, 4, 0);//Data length info
char *recvbuf = new char[nlength];//dynamic memory allocation based on data length info
//code to make sure all data has been received
while (nlength > 0)
{
    bytesRecv = recv(m_socket, &recvbuf[idx], nlength, 0);

    if (bytesRecv == SOCKET_ERROR)
    {
        std::cerr<<"recv() error: " << WSAGetLastError() <<std::endl;
        break; 
    }
    idx += bytesRecv;
    nlength -= bytesRecv;
}

  cout<<"Server: Received complete data is:"<< recvbuf<<std::endl;
  cout<<"Server: Received bytes are"<<bytesRecv<<std::endl;
  WSACleanup();
  system("pause");
  delete[] recvbuf; 
  return 0;

}

2 回答

  • 1

    第一个问题:我在TCP中的缓冲区之间感到困惑 . 我试图解释我的问题,我读了这篇文档TCP缓冲区,作者说了很多关于TCP缓冲区,这很好,对初学者来说是一个非常好的解释 . 我需要知道的是,这个TCP缓冲区与我们在基本客户端服务器程序(Char * buffer [Some_Size])中使用的缓冲区是相同的缓冲区,还是TCP内部保留的一些不同的缓冲区?

    当您调用send()时,TCP堆栈会将char数组中的一些字节复制到内核缓冲区中,send()将返回它复制的字节数 . 然后,TCP堆栈将尽可能快地处理那些内核中字节到网络目的地的传输 . 's important to note that send()'的返回值不保证与您在传递给它的长度参数中指定的字节数相同;它可能会更少 . 它's also important to note that sends()'的返回值并不意味着很多字节已经到达接收程序;相反,它只表示内核已从您接受并将尝试传递的字节数 .

    同样,recv()只是将一些字节从内核缓冲区复制到您指定的数组,然后将它们从内核缓冲区中删除 . 同样,复制的字节数可能小于您要求的数量,并且通常与发送方在任何特定send()调用时传递的字节数不同 . (例如,如果发送方调用send()并且其send()返回1000,则可能导致您调用recv()两次并且recv()每次返回500,或者recv()可能返回250四次,或者(1) ,990,9),或者你能想到的任何其他组合,最终总计达1000)

    我的第二个问题是我发送一个带有前缀长度的字符串数据(这是来自我的数据)从客户端通过套接字发送到服务器,当我在控制台上打印我的数据和我的字符串时它打印一些垃圾值也像这样“这个是来自我的数据zzzzzz 1/2 1/2 .....“ . 然而我通过右移char * recvbuf = new char [nlength >> 3]来修复它; nlength到3位,但为什么我需要这样呢?

    像Joachim所说,这是因为C字符串依赖于NUL终结符字节(即零字节)的存在来指示它们的结束 . 您正在接收strlen(sendbuf)字节,并且strlen()返回的值不包含NUL字节 . 当接收者的字符串打印例程尝试打印字符串时,它会一直打印,直到在内存中稍后某处找到NUL字节(偶然);与此同时,您可以在该点之前看到内存中的所有随机字节 . 要解决这个问题,可以将发送字节计数器增加到(strlen(sendbuf)1),这样也可以接收NUL终结器字节,或者让接收器手动将NUL字节放在字符串结尾处 . 已收到字符串的所有字节 . 无论哪种方式都是可以接受的(后一种方式可能稍微优选,因为接收方不依赖发送方来做正确的事情) .

    请注意,如果您的发送方将始终发送200个字节而不仅仅是字符串中的字节数,那么如果接收方想要接收多个块,则需要始终接收200个字节;否则当它试图接收下一个块时,它将首先获得所有额外的字节(在字符串之后),然后才能获得下一个块的发送长度字段 .

    我的第三个问题与第一个问题有关,如果没有像TCP缓冲区那样,它只有关于Char *缓冲区[some_size]那么我的程序会注意到使用这样的静态内存分配缓冲区和使用动态内存分配缓冲区的差异char * recvbuf = new char [nlength]; . 总之,这是最好的,为什么?

    在性能方面,它完全没有区别 . send()和receive()不关心是否有传递给它们的指针指向堆或堆栈 .

    在设计方面,有一些权衡:如果使用new,如果在完成缓冲区后并不总是调用delete [],则有可能泄漏内存 . (当抛出异常或采用错误路径时,尤其会发生这种情况) . 另一方面,将缓冲区放在堆栈上保证不会泄漏内存,但堆栈上可用的空间量是有限的,因此一个非常大的数组可能导致程序耗尽堆栈空间并崩溃 . 在这种情况下,堆栈上的单个200字节数组没有问题,所以这就是我要使用的 .

  • 4

    您无条件地从客户端发送200个字节,但在服务器中您只收到字符串的实际长度,并且该长度不包括字符串终止符 .

    首先,你没有收到所有发送的数据(这意味着你将填满系统缓冲区),然后你没有正确终止字符串(这会导致在尝试打印字符串时出现“垃圾”输出) .

    要解决这个问题,在客户端只发送字符串的实际长度( varSize 的值),并在接收服务器中为终结器分配一个字符,当然需要添加 .

相关问题