Send binary file over TCP/IP connection

2019-01-15 04:51发布

I will rephrase the whole question here so that it is answerable.

I am able to copy binary file perfectly in the same machine not using sockets but just making a simple copy function. Trying to implement this code for copying onto a TCP/IP connection but can't get it to work.

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
}

The code above works perfectly for copying an image file in the computer but when implemented to copy on server, it is difficult to go about it.

I am trying to send an image file from a server to a client both which have been made manually in C. The length of the file to be sent by the server is only known to the server when it's sending the file so the buffer is dynamically generated in the server, something like this:

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

CLIENT

// 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
    }
}

How can I dynamically allocate the 11000 bytes sent by the server?

2条回答
Summer. ? 凉城
2楼-- · 2019-01-15 05:25

You need to loop both the sending and receiving. Neither send() nor recv() are guaranteed to send/read as many bytes as you requested.

You also should send the file size before the file data so the receiver knows how many bytes to expect and when to stop reading.

Try something more like this:

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);
}

CLIENT

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");
}
查看更多
爱情/是我丢掉的垃圾
3楼-- · 2019-01-15 05:33

We could avoid the header that contains the image size, but we just read to the end of the sent data. About the buffer size, we could use a fixed number such as 10 * 1024, when we received some data from the server, we just save it into a file according to the actual received data length.

// 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

@T.C: I guess we cannot allocate a buffer according to the size sent from the server in case the image is too large to save inside the client's memory. Not mention the server is fake, and intended to make any attack.

查看更多
登录 后发表回答