javac++tcpclienttcpserversos

C++ client socket sends original file and file size, Java client always get 8 bytes more. Even after force limiting to -8 bytes file is unreadable


For a while, I am troubled with this issue.

Using a snippet of C++ code, which I found here, and in my opinion should do a good job actually sending exact amount of data, I guess the problem is in the Java code or something unknown.

Also, when sending simple "Hello World" data, transfer is done correctly.

I would be grateful is somebody could solve it, or give a valuable advice. I am running Windows 10 with jdk1.8.0_221 and Visual Studio 2022.

I understand there could be a need to send filesize as a string and aknowledge flag in return in future, but for simplicity the C++ client is sending only one file with known amount of data.

Here is the Java side:

while(!Terminate)
{    
    byte[] bytes = new byte[1];//code was different for a real buffer 
    //...still +8 bytes
    while (in.available() > 0) 
    {
        in.read(bytes);
        out.write(bytes);
        out.flush();
    }
}
out.close();
int filesize = 15670;
int cnt = 0;
while (cnt<filesize)
{
    out.write(in.read());
    out.flush();
    cnt++;
}

It makes me think 8 bytes are added while saving.

The C++ code:

int SendBuffer(SOCKET s, const char* buffer, int bufferSize, int chunkSize = 4 * 1024) {
    int l = -1;
    int i = 0;
    while (i < bufferSize) 
    {
        int l = send(s, &buffer[i], __min(chunkSize, bufferSize - i), 0);
        //int l = send(s, &buffer[i], bufferSize , MSG_DONTROUTE);
        int j = 0;
        std::cout << i << std::endl;
        while (l < 0) { Beep(433, 1000); j++; std::cout << "shiban l" << l << std::endl; l = send(s, &buffer[i], __min(chunkSize, bufferSize - i), 0); } // this is an error
        i += l;
    }
    return i;
}

int64_t SendFile(SOCKET s, const std::string fileName, int chunkSize) {

    const int64_t fileSize = GetFileSize(fileName);
    if (fileSize < 0) { return -1; }

    std::ifstream file(fileName, std::ifstream::binary);
    if (file.fail()) { return -1; }

    if (SendBuffer(s, reinterpret_cast<const char*>(&fileSize),
        sizeof(fileSize)) != sizeof(fileSize)) {
        return -2;
    }

    char* buffer = new char[chunkSize];
    bool errored = false;
    int64_t i = fileSize;
    auto bytes_sent = 0;
    while (i > 0) {
        const int64_t ssize = __min(i, (int64_t)chunkSize);
        if (!file.read(buffer, ssize)) { errored = true; break; }
        const int l = SendBuffer(s, buffer, (int)ssize);
        bytes_sent += l;
        int bi = 0;
        if (l < 0) { std::cout <<" err :"<< l<<std::endl; errored = true; break; }
        i -= l;
    }
    delete[] buffer;

    file.close();
    std::cout << "bytes_sent:" << bytes_sent << std::endl;
    return errored ? -3 : fileSize;
}

Solution

  • The C++ code is sending the file size before sending the file data (good), but is not doing adequate error handling (bad), and it is NOT sending the file size in an platform-agnostic format (bad).

    Not that it matters, because the Java code shown is NOT even attempting to read the file size before reading the file data (very bad), nor is it paying attention to the return value of in.read() to know how many bytes are actually received. But even if it were, the C++ code is sending the size as an 8-byte integer (needed for large files > 2GB), but the Java code is using a 4-byte integer instead (bad). The C++ is also using a 4-byte integer for bytes_sent in SendFile().

    Try something more like this:

    BufferedInputStream bis = new BufferedInputStream(in);
    DataInputStream dis = new DataInputStream(bis);
    long filesize = dis.readLong();
    if (filesize > 0)
    {
        byte[] bytes = new byte[4*1024];
        do
        {
            int chunkSize = (int) Math.min(filesize, (long) bytes.length);
            dis.readFully(bytes, 0, chunkSize);
            out.write(bytes, 0, chunkSize);
            out.flush();
            filesize -= chunkSize;
        }
        while (filesize > 0);
    }
    out.close();
    
    bool SendBuffer(SOCKET s, const char* buffer, int bufferSize) {
        while (bufferSize > 0)
        {
            int numSent = send(s, buffer, bufferSize, 0/*MSG_DONTROUTE*/);
            if (numSent < 0) {
                Beep(433, 1000);
                return false;
            }
        }
        return true;
    }
    
    int64_t SendFile(SOCKET s, const std::string fileName, int chunkSize) {
    
        const int64_t fileSize = GetFileSize(fileName);
        if (fileSize < 0) { return -1; }
    
        std::ifstream file(fileName, std::ifstream::binary);
        if (file.fail()) { return -1; }
    
        const int64_t tmp = htonll(fileSize); // see https://stackoverflow.com/questions/3022552/
        if (!SendBuffer(s, reinterpret_cast<const char*>(&tmp), sizeof(tmp))) {
            return -2;
        }
    
        std::vector<char> buffer(chunkSize);
        bool errored = false;
        int64_t bytes_sent = 0;
    
        while (fileSize > 0) {
            int ssize = static_cast<int>(std::min<int64_t>(fileSize, chunkSize));
            if (!file.read(buffer.data(), ssize)) { errored = true; break; }
            if (!SendBuffer(s, buffer.data(), ssize)) { errored = true; break; }
            fileSize -= ssize;
        }
    
        file.close();
    
        if (errored) {
            std::cout << "err" << std::endl;
            return -3;
        }
    
        std::cout << "bytes_sent:" << bytes_sent << std::endl;
        return bytes_sent;
    }