简体   繁体   中英

C++ download binary file from http

I'm creating an update mechanism for my first program written in c++. Theory is:

  1. program sends it's version to the server php as a http header
  2. server checks if later version exists
  3. if it does, server sends the new binary to the client.

Most of it works however the binary received is malformed. When I compare the malformed exe with the working exe I have differences at places where I have \\r\\n s in the compiled exe. Seems like the \\r is doubled.

My c++ code for downloading:

void checkForUpdates () {
    SOCKET sock = createHttpSocket (); // creates the socket, nothing wrong here, other requests work

    char* msg = (char*)"GET /u/2 HTTP/1.1\r\nHost: imgup.hu\r\nUser-Agent: imgup uploader app\r\nVersion: 1\r\n\r\n";

    if (send(sock, msg, strlen(msg), 0) == SOCKET_ERROR) {
        error("send failed with error\n");
    }
    shutdown(sock, SD_SEND);

    FILE *fp = fopen("update.exe", "w");
    char answ[1024] = {};
    int iResult;
    bool first = false;
    do {
        if ((iResult = recv(sock, answ, 1024, 0)) < 0) {
            error("recv failed with error\n");
        }
        if (first) {
            info (answ); // debug purposes
            first = false;
        } else {
            fwrite(answ, 1, iResult, fp);
            fflush(fp);
        }
    } while (iResult > 0);
    shutdown(sock, SD_RECEIVE);

    if (closesocket(sock) == SOCKET_ERROR) {
        error("closesocket failed with error\n");
    }

    fclose(fp);

    delete[] answ;
}

and my php to process the request

<?php
if (!function_exists('getallheaders')) {
    function getallheaders() {
        $headers = '';
        foreach ($_SERVER as $name => $value) {
            if (substr($name, 0, 5) == 'HTTP_') {
                $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
            }
        }
        return $headers;
    }
}

$version = '0';
foreach (getallheaders() as $name => $value) {
    if (strtolower ($name) == 'version') {
        $version = $value;
        break;
    }
}

if ($version == '0') {
    exit('error');
}


if ($handle = opendir('.')) {
    while (false !== ($entry = readdir($handle))) {
        if ($entry != '.' && $entry != '..' && $entry != 'u.php') {
            if (intval ($entry) > intval($version)) {
                header('Content-Version: ' . $entry);
                header('Content-Length: ' . filesize($entry));
                header('Content-Type: application/octet-stream');
                echo "\r\n";
                ob_clean();
                flush();
                readfile($entry);
                exit();
            }
        }
    }
    closedir($handle);
}
echo 'error2';


?>

notice the way I flush content after I send the headers ob_clean(); flush(); ob_clean(); flush(); so I don't have to parse them in c++. The first bytes written to the file are fine, so I doubt there is any problem here.

Also, example comparison of the binaries http://i.imgup.hu/meC16C.png

Question: Does http escape \\r\\n in binary file transfers? If not, what is causing this behavior and how do I solve this problem?

fopen opens a File in the mode you specified, first read/write/both, then Append, then a binary identifier.

r/w should be clear to you, append is also quite obvious. The Trick & Trouble in your case is the binary-mode.

If a file is threated as a Text-File (without the "b") then, depending on the environment where the application runs, some special character conversion may occur in input/output operations in text mode to adapt them to a system-specific text file format. On Windows this would be \\r\\n, on a linux machine you have \\n and on some architectures exist, where it is \\r.

In your case, the input file is read as a text file. This means, all your line-endings get converted when reading the file from the HTTP-Data.

Opening the File as a binary file (wich indeed it is!) avoids trouble that your file is not binary identically anymore.

The problem is that the output file isn't being opened in binary mode. To do that, change the mode to "wb" versus just "w" like this:

FILE *fp = fopen("update.exe", "wb");

In text mode on Windows the ctrl+z character specifies the end of the file when seeking/reading, and the linefeed character \\n is translated to \\r\\n when writing and \\r\\n pairs are translated to \\n on reading. In binary mode, the file data is not interpreted or translated in any way.

On other platforms the translations may not apply, but it is still good practice to show the intent of the code by specifying the explicit mode even when not strictly necessary. This is especially true for code meant to be portable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM