Cannot send a bigger file

215 Views Asked by At

I made a client server program by using C++.

I have a problem if I try to send large files. For example, a 50 byte file works fine while a 200 byte file fails.

Server Code:

// server.cpp : Defines the entry point for the console application.   
#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#include <stdio.h>

int _tmain(int argc, _TCHAR* argv[])
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int wsaerr;
    wVersionRequested = MAKEWORD(2, 2);

    wsaerr = WSAStartup(wVersionRequested, &wsaData);

    if (wsaerr != 0) {
        printf("The Winsock DLL not found \n "); 
    } else {
        printf("The Winsock DLL found\n ");
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("not support Winsock version 2.2 ");
    } else {
        printf("support winsock version 2.2 \n ");
    }

    SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET) {
        printf("Error di socket(): %ld\n", WSAGetLastError());
        WSACleanup();
    } else{
        printf("Socket() Berhasil ! \n");
    }

    sockaddr_in service;
    service.sin_family = AF_INET;
    service.sin_addr.s_addr = inet_addr("127.0.0.1"); 
    service.sin_port = htons(55555);  

    int namelen = sizeof(service);

    int m_bind = bind(m_socket, (sockaddr*)&service, namelen);
    if (m_bind == SOCKET_ERROR){
        printf("bind() failed ! %ld\n ", WSAGetLastError());
    } else {
        printf("bind() ok ! \n");
    }

    if (listen(m_socket, 1) == SOCKET_ERROR) {
        printf("Listen() failed ! %d\n ", WSAGetLastError());
    } else {
        printf("Listen() ok ! \n"); 
    }

    SOCKET AcceptSocket;
    printf("waiting for Client...\n \n");
    int addresslen = sizeof(service);

    while (AcceptSocket = accept(m_socket, (sockaddr*)&service, &addresslen)) {
        printf("Server dan Client connected --> ");
        char *ClientIP = inet_ntoa(service.sin_addr);
        int ClientPort = ntohs(service.sin_port);

        printf("IP:  %s:%d\n ", ClientIP, ClientPort);

        char *Filesize = new char[10];
        int Size = 0;
        int recv_size, recv_file;
        char Buffer[MAXCHAR];
        FILE *File; 

        recv_file = recv(AcceptSocket, Buffer, Size, 0);
        recv_size = recv(AcceptSocket, Filesize, 10, 0);

        while (Filesize) {
            //Menerima File Size
            Size = atoi((const char*)Filesize);
            File = fopen("D:\\fileReceived.txt", "wb");
            fwrite((const char*)Buffer, 1, Size, File);
            fclose(File);
            printf("File received \n");
            ZeroMemory(Buffer, Size);
            //  printf("File size : %d\n",Size);
            recv_file = recv(AcceptSocket, Buffer, Size, 0);
            recv_size = recv(AcceptSocket, Filesize, 10, 0);
        }
    }
}

Client Code

// client.cpp : Defines the entry point for the console application.
#include "stdafx.h"
#include <winsock2.h>
#include <Windows.h>
#include <stdio.h>

using namespace std;

int Size = 0;
char *Buffer;

int _tmain(int argc, _TCHAR* argv[])
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int wsaerr;
    wVersionRequested = MAKEWORD(2, 2);

    wsaerr = WSAStartup(wVersionRequested, &wsaData);

    if (wsaerr != 0) {
        printf("The Winsock DLL not found \n ");
    } else {
        printf("The Winsock DLL found \n ");
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        printf("not support Winsock version 2.2 ");
    } else {
        printf("support winsock version 2.2 \n ");
    }

    SOCKET Client_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (Client_socket == INVALID_SOCKET) {
        printf("Error di socket(): %ld\n", WSAGetLastError());
        WSACleanup();
    } else{
        printf("Socket() ok ! \n");
    }

    SOCKADDR_IN  clientService;
    clientService.sin_family = AF_INET;
    clientService.sin_addr.s_addr = inet_addr("127.0.0.1");
    clientService.sin_port = htons(55555);

    if (connect(Client_socket, (SOCKADDR*)&clientService, sizeof(clientService)) == SOCKET_ERROR) {
        printf("connect() fail ! \n");
    } else {
        printf(" connect() ok .... \n ");

        while (1){
            FILE *File;
            File = fopen("D:\\logging21.txt", "rb");
            if (!File){
                printf("", WSAGetLastError());
            }

            printf("File open ok ! \n");

            fseek(File, 0, SEEK_END);
            Size = ftell(File);
            fseek(File, 0, SEEK_SET);

            char cisi[10];
            sprintf(cisi, "%i", Size);
            //  fclose(File);
            send(Client_socket, cisi, 10, 0);   //file size sent
            //  Sleep(6000);  

            Buffer = (char*)malloc(Size + 1); 
            fread(Buffer, Size, 1, File);
            fclose(File);
            send(Client_socket, Buffer, Size, 0); // File Binary sent
            free(Buffer);
            printf("sending finished....\n");
            Sleep(6000);
        }
    }
}
2

