Как отправить данные размером 10 000 ~ 20 000 байт по TCP? - PullRequest
0 голосов
/ 10 августа 2011

Могу ли я отправить около 10 000 ~ 20 000 байт данных по TCP? Я передаю изображение (60 на 60) с клиента Android на сервер Linux. На андроиде вроде нормально. На стороне сервера, если я пытаюсь отправить данные изображения обратно клиенту, то это не работает. На стороне клиента, если я проанализирую, то получу какой-то странный номер, который мне не следует получать

Есть ли техническая проблема передачи больших данных по 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 * 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;

Ответы [ 3 ]

5 голосов
/ 10 августа 2011

То, что вы пытаетесь сделать, легко (20K не "большой").Наиболее распространенная причина, по которой происходит подобное, - игнорирование кодов возврата send и recv.Следует помнить несколько вещей:

  • send(2) не всегда может копировать все данные из пространства пользователя в пространство ядра.Проверьте возвращаемое значение
  • Все данные поступают не одновременно, поэтому вам придется recv несколько раз, прежде чем получить их все

На практике,во многих системах вам может не хватить send больших объемов данных (ядро будет смеяться над вашими 20КБ), но вам придется получать их в цикле.Вот функция, вдохновленная Стивенсом.Используйте его вместо 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;
}

РЕДАКТИРОВАТЬ

Вы, кажется, забываете о порядке байтов (как подозревал Эндрю Финнелл).Для каждого целого числа вы должны сделать что-то подобное перед отправкой (до memcpy):

m_dataLength = htonl(m_datalength);

И это при получении:

m_dataLength = ntohl(m_datalength);
2 голосов
/ 10 августа 2011

Вы говорите, что можете успешно отправить и прочитать изображение, если отправили его с Android на свой компьютер с Linux?Если нет, то это может звучать как проблема Endian , если ваш процесс Linux Server не был написан на Java.

Обновление

СУ cnicutar есть отличный ответ. Я хотел бы предложить альтернативу необходимости делать ваш протокол вручную.

Буфер протокола Google

При его использовании он автоматически преобразуетот хоста к сети и обратно.Его также можно использовать для создания привязок C ++ и Java.

0 голосов
/ 10 августа 2011

Я думаю, strace может помочь здесь как на стороне клиента, так и на стороне сервера. Ищите сетевые ошибки и количество отправленных / полученных данных в журнале strace.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...