简体   繁体   English

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

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

I'm trying to implement transmission of files through UDP protocol in C++.我正在尝试在C++中通过UDP协议实现文件传输。 What I've got is the server which can send a file requested by a client, but it only works for .txt files.我所拥有的是可以发送客户端请求的文件的服务器,但它仅适用于 .txt 文件。 When I try to do the same with image or executable, the transmission corrupts and the file is about 0 KB.当我尝试对图像或可执行文件执行相同操作时,传输损坏并且文件大约为 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) .为了完成我上面所说的,我使用了教程: C program for file Transfer using UDP (Linux) How can I adapt the code to send other files than .txt only?如何修改代码以仅发送 .txt 以外的其他文件? Thank you in advance.先感谢您。

As said in the comments above you need a data type where EOF has a different value from all other character values, char is inadequate in this respect, especially when you are dealing with binary data.正如上面的评论中所说,您需要一种数据类型,其中EOF的值与所有其他字符值不同, char在这方面是不够的,尤其是在处理二进制数据时。

The following change should improve things以下更改应该会有所改善

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

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 .最重要的变化是使用fread读取文件并使用fwrite写入文件。 The file is send in parts of 512 bytes (or less) and those parts are counted in the variable.文件以 512 字节(或更少)的部分发送,这些部分计入变量。 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.现在,即使对于原始文件和接收文件的可执行文件和 SHA256 相同,该程序也能按预期工作。 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;

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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