Как получить несколько файлов через TCP и сохранить их одновременно в C ++? - PullRequest
1 голос
/ 19 апреля 2020

Я пытаюсь создать приложение, которое будет принимать много подключений от клиентов одновременно, и оно работает для меня, но оно также должно загружать эти файлы одновременно. В этой версии сервера, даже если клиенты подключены одновременно, файлы записываются по одному.

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

Я думал об открытии и закрытии этих файлов для каждого полученного пакета и добавления каждого пакета к ним, но я действительно не представляю, как это сделать. Пример модифицированного кода был предназначен для этого, но он не работает.

Мне запрещено использовать другие процессы и потоки, кроме основного, поэтому я сейчас немного беспомощен. Спасибо за вашу помощь.

1 Ответ

2 голосов
/ 19 апреля 2020

У вас есть база c l oop с select на месте, что хорошо.

accept уже (в основном) неблокирует. Вам просто нужно включить неблокирующий режим на клиентских сокетах, и тогда вы сможете обрабатывать несколько клиентских операций чтения, записи и принятия в вашем основном select l oop.

Вы можете иметь vector клиентских данных c для каждого клиента, каждая запись содержит сокет клиента, открытый файл и любое другое клиентское c состояние.

После accept, вы создаете новую запись клиента и добавляете ее в вектор. Затем в главном l oop вы делаете FD_SET для accept и все клиентские операции чтения и записи. После select вы проверяете наборы FD и обрабатываете их один за другим. Для лучшей производительности вы захотите, чтобы ваш файловый ввод / вывод также находился в неблокирующем режиме, но для этого назначения это, вероятно, излишне.

...