There are 2 best solutions below

0
On

The server is riddled with errors, but this is the most relevant to the asked question: Why can I only send a few bytes?

char *Filesize = new char[10];
int Size = 0;  // note the size is set to zero
int recv_size, recv_file;
char Buffer[MAXCHAR]; 
// no idea how big MAXCHAR is, but it turns out to be irrelevant
FILE *File; 

recv_file = recv(AcceptSocket, Buffer, Size, 0); 
// Above we use that size of zero to read zero bytes from the socket
recv_size = recv(AcceptSocket, Filesize, 10, 0);
// get the size of the file. This doesn't seem too bad
while (Filesize) { // but we just used a 10 byte blob of data containing who knows what 
                   // as the exit condition from a while loop. 
                   // never use anything from an external source, especially the internet
                   // without validating and verifying first.  
    //Menerima File Size
    Size = atoi((const char*)Filesize); // atoi fails to convert silently. Use strtol instead.
    File = fopen("D:\\fileReceived.txt", "wb"); // open file
    fwrite((const char*)Buffer, 1, Size, File); 
        // write over file contents with what  we hope is filesize from a buffer into 
        // which we read zero bytes. File now full of random crap.
    fclose(File);
    printf("File received \n");
    ZeroMemory(Buffer, Size);
    //  printf("File size : %d\n",Size);
    recv_file = recv(AcceptSocket, Buffer, Size, 0);
        // read size of the **last file** (we hope) into buffer
    recv_size = recv(AcceptSocket, Filesize, 10, 0);
}

At the very least, filesize must be read before trying to read the file.

Important fun fact about TCP: TCP is a stream, not a packet. Do not assume that because you wrote a number with send that the number is the only thing waiting to be read. For efficiency, TCP packs data together, so if you send "1234" and then a file 1234 bytes long, odds are pretty good both the file size and the file will arrive at the same time. So recv of 10 bytes will very likely read 1234, "1234"'s terminating null, and the first five bytes of the file. It's now up to you to separate the file length from the file data.

But if you send the length as a 32 bit integer, it will always be 4 bytes. Easy, yes? No. Because some computers and network protocols represent numbers backwards. I'm serious here. Google up endian.

Next: recv returns the number of bytes read. You may not get the number of bytes you asked for and have to keep asking until you get the while thing. recv also returns -1 if something goes wrong, so every time you recv, check that the return code is positive and that it's the number of bytes you need before doing anything with the data. Reading a 32 bit filesize, getting only 24 bits, and then trying to use those 24 bits to do meaningful work will really ruin your day.

And there's more! What if MAXCHARS is smaller than the size of the file? Well, that one is easy. You recv MAXCHARS or the number of bytes left in the file and write it out until the file is done.

So:

recv file size
Make sure it's really the filesize and nothing else.
open the output file
while file size is greater than zero
    recv up to MAXCHARS or file size, whichever is lower, into buffer
    if the number of bytes read is greater than zero
        write the number of bytes read from buffer into output file
        subtract the number of bytes read from file size
    else
        something bad happened to the connection. Give up.
close file
0
On

You tagged your question as C++ but the code is pretty much entirely C.

Here is a somewhat more C++ version of the server code. To finish the project, your client will need to start by sending a populated "FileTransfer" object, e.g.

FileTransfer xfer(file.size);
auto result = send(send_socket, &xfer, sizeof(xfer), 0);

then send the data from the file, ideally read <= FileTransfer::BufferSize bytes and then push them onto the socket until you have reach all the bytes you promised to send.

// move the code between 8x----x8x into protocol.h
// 8x---- snip ----x8
#pragma once
// protocol.h

#ifndef PROTOCOL_H
#define PROTOCOL_H 1

#include <cstdint>

struct FileTransfer
{
    enum { ProtoVersion = 1 };
    static const size_t BufferSize = 4 * 4096;
    uint32_t    m_proto;
    size_t      m_size;

    FileTransfer(size_t size_) : m_proto(ProtoVersion), m_size(size_) {}
    FileTransfer() : m_proto(0), m_size(0) {}

};

#endif // PROTOCOL_H
// 8x---- snip ----x8

// server.cpp : Defines the entry point for the console application.

#include "stdafx.h"
#define NOMINMAX
#include <WinSock2.h>
#include <Windows.h>
#include <Ws2tcpip.h>
#include <algorithm>
#include <fstream>
#include <iostream>

//#include "protocol.h"

#pragma comment(lib, "Ws2_32.lib")

void _describeConnection(sockaddr_in& service)
{
    char clientIP[128];
    inet_ntop(AF_INET, &(service.sin_addr), clientIP, sizeof(clientIP));
    auto clientPort = ntohs(service.sin_port);

    std::cout << "new connection from " << clientIP << ':' << clientPort << "\n";
}

bool _errorIndicatesInterrupted()
{
    auto err = WSAGetLastError();
    return (err == WSAEINTR || err == WSAEINPROGRESS);
}

void _receiveFile(SOCKET socket)
{
    FileTransfer xfer;
    auto recv_size = recv(socket, reinterpret_cast<char*>(&xfer), sizeof(xfer), 0);
    if (recv_size < sizeof(xfer)) {
        std::cout << "error: only " << recv_size
            << " bytes while recv()ing FileTransfer\n";
        return;
    }

    if (xfer.m_proto != FileTransfer::ProtoVersion) {
        std::cout << "error: connection protocol " << xfer.m_proto
            << " not supported\n";
        return;
    }

    if (xfer.m_size <= 0) {
        std::cout << "error: zero length transfer\n";
        return;
    }

    std::ofstream out("D:\\fileReceived.txt", std::ios::binary | std::ios::trunc);
    char recvBuffer[FileTransfer::BufferSize];
    size_t bytesLeft = xfer.m_size;
    while (bytesLeft) {
        do {
            recv_size = recv(socket, recvBuffer, std::min(bytesLeft, FileTransfer::BufferSize), 0);
        } while (recv_size < 0 && _errorIndicatesInterrupted());
        if (recv_size < 0) {
            std::cout << "error: transfer aborted\n";
            return;
        }
        out.write(recvBuffer, recv_size);
        bytesLeft -= recv_size;
    }

    std::cout << "transfered " << xfer.m_size << " bytes\n";
}

bool _server()
{
    SOCKET m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (m_socket == INVALID_SOCKET) {
        std::cout << "socket() failed! " << WSAGetLastError() << "\n";
        return false;
    }

    sockaddr_in service;
    service.sin_family = AF_INET;
    inet_pton(service.sin_family, "127.0.0.1", &service.sin_addr.s_addr);
    service.sin_port = htons(55555);

    int m_bind = bind(m_socket, (sockaddr*)&service, sizeof(service));
    if (m_bind == SOCKET_ERROR) {
        std::cout << "bind() failed! " << WSAGetLastError() << "\n";
        return false;
    }

    if (listen(m_socket, 1) == SOCKET_ERROR) {
        std::cout << "listen() failed! " << WSAGetLastError() << "\n";
        return false;
    }

    // This code can only accept one connection at a time.
    int addresslen = sizeof(service);
    for (;;) {
        std::cout << "waiting for client...\n";

        SOCKET acceptSocket = accept(m_socket, (sockaddr*)&service, &addresslen);
        if (acceptSocket < 0) {
            std::cout << "accept() failed: " << WSAGetLastError() << "\n";
            return false;
        }

        _describeConnection(service);

        _receiveFile(acceptSocket);

        closesocket(acceptSocket);
    }
}

int _tmain()
{
    WSADATA wsaData;
    WORD wVersionRequested = MAKEWORD(2, 2);
    int wsaerr = WSAStartup(wVersionRequested, &wsaData);
    if (wsaerr != 0) {
        std::cout << "WinSock DLL not found\n";
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) {
        std::cout << "WinSock 2.2 required\n";
        return 1;
    }

    _server();

    //  system("PAUSE");    Just use CTRL+F5.

    return 0;
}

If this is not a homework project and you are earnestly trying to get a file transfer project set up, consider using one of the libraries mentioned here: Best C/C++ Network Library.