首页 > 解决方案 > 如何在 C++ 中通过 UDP 发送任何文件(图像、exe)?

问题描述

我正在尝试在 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] = '\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;
}

客户:

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

}

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

标签: c++windowsudpwinsock2

解决方案


正如上面评论中所说,您需要一种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);
    }
    ...

推荐阅读