C++如何通过TCP接收多个文件并同时保存?

How to receive multiple files through TCP and save them at the same time in C++?

我正在尝试创建一个可以同时接受来自客户端的多个连接的应用程序,它适用于我,但它也应该同时下载这些文件。在这个版本的服务器中,即使客户端同时连接,文件也是一个一个写入的。

#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_STREAM, IPPROTO_TCP);

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

    int const max_clients = 100;
    int client_socket[max_clients];

    for (int i = 0; i < max_clients; i++)
    {
        client_socket[i] = 0;
    }

    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 opt = TRUE;
    if( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
    {
        closesocket(server_socket);
        WSACleanup();
        exit(1);
    }


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

    if(listen(server_socket, 5) == SOCKET_ERROR)
    {
        std::cout << "Nasluchiwanie portu nieudane." << std::endl;
    }
    else
    {
        std::cout << "Nasluchiwanie portu " << port << " udane." << std::endl << std::endl;
    }

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

    int max_socket_descriptor, socket_descriptor;
    int downloaded_files = 1;

    fd_set readfds;

    while(true)
    {
        FD_ZERO(&readfds);
        FD_SET(server_socket, &readfds);
        max_socket_descriptor = server_socket;

        for (int i = 0 ; i < max_clients ; i++)
        {
            socket_descriptor = client_socket[i];
            if(socket_descriptor > 0)
            {
                FD_SET( socket_descriptor, &readfds);
            }

            if(socket_descriptor > max_socket_descriptor)
            {
                max_socket_descriptor = socket_descriptor;
            }
        }

        if ((select( max_socket_descriptor + 1, &readfds, NULL, NULL, NULL) < 0) && (errno != EINTR))
        {
            std::cout << "Blad funkcji select." << std::endl;
        }

        if (FD_ISSET(server_socket, &readfds))
        {
            int new_sockfd;
            if ((new_sockfd = accept(server_socket,(SOCKADDR *)&server, &server_sizeof)) == SOCKET_ERROR)
            {
                std::cout << "Otrzymanie deskryptora nieudane." << std::endl;
            }
            else
            {
                for (int i = 0; i < max_clients; i++)
                {
                    if( client_socket[i] == 0 )
                    {
                        client_socket[i] = new_sockfd;
                        std::cout << "Dodawanie do listy socketow jako numer " << i << std::endl;

                        break;
                    }
                }
            }
        }

        for (int i = 0; i < max_clients; i++)
        {
            socket_descriptor = client_socket[i];

            if (FD_ISSET( socket_descriptor, &readfds))
            {
                struct sockaddr_in client_address;

                char filename[buffer_size];
                std::stringstream ip_filename;
                ip_filename << "plik" << downloaded_files << "_" << inet_ntoa(client_address.sin_addr);
                strcpy(filename, ip_filename.str().c_str());

                std::cout << "Nazwa pliku (IP klienta): " << filename << std::endl;

                FILE* file;
                file = fopen(filename, "wb");

                const clock_t begin_time = clock();
                int received_size;

                do
                {
                    memset(buffer, 0, buffer_size);
                    received_size = recv(socket_descriptor, buffer, buffer_size, 0);

                    if (received_size == 0 || received_size == -1)
                    {
                        break;
                    }
                    fwrite(buffer, sizeof(char), received_size, file);
                }
                while (received_size != 0);

                fclose(file);
                std::cout << "Czas wysylania pliku: " << float( clock () - begin_time ) /  CLOCKS_PER_SEC << " sekund." << std::endl << std::endl;

                closesocket(socket_descriptor);
                client_socket[i] = 0;

                downloaded_files++;
            }
        }
    }

    closesocket(server_socket);
    WSACleanup();
    system("pause");

    return 0;
}

我应该怎么做才能让他们同时写很多?我已经尝试对上面的代码进行多次修改,但每次都无法获得想要的结果。 例如:

#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_STREAM, IPPROTO_TCP);

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

    int const max_clients = 100;
    int client_socket[max_clients];

    for (int i = 0; i < max_clients; i++)
    {
        client_socket[i] = 0;
    }

    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 opt = TRUE;
    if( setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
    {
        closesocket(server_socket);
        WSACleanup();
        exit(1);
    }


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

    if(listen(server_socket, 5) == SOCKET_ERROR)
    {
        std::cout << "Nasluchiwanie portu nieudane." << std::endl;
    }
    else
    {
        std::cout << "Nasluchiwanie portu " << port << " udane." << std::endl << std::endl;
    }

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

    int max_socket_descriptor;
    int downloaded_files = 1;

    fd_set readfds;
    FD_ZERO(&readfds);
    FD_SET(server_socket, &readfds);
    max_socket_descriptor = server_socket;

    while(true)
    {
        if ((select( max_socket_descriptor + 1, &readfds, NULL, NULL, NULL) < 0) && (errno != EINTR))
        {
            std::cout << "Blad funkcji select." << std::endl;
        }

        for (int i = 0 ; i < max_clients ; i++)
        {
            if(FD_ISSET(server_socket, &readfds))
            {
                int new_sockfd;
                if ((new_sockfd = accept(server_socket,(SOCKADDR *)&server, &server_sizeof)) == SOCKET_ERROR)
                {
                    std::cout << "Otrzymanie deskryptora nieudane." << std::endl;
                }
                else
                {
                    for (int i = 0; i < max_clients; i++)
                    {
                        if( client_socket[i] == 0 )
                        {
                            client_socket[i] = new_sockfd;
                            FD_SET( client_socket[i], &readfds);

                            if(client_socket[i] > max_socket_descriptor)
                            {
                                max_socket_descriptor = client_socket[i];
                            }

                            std::cout << "Dodawanie do listy socketow jako numer " << i << std::endl;

                            break;
                        }
                    }
                }
            }

            if(FD_ISSET(client_socket[i], &readfds))
            {
                struct sockaddr_in client_address;

                char filename[buffer_size];
                std::stringstream ip_filename;
                ip_filename << "plik" << downloaded_files << "_" << inet_ntoa(client_address.sin_addr);
                strcpy(filename, ip_filename.str().c_str());

                std::cout << "Nazwa pliku (IP klienta): " << filename << std::endl;

                FILE* file;

                memset(buffer, 0, buffer_size);
                int received_size;

                received_size = recv(client_socket[i], buffer, buffer_size, 0);

                if (received_size <= 0)
                {
                    closesocket(client_socket[i]);
                    FD_CLR(client_socket[i], &readfds);
                    client_socket[i] = 0;
                    break;
                }
                else
                {
                    file = fopen(filename, "ab");
                    fwrite(buffer, sizeof(char), received_size, file);
                    fclose(file);
                }

                downloaded_files++;
            }

        }
    }

    closesocket(server_socket);
    WSACleanup();
    system("pause");

    return 0;
}

我考虑过在每个收到的数据包中打开和关闭这些文件并将每个数据包附加到它们,但我真的不知道该怎么做。修改代码的例子本来是想做的,结果不行

除了主进程和线程,我被禁止使用其他进程和线程,所以我现在有点无奈。感谢您的帮助。

你有 select 的基本循环,这很好。

accept 已经(大部分)是非阻塞的。您只需要在客户端套接字上打开 non-blocking mode,然后您就可以在主 select 循环中处理多个客户端读取、写入和接受。

每个客户端可以有一个 vector 特定于客户端的数据,每个条目都包含客户端套接字、打开的文件和任何其他特定于客户端的状态。

accept 之后,您创建一个新的客户端条目并将其添加到向量中。然后在主循环中,您为 accept 执行 FD_SET 以及所有客户端的读写操作。在 select 之后,您检查 FD 集合并逐个处理它们。为了获得最佳性能,您需要文件 I/O 也处于非阻塞模式,但对于此分配,这可能有点矫枉过正。