【问题标题】:TCP Socket garbageTCP 套接字垃圾
【发布时间】:2014-05-17 04:46:17
【问题描述】:

我有一个 TCP 协议服务器-客户端程序。

与很多人不同,我没有收到错误,因为我的发送函数没有发送应有的字节数。 我的问题是因为 recv() 函数捕获了一些奇怪的字符。我对它们进行了十六进制转储,它们往往是负数(我猜大于 4 位长)或不是字母数字的。(一直出现的是 \8 = ◘)。

我使用的功能是:

bool Socket::Send(char* msg){
    if ((send(sockfd, msg, strlen(msg), 0)) == SOCKET_ERROR){
        Close();
        return false;
    }
    return true;
}

int Socket::Receive(char* msg, int maxlen){
    int recvResult;
    if ((recvResult = recv(sockfd, msg, maxlen, 0)) == SOCKET_ERROR){
        Close();
    }
    return recvResult;
}

我检查过,发送的所有内容都正常。我的意思是,我将它写入一个文件并检查它,它是好的。

还有什么我应该考虑的吗?我做错了什么?

【问题讨论】:

  • 在声明变量时,是否有某种险恶的力量阻止您初始化变量?一定是因为您在 if 语句中为它们赋值。
  • 如何打印缓冲区?

标签: c++ sockets hexdump


【解决方案1】:

与很多人不同,我没有收到错误,因为我的发送函数没有发送应有的字节数。

没有人得到那个错误,因为它永远不会发生,尽管,因为你没有检查它,你无法断言它没有发生。

我的问题是因为 recv() 函数捕获了一些奇怪的字符。

没有。发生的情况是recv() 函数返回的字节数少于请求的字节数,这是因为未指定在非阻塞模式下传输多于一个字节或零字节。您必须循环,直到您读取所有您期望的数据。

并且您必须注意不要使用接收缓冲区中未在最近的recv() 调用中传输的字节,如其返回值所示。

您几乎肯定会打印整个缓冲区,因此您看到的是垃圾。既没有发送也没有收到。

注意如果recv() 返回零,您应该关闭套接字。

【讨论】:

  • 我说是因为我正在处理的情况。想象一下我发送(“你好”)。当我调用 recv(buffer) 函数然后打印出缓冲区时,我得到类似:“hel◘◘♦☺lo”。你看,我得到了我应该得到的所有字节,但中间有垃圾。如果你指的是这样的情况,那我就不明白了。你能告诉我具体是怎么写这个函数的吗?
  • 缓冲区中有一个空字符,或者您正在读取超过 recv 返回的数量,或者您正在使用分块传输编码。
  • 当您调用recv() 函数时,它会告诉您它传输了多少字节。告诉我们缓冲区中的内容是没有意义的,除非它仅限于recv().的返回值所指示的内容@
  • 也许这就是问题所在。我没有对消息进行编码。我只是发送缓冲区。 htonl 真的有必要吗?
  • 好吧,我发现了一些东西。 recv() 函数总是返回最大字节数。消息是否更短并不重要(a 并且只有 2 个字节的消息并且正在读取所有 100 个字节)。也许我发错了,我的意思是,我在最后发送了一个带有一堆 ceros 的消息。
【解决方案2】:

你说你有一个 TCP 协议,你有的不是一个协议,它只是一个 TCP 连接。

为了实现一个协议,您需要发送更多信息,recv() 部分应该(必须)知道将要接收什么。

您需要在缓冲区的长度之前发送一个终止字符,或者在缓冲区的末尾发送一个终止字符,这不应该是已发送消息的其余部分的一部分。 Line-Feed (10)、Carriage-Return (13) 和 Form-Feed (12) 等字符都是不错的选择。

如果您决定使用终止字符,那么您应该一次读取 (recv) 一个字符,验证这是否是终止指示符。终止字符仅在发送表示文字的字符串时才有用,不指示用于发送文件或图像或二进制字符串。

我已经修改了实现缓冲区指示器方法长度的例程:

bool Socket::Sender(char* msg){
    // Send a string with the length (int) of it ahead
    int len = strlen(msg);
    int hLen = htonl(len); // put it in network byte order
    if (send(sockfd, (char *) &hLen, sizeof(int), 0) == sizeof(int) &&
        send(sockfd, msg, len, 0) == len) 
    {
        return true;
    }
    Close();
    return false;
}

int Socket::Receive(char* msg, int maxlen){
    int recvResult;
    int len;
    int offset;

    // receive a string with the length (int) of it first
    if (recv(sockfd, (char *) &len, sizeof(int), 0) == sizeof(int))
    {
        len=ntohl(len); // put in host byte order
        // Check there is enough space
        if (len > maxlen)
        {
            // the receive chars is bigger than maxlen, 
            // either receive as many as maxlen or return error
            len=maxlen;
            // Close();
            // return -1;
        }
        offset=0;
        // loop until len chars are received, no more no less
        while (offset < len) 
        {
            recvResult = recv(sockfd, (char *) &msg[offset], len-offset, 0);
            if (recvResult <= 0)
            {
                Close();
                return -1;
            }
            offset +=recvResult;
        }
        // Succeeded, return len
        return len;
    }
    Close();
    return -1;
}

【讨论】:

  • 他没有说他有一个“TCP 协议”:他说他有一个“TCP 协议客户端/服务器程序”。您不必一次接收一个字符,这样做效率极低。终止字符是在之后发送的,而不是在前面发送的。终止字符与“文字”无关:只要终止符不能出现在消息中,就可以使用它们,或者即使它可以出现,如果还有转义约定的话。如果收到少于或多于初始长度的单词,您的代码将不起作用。 -1
猜你喜欢
  • 1970-01-01
  • 2013-05-25
  • 2021-11-28
  • 1970-01-01
  • 1970-01-01
  • 2021-12-13
  • 1970-01-01
  • 2020-04-30
  • 1970-01-01
相关资源
最近更新 更多