如何在 C++ 中通过 UDP 发送任何文件(图像、exe)?

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

我正在尝试在C++中通过UDP协议实现文件传输。 我得到的是可以发送客户端请求的文件的服务器,但它只适用于 .txt 文件。当我尝试对图像或可执行文件执行相同操作时,传输损坏并且文件大约为 0 KB。

服务器:

#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] = '[=10=]';
}

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

客户:

#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] = '[=11=]';
}

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;

}

为了完成我上面所说的,我使用了教程:C program for file Transfer using UDP (Linux)。 如何调整代码以仅发送 .txt 以外的其他文件?提前谢谢你。

如上面的评论所述,您需要一种数据类型,其中 EOF 具有与所有其他字符值不同的值,char 在这方面是不够的,尤其是当您处理二进制时数据.

以下更改应该会有所改善

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

我决定更改我尝试实施的原始解决方案中的几乎所有内容。最重要的变化是使用 fread 读取文件并使用 fwrite 写入文件。 文件以 512 字节(或更少)的部分发送,这些部分计入变量中。 如果客户端请求的文件在服务器上不存在,则发送特殊信息并删除为保存数据而创建的文件。 现在,该程序即使对于可执行文件也能按预期工作,并且两个文件(原始文件和接收到的文件)的 SHA256 相同。 服务器:

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

#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;

}