Отправить двоичный файл через TCP / IP соединение

0

Здесь я буду перефразировать весь вопрос, чтобы он отвечал.

Я могу полностью копировать двоичный файл на том же компьютере, не используя сокеты, но просто создавая простую функцию копирования. Попытка реализовать этот код для копирования на TCP/IP-соединение, но не может заставить его работать.

FILE *filehandle = fopen("imagefile.jpg", "rb");
FILE *dest  =fopen("imagecopy.jpg", "wb");    // copied image file
fseek(filehandle, 0, SEEK_END);
unsigned long filesize = ftell(filehandle);
char *buffer = (char*)malloc(sizeof(char)*filesize);
rewind(filehandle);
int bytesread = fread(buffer, sizeof(char), filesize, filehandle);
for( int i=0; i<filesize; i++ )
{
    fputc(buffer[i], filehandle);    // copies all the contents to dest
}

Приведенный выше код отлично работает для копирования файла изображения на компьютер, но при его копировании на сервер его трудно обойти.

Я пытаюсь отправить файл изображения с сервера клиенту, который был сделан вручную на C. Длина файла, который будет отправлена сервером, известна только серверу при отправке файла, чтобы буфер был динамически сгенерированный на сервере, что-то вроде этого:

SERVER

fseek(filehandle, 0, SEEK_END);
long filesize = ftell(filehandle);    // file could be 11000bytes
char *buffer = (char*)malloc(sizeof(char)*filesize);    // char buffer with 11000 bytes to store the data from the file.
// then I call the send() function
rewind(filehandle);    // go back to beginning
send(clientsocket, buffer, filesize, 0);    // this is being sent perfectly, no errors because in the actual code, I am checking for errors

КЛИЕНТ

// here is where I don't understand how to dynamically allocate the 11000 bytes to store the data in a client buffer
// the filesize is not necessarily going to be 11000 so need to dynamically allocate
// I did the following:
#define BUFSIZE 10
FILE *filehandle = fopen("imagefile.jpg", "wb");    // image file created by client
char *buffer = (char*)malloc(sizeof(char)*BUFSIZE);
int bytesread = recv(buffer, 1, strlen(buffer), 0);
if( bytesread > 0 )
{
    printf("Bytes read: %d\n", bytesread);    // bytes read is 5
    printf("Buffer: %s\n", buffer);    // but buffer shows all the binary text like it normally would
    // when I try to store buffer in a file, it doesn't put full buffer because only 5 characters are written
    for( int i=0; i<bytesread; i++ )
    {
        fputc(buffer[i], filehandle);    // this doesn't create full image
    }
}

Как я могу динамически распределить 11000 байт, отправленных сервером?

  • 2
    Вы спрашиваете, почему код, который вы не показывали, не работает?
  • 2
    Неопределенное поведение - это именно то, что не определено . Не требуется сбой.
Показать ещё 6 комментариев
Теги:
file-io
malloc
winsock

2 ответа

11
Лучший ответ

Вам необходимо зацикливать как отправку, так и получение. Ни send() ни recv() могут отправлять/читать столько байтов, сколько вы запросили.

Вы также должны отправить размер файла перед данными файла, чтобы получатель знал, сколько байтов ожидать и когда прекратить чтение.

Попробуйте что-нибудь еще:

SERVER

bool senddata(SOCKET sock, void *buf, int buflen)
{
    unsigned char *pbuf = (unsigned char *) buf;

    while (buflen > 0)
    {
        int num = send(sock, pbuf, buflen, 0);
        if (num == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSAEWOULDBLOCK)
            {
                // optional: use select() to check for timeout to fail the send
                continue;
            }
            return false;
        }

        pbuf += num;
        buflen -= num;
    }

    return true;
}

bool sendlong(SOCKET sock, long value)
{
    value = htonl(value);
    return senddata(sock, &value, sizeof(value));
}

bool sendfile(SOCKET sock, FILE *f)
{
    fseek(f, 0, SEEK_END);
    long filesize = ftell(f);
    rewind(f);
    if (filesize == EOF)
        return false;
    if (!sendlong(sock, filesize))
        return false;
    if (filesize > 0)
    {
        char buffer[1024];
        do
        {
            size_t num = min(filesize, sizeof(buffer));
            num = fread(buffer, 1, num, f);
            if (num < 1)
                return false;
            if (!senddata(sock, buffer, num, 0))
                return false;
            filesize -= num;
        }
        while (filesize > 0);
    }
    return true;
}

FILE *filehandle = fopen("imagefile.jpg", "rb");
if (filehandle != NULL)
{
    sendfile(clientsocket, filehandle);
    fclose(filehandle);
}

КЛИЕНТ

bool readdata(SOCKET sock, void *buf, int buflen)
{
    unsigned char *pbuf = (unsigned char *) buf;

    while (buflen > 0)
    {
        int num = recv(sock, pbuf, buflen, 0);
        if (num == SOCKET_ERROR)
        {
            if (WSAGetLastError() == WSAEWOULDBLOCK)
            {
                // optional: use select() to check for timeout to fail the read
                continue;
            }
            return false;
        }
        else if (num == 0)
            return false;

        pbuf += num;
        buflen -= num;
    }

    return true;
}

bool readlong(SOCKET sock, long *value)
{
    if (!readdata(sock, value, sizeof(value)))
        return false;
    *value = ntohl(*value);
    return true;
}

bool readfile(SOCKET sock, FILE *f)
{
    long filesize;
    if (!readlong(sock, &filesize))
        return false;
    if (filesize > 0)
    {
        char buffer[1024];
        do
        {
            int num = min(filesize, sizeof(buffer));
            if (!readdata(sock, buffer, num))
                return false;
            int offset = 0;
            do
            {
                size_t written = fwrite(&buffer[offset], 1, num-offset, f);
                if (written < 1)
                    return false;
                offset += written;
            }
            while (offset < num);
            filesize -= num;
        }
        while (filesize > 0);
    }
    return true;
}

FILE *filehandle = fopen("imagefile.jpg", "wb");
if (filehandle != NULL)
{
    bool ok = readfile(clientsocket, filehandle);
    fclose(filehandle);

    if (ok)
    {
        // use file as needed...
    }
    else
        remove("imagefile.jpg");
}
  • 0
    не будет ли буфер 1024 слишком мал для размера файлов, отправляемых сервером?
  • 0
    Нет, поскольку получатель зацикливается, считывая фрагменты по 1024 байта, пока не будет получен конец файла.
Показать ещё 4 комментария
2

Мы могли бы избежать заголовка, который содержит размер изображения, но мы просто читаем до конца отправленных данных. Что касается размера буфера, мы могли бы использовать фиксированное число, такое как 10 * 1024, когда мы получили некоторые данные с сервера, мы просто сохраняем его в файл в соответствии с фактической длиной полученных данных.

// please open a file ...
FILE * fp;
// ...
const int LENGTH = 10 * 1024;

int len = 0;
char * buffer = (char *)malloc(LENGTH);
while ((len = recv(socket, buffer, LENGTH, 0)) > 0) {
    fwrite(buffer, 1, len, fp);
}
free(buffer);
// close the file

@TC: Я думаю, мы не можем выделить буфер в соответствии с размером, отправленным с сервера, если изображение слишком велико для сохранения внутри клиентской памяти. Не упоминайте, что сервер подделка и предназначена для любой атаки.

  • 0
    объем памяти, выделенный LENGTH, составляет 10240 байт, я полагаю, но размер файла изображения составляет 11656 байт, поэтому файл не будет скопирован полностью.
  • 0
    @ Хок: Вы продолжаете публиковать комментарии, которые говорят, что вы НЕ понимаете, как работают циклы. Это действительно так? Если это так, вам нужно прекратить то, что вы делаете, и вернуться к изучению основ программирования. Этот код будет продолжать читать в LENGTH чанках, записывая каждый чанк в fp , пока либо сокет не закроется, либо recv() завершится с ошибкой. Это означает, что этот код требует, чтобы отправитель закрыл сокет после завершения отправки файла, не отправляя размер файла заранее.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню