【问题标题】:How do I send 10,000 ~ 20,000 bytes data over TCP?如何通过 TCP 发送 10,000 ~ 20,000 字节的数据?
【发布时间】:2011-08-11 18:48:12
【问题描述】:

我可以通过 TCP 发送大约 10,000 ~ 20,000 字节的数据吗?我正在将图像(60 x 60)从 Android 客户端传输到 linux 服务器。在android上似乎还可以。在服务器端,如果我尝试将图片数据发送回客户端,则它不起作用。在客户端,如果我解析然后我得到一些我不应该得到的奇怪数字。

通过 TCP 传输大数据是否存在任何技术问题?我该如何解决? 提前谢谢..

char* PictureResponsePacket::toByte(){

    /*
     * HEADER
     *
     * Magic number (4)
     * Data length  (4)
     * Packet Id    (2)
     * Packet type  (2)
     * Device Id    (48)
     *
     */

    /*
     * BODY
     *
     * Nickname  (48)
     * deviceId  (4)
     * m_pictureSize
     */

    int offset = 0;

    int headerLength = sizeof(int) + sizeof(int) + sizeof(short) + sizeof(short) + 48;
    int bodyLength = 48 + 4 + m_pictureSize;
    int dataLength = headerLength + bodyLength;
    m_dataLength = dataLength;

    log("PictureResponsePacket::toByte(), data length %d \n", m_dataLength);

    char *sendBuffer = new char[dataLength];
    memset(sendBuffer, 0x00, dataLength);

    char *ptr = sendBuffer;

    /*
     * -------------
     * HEADER
     * -------------
     */
    /*
     * Magic number
     */
    memcpy(ptr + offset, m_magicNumberBuffer, sizeof(int));
    offset += sizeof(int);

    /*
     * Data length
     */


    memcpy(ptr + offset, &m_dataLength, sizeof(int));
    offset += sizeof(int);

    /*
     * Packet id
     */
    memcpy(ptr + offset, &m_packetId, sizeof(short));
    offset += sizeof(short);

    /*
     * Packet type
     */
    memcpy(ptr + offset, &m_packetType, sizeof(short));
    offset += sizeof(short);

    /*
     *Device Id
     */

    memcpy(ptr + offset, m_deviceId.c_str(), m_deviceId.size());
    offset += 48;

    /*
     * -------------
     * BODY
     * -------------
     */
    memcpy(ptr + offset, m_senderDeviceId.c_str(), m_senderDeviceId.size());
    offset += 48;

    memcpy(ptr + offset, &m_pictureSize, sizeof(int));
    offset += sizeof(int);

    memcpy(ptr + offset, m_pictureData, m_pictureSize);
    offset += m_pictureSize;


    return sendBuffer;


}

我以这种方式获取 char* 并像这样发送它

                char * sBuffer = reponsePacket->toByte();
                int remainLength = reponsePacket->getDataLength();
                int currentSentLength = 0;
                SocketClient *client = work->getClient();

                while(remainLength > 0){

                    if(remainLength >= MAX_LENGTH)
                        currentSentLength = send(client->getFd(), sBuffer, MAX_LENGTH, MSG_NOSIGNAL);
                    else
                        currentSentLength = send(client->getFd(), sBuffer, remainLength, MSG_NOSIGNAL);

                    if(currentSentLength == -1){
                        log("WorkHandler::workLoop, connection has been lost \n");
                        break;
                    }

                    sBuffer += currentSentLength;
                    remainLength -= currentSentLength;

【问题讨论】:

  • 是的,你可以发送这么多的数据,但通常不能在一个数据包中发送。
  • 显示一些代码,可能有很多问题。
  • 20kB 是大数据吗? O RLY?
  • 直到有人告诉我我疯了,我仍然坚持至少部分问题是字节序。查看gnu.org/s/hello/manual/libc/Byte-Order.html 并在发送前将所有值转换为网络字节顺序。

标签: c++ tcp


【解决方案1】:

您尝试做的事情很简单(20K 并不“大”)。到目前为止,发生这种情况的最常见原因是忽略 sendrecv 的返回码。您应该记住以下几点:

  • send(2) 不能总是将所有数据从用户空间复制到内核空间。检查返回值
  • 数据不会同时到达,因此您必须recv 几次才能获得所有数据

在实践中,在许多系统上,您可能会使用sending 大量数据(内核会嘲笑您的 20K),但您必须循环接收。这是一个深受 Stevens readn 启发的函数。用它代替recv

ssize_t
readn(int fd, void *vptr, size_t n)
{
    size_t nleft;
    ssize_t nread;
    char *ptr;

    ptr = vptr;
    nleft = n;
    while (nleft > 0) {
        if ((nread = read(fd, ptr, nleft)) < 0) {
            if (errno == EINTR)
                /* Loop back and call read again. */
                nread = 0;
            else
                /* Some other error; can't handle. */
                return -1;
        } else if (nread == 0)
            /* EOF. */
            break;

        nleft -= nread;
        ptr += nread;
    }
    return n - nleft;
}

编辑

您似乎忘记了字节顺序(正如 Andrew Finnell 所怀疑的那样)。对于每个整数,您应该在发送之前(memcpy 之前)执行以下操作:

m_dataLength = htonl(m_datalength);

接收时是这样的:

m_dataLength = ntohl(m_datalength);

【讨论】:

  • 您仍然没有考虑 Endian 问题。 Java 总是大端的。 x86 上的原生代码理解 little-endian。
  • 我把我之前的评论留在那里,因为从技术上讲这是真的。但这段代码只是一个网络流媒体。他仍然需要使用 that 可以解决字节序问题的解码器。
  • @Andrew Finnell 我在 OP 编辑​​并添加代码之前回答了问题。 Indedd 似乎他没有像他应该的那样使用 htons / htonl。
  • 在他发布代码之前我们都不知道。在代码更新之前,我一直在猜测。因为你现在是我最完整的 +1。
【解决方案2】:

您是说如果您将图像从 Android 发送到您的 Linux 机器,您可以成功发送和读取图像吗?如果不是,那么听起来可能是Endian 问题,如果您的 Linux 服务器进程不是用 Java 编写的。

更新

由于 cnicutar 有一个很好的答案,我想提供一个替代方案,而不必手动执行您的协议。

Google Protocol Buffer

使用它时,它会自动从主机转换到网络并返回。它还可用于创建 C++ 和 Java 绑定。

【讨论】:

    【解决方案3】:

    我认为 strace 可能在客户端和服务器端都有帮助。在 strace 日志中查找网络错误和发送/接收的数据量。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-22
      相关资源
      最近更新 更多