Search code examples
c++windowsudpwinsock2

How to send any file (image, exe) through UDP in C++?


I'm trying to implement transmission of files through UDP protocol in C++. What I've got is the server which can send a file requested by a client, but it only works for .txt files. When I try to do the same with image or executable, the transmission corrupts and the file is about 0 KB.

Server:

#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <sstream>

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

#define cipherKey 'S'

int const bufferSize = 512;
char buffer[bufferSize];

void clearBuf(char* b)
{
    int i;
    for (i = 0; i < bufferSize; i++)
        b[i] = '\0';
}

char* notFound = "File not found.";

char Cipher(char ch)
{
    return ch ^ cipherKey;
}

int sendFile(FILE* file, char* buffer, int s)
{
    int i, len;
    if (file == NULL)
    {
        strcpy(buffer, notFound);
        len = strlen(notFound);
        buffer[len] = EOF;
        return 1;
    }

    char ch, ch2;
    for (i = 0; i < s; i++)
    {
        ch = fgetc(file);
        ch2 = Cipher(ch);
        buffer[i] = ch2;
        if (ch == EOF)
            return 1;
    }
    return 0;
}

int main()
{
    WSADATA wsaData;
    int wynik_winsock = WSAStartup(MAKEWORD(2,2), &wsaData);

    if(wynik_winsock != 0)
    {
        exit(1);
    }

    SOCKET socketServer;
    socketServer = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if(socketServer == INVALID_SOCKET)
    {
        WSACleanup();
        exit(1);
    }

    char* ipAdd = "127.0.0.1";
    int port = 1234;

    SOCKADDR_IN server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ipAdd);

    if(bind(socketServer, (SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        closesocket(socketServer);
        WSACleanup();
        exit(1);
    }

    std::cout << "Waiting." << std::endl;

    SOCKADDR_IN client;
    int len_client = sizeof(client);

    FILE* file;
    if(recvfrom(socketServer, buffer, bufferSize, 0, (SOCKADDR *)&client, &len_client) == SOCKET_ERROR) //Odbiór danych od clienta wraz z kontrolą błędów.
    {
        closesocket(socketServer);
        WSACleanup();
        exit(1);
    }
    else
    {
        file = fopen(buffer, "rb");
        std::cout << "Filename: " << buffer << std::endl;
        if(file == NULL)
        {
            std::cout << "Couldnt open a file." << std::endl;
        }
        else
        {
            while (true)
            {
                if(sendFile(file, buffer, bufferSize))
                {
                    sendto(socketServer, buffer, bufferSize, 0, (SOCKADDR *)&client, len_client);
                    break;
                }

                sendto(socketServer, buffer, bufferSize, 0, (SOCKADDR *)&client, len_client);
                clearBuf(buffer);
            }
            fclose(file);
        }
    }
    closesocket(socketServer);
    WSACleanup();

    system("pause");
    return 0;
}

Client:

#include <winsock2.h>
#include <stdio.h>
#include <iostream>
#include <sstream>
#include <string.h>

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

#define cipherKey 'S'

int const bufferSize = 512;
char buffer[bufferSize];

void clearBuf(char* b)
{
    int i;
    for (i = 0; i < bufferSize; i++)
        b[i] = '\0';
}

char Cipher(char ch)
{
    return ch ^ cipherKey;
}

int recvFile(char* buffer, int s, FILE* file)
{
    int i;
    char ch;
    for (i = 0; i < s; i++)
    {
        ch = buffer[i];
        ch = Cipher(ch);
        if (ch == EOF)
        {
            return 1;
        }
        else
        {
            fprintf(file, "%c", ch);
        }
    }
    return 0;
}

int main()
{
    WSADATA wsaData;
    int wynik_winsock = WSAStartup(MAKEWORD(2,2), &wsaData);

    if(wynik_winsock != 0)
    {
        exit(1);
    }

    SOCKET socketClient;
    socketClient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    if(socketClient == INVALID_SOCKET)
    {
        WSACleanup();
        exit(1);
    }

    char* ipAdd = "127.0.0.1";
    int port = 1234;

    SOCKADDR_IN server;
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ipAdd);

    int serverSizeOf = sizeof(server);

    std::cout << "Name of file to send: ";
    std::cin >> buffer;

    if(sendto(socketClient, buffer, bufferSize, 0, (SOCKADDR *)&server, serverSizeOf) == SOCKET_ERROR)
    {
        closesocket(socketClient);
        WSACleanup();
        exit(1);
    }

    FILE* file;
    file = fopen(buffer, "ab");

    while (true)
    {
        clearBuf(buffer);
        if(recvfrom(socketClient, buffer, bufferSize, 0, (SOCKADDR *)&server, &serverSizeOf) == SOCKET_ERROR)
        {
            closesocket(socketClient);
            WSACleanup();
            exit(1);
        }

        if (recvFile(buffer, bufferSize, file))
        {
            break;
        }
        fclose(file);
    }


    closesocket(socketClient);
    WSACleanup();

    system("pause");
    return 0;

}

To do what I said above, I used the tutorial: C program for file Transfer using UDP (Linux). How can I adapt the code to send other files than .txt only? Thank you in advance.


Solution

  • I've decided to change nearly everything in the original solution I tried to implement. The most important changes are reading file using fread and writing it using fwrite. The file is send in parts of 512 bytes (or less) and those parts are counted in the variable. If the file requested by a client doesn't exist on the server, special information is sent and the file created to save data is deleted. Now the program works as expected even for executables and SHA256 of both files, original and received, are the same. Server:

    //SERVER
    
    #include <winsock2.h>
    #include <stdio.h>
    #include <iostream>
    #include <sstream>
    #include <algorithm>
    #include <ctime>
    
    #pragma comment(lib, "ws2_32.lib")
    
    int main()
    {
        WSADATA wsaData;
        int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
    
        if(winsock_result != 0)
        {
            exit(1);
        }
    
        SOCKET server_socket;
        server_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
        if(server_socket == INVALID_SOCKET)
        {
            WSACleanup();
            exit(1);
        }
    
        char* ip_address = "127.0.0.1";
        int port = 6666;
    
        SOCKADDR_IN server;
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(ip_address);
    
        if(bind(server_socket,(SOCKADDR *)&server, sizeof(server)) == SOCKET_ERROR)
        {
            closesocket(server_socket);
            WSACleanup();
            exit(1);
        }
    
        std::cout << "Waiting for data." << std::endl;
    
        SOCKADDR_IN client;
        int client_sizeof = sizeof(client);
    
        int const buffer_size = 512;
        char buffer[buffer_size];
    
        if(recvfrom(server_socket, buffer, buffer_size, 0,(SOCKADDR *)&client, &client_sizeof) == SOCKET_ERROR)
        {
            closesocket(server_socket);
            WSACleanup();
            exit(1);
        }
        else
        {
            FILE* file;
            file = fopen(buffer, "rb");
            std::cout << "Filename: " << buffer << std::endl;
    
            if(file == NULL)
            {
                std::cout << "Couldn't open the file." << std::endl;
    
                strcpy(buffer, "NOFILE");
                if(sendto(server_socket, buffer, buffer_size, 0,(SOCKADDR *)&client, client_sizeof) == SOCKET_ERROR)
                {
                    fclose(file);
                    closesocket(server_socket);
                    WSACleanup();
                    exit(1);
                }
            }
    
            fseek(file, 0, SEEK_END);
            int file_size = ftell(file);
    
            size_t reading_size;
            int part = 0;
    
            const clock_t begin_time = clock();
    
            while((part * buffer_size) < file_size)
            {
                fseek(file, (part * buffer_size), SEEK_SET);
                reading_size = fread(buffer, 1, buffer_size, file);
    
                if(sendto(server_socket, buffer, reading_size, 0,(SOCKADDR *)&client, client_sizeof) == SOCKET_ERROR)
                {
                    fclose(file);
                    closesocket(server_socket);
                    WSACleanup();
                    exit(1);
                }
                part++;
            }
            std::cout << "Sent " << part << " parts of " << buffer_size << " bytes." << std::endl;
            std::cout << "Time of sending file: " << float( clock () - begin_time ) /  CLOCKS_PER_SEC << " seconds." << std::endl;
    
            strcpy(buffer, "QUIT");
            if(sendto(server_socket, buffer, buffer_size, 0,(SOCKADDR *)&client, client_sizeof) == SOCKET_ERROR)
            {
                fclose(file);
                closesocket(server_socket);
                WSACleanup();
                exit(1);
            }
            fclose(file);
        }
        closesocket(server_socket);
        WSACleanup();
    
        system("pause");
    
        return 0;
    }
    

    Client:

    //CLIENT
    
    #include <winsock2.h>
    #include <stdio.h>
    #include <iostream>
    #include <sstream>
    #include <string.h>
    
    #pragma comment(lib, "ws2_32.lib")
    
    int main()
    {
        WSADATA wsaData;
        int winsock_result = WSAStartup(MAKEWORD(2,2), &wsaData);
    
        if(winsock_result != 0)
        {
            exit(1);
        }
    
        SOCKET client_socket;
        client_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
        if(client_socket == INVALID_SOCKET)
        {
            WSACleanup();
            exit(1);
        }
    
        char* ip_address = "127.0.0.1";
        int port = 6666;
    
        SOCKADDR_IN server;
        server.sin_family = AF_INET;
        server.sin_port = htons(port);
        server.sin_addr.s_addr = inet_addr(ip_address);
    
        int server_sizeof = sizeof(server);
    
        int const buffer_size = 512;
        char buffer[buffer_size];
    
        std::cout << "Name of the requested file: ";
        std::cin >> buffer;
    
        char filename[buffer_size];
        strcpy(filename, buffer);
    
        if(sendto(client_socket, buffer, buffer_size, 0,(SOCKADDR *)&server, server_sizeof) == SOCKET_ERROR)
        {
            closesocket(client_socket);
            WSACleanup();
            exit(1);
        }
    
        FILE* file;
        file = fopen(filename, "wb");
    
        int received_size = 0;
        while(true)
        {
            received_size = recvfrom(client_socket, buffer, buffer_size, 0,(SOCKADDR *)&server, &server_sizeof);
            if(received_size == SOCKET_ERROR)
            {
                fclose(file);
                closesocket(client_socket);
                WSACleanup();
                exit(1);
            }
            if(strcmp(buffer, "NOFILE") == 0)
            {
                std::cout << "The file does not exist on the server." << std::endl;
    
                fclose(file);
                remove(filename);
    
                break;
            }
            else if(strcmp(buffer, "QUIT") == 0)
            {
                std::cout << "Transmission ended by the server." << std::endl;
                break;
            }
            fwrite(buffer, sizeof(char), received_size, file);
        }
        fclose(file);
    
        closesocket(client_socket);
        WSACleanup();
        system("pause");
    
        return 0;
    
    }