Данные, отправленные с Tcp Client, принимаются очень медленно - PullRequest
0 голосов
/ 10 марта 2020

Система, которую я должен сделать, имеет один tcp-сервер и около 1000 tcp-клиентов. 1000 клиентов будут отправлять данные на tcp сервер каждую секунду. Чтобы смоделировать эту ситуацию, сначала я подключился к tcp-серверу с 50 сокетами из одного p c с кодом ниже.

int main() {

    const char *hello = "Hello from client";

    struct sockaddr_in serv_addr;
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(8080);
    serv_addr.sin_addr.s_addr = inet_addr("192.168.1.39");

    vector<int> vec;


    for ( uint8_t i = 0; i < 50; i++ ) {

        int sock =  socket(AF_INET, SOCK_STREAM, 0);
        if ( sock < 0 ) {
            cout << "... Cant Allocated Socket\n";
            return -1;
        }

        if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
        {
            cout << "... Connection Failed \n";
            return -1;
        }



        vec.push_back(sock);
    }

    for ( uint8_t i = 0; i < vec.size(); i++ ) {
        send(vec[i], hello, strlen(hello), 0);
        cout << "Message Send\n";
    }


    for ( uint8_t i = 0; i < vec.size(); i++ ) {
        shutdown(vec[i], 0);
        close(vec[i]);
    }
    return 0;
}

После подключения tcp-клиентов к tcp-серверу они отправляют данные tcp сервер и закройте сокет. Из терминала видно, что клиенты tcp могут отправлять пакеты без ожидания (менее 10 мс)

Приведенный выше код клиента tcp может успешно работать и успешно отправлять данные на tcp-сервер. Я показываю данные от tcp-клиента с кодом tcp-сервера ниже.

#define _DEF_TCP_SERVER_PORT                                        8080
#define _DEF_TCP_SERVER_MAX_QUEUE_LISTEN                            12

bool finish_app = false;

struct TcpClient {
    int clientSocket;
    struct in_addr clientAddr;
};
vector<TcpClient> TcpClients;


struct _ServiceTcpServer {
    bool enable;
    int sock;
    uint16_t connectedClient;
    uint32_t sockLen;
    sockaddr_in tcpServerAddr;
    sockaddr_in remoteAddr;
};

struct _ServiceTcpServer _serviceTcpServer;


void init_tcp_server_socket() {

    _serviceTcpServer.tcpServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
    _serviceTcpServer.tcpServerAddr.sin_family = AF_INET;
    _serviceTcpServer.tcpServerAddr.sin_port = htons(_DEF_TCP_SERVER_PORT);
    _serviceTcpServer.sockLen = sizeof(_serviceTcpServer.remoteAddr);

    int flag = 1;

    for ( ;; ) {
        _serviceTcpServer.sock = socket(AF_INET, SOCK_STREAM, 0);
        if ( _serviceTcpServer.sock < 0 ) {
            cout << "... Failed to allocate socket.\n";
            this_thread::sleep_for(chrono::seconds(1));
            continue;
        }

        if ( setsockopt(_serviceTcpServer.sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int)) ) {
            cout << "... Set SockOpt failed.\n";
            close(_serviceTcpServer.sock);
            this_thread::sleep_for(chrono::seconds(1));
            continue;
        }

        if( bind(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.tcpServerAddr, sizeof(_serviceTcpServer.tcpServerAddr)) == -1 ) {
            cout << "... Socket bind failed.\n";
            close(_serviceTcpServer.sock);
            this_thread::sleep_for(chrono::seconds(1));
            continue;
        }

        if ( listen(_serviceTcpServer.sock, _DEF_TCP_SERVER_MAX_QUEUE_LISTEN) != 0 ) {
            cout << "... Socket listen failed.\n";
            close(_serviceTcpServer.sock);
            this_thread::sleep_for(chrono::seconds(1));
            continue;
        }

        break;

    }


    cout << "Socket init done \n";
}

void tcp_user_accept_task() {

    while ( finish_app == false ) {
        int temp_sck = -1;
        temp_sck = accept(_serviceTcpServer.sock, (sockaddr *) &_serviceTcpServer.remoteAddr, &_serviceTcpServer.sockLen);
        if ( temp_sck == -1 ) {
            this_thread::sleep_for(chrono::seconds(2));
            continue;
        }
        TcpClient tcpClient;
        tcpClient.clientAddr = _serviceTcpServer.remoteAddr.sin_addr;
        tcpClient.clientSocket = temp_sck;
        TcpClients.push_back( tcpClient );
        cout << "... New connection request: " << temp_sck << endl;
        ++_serviceTcpServer.connectedClient;
        this_thread::sleep_for(chrono::milliseconds(50));
    }
}


uint8_t temp_recv[100];
void tcp_server_run() {

    while ( finish_app == false ) {

        for(uint16_t i = 0 ; i < _serviceTcpServer.connectedClient; i++ ) {
            int temp_cs = TcpClients[i].clientSocket;
            fcntl(temp_cs, F_SETFL, O_NONBLOCK);
            int temp_recvLen  = recv(temp_cs, temp_recv, 20, 0);
            if( temp_recvLen > 0 ) {
                time_t _time = chrono::system_clock::to_time_t(chrono::system_clock::now());
                cout << "Message Received At:" << ctime(&_time) << "   :";
                cout << temp_recv << endl;
                break;
            } else {
                this_thread::sleep_for(chrono::milliseconds(10));
            }
        }

        if ( temp_recv[0] == 'q' ) {
            finish_app = true;
        }
    }

    close(_serviceTcpServer.sock);
}




int main() {

    thread init_thread(init_tcp_server_socket);
    init_thread.join();
    thread accept_thread(tcp_user_accept_task);
    thread run_thread(tcp_server_run);
    accept_thread.join();
    run_thread.join();

    return 0;
}

Но проблема в том, что за 3-4 секунды получено 3-4 пакета, как на экране.

enter image description here

Примечание: Когда код this_thread::sleep_for(chrono::milliseconds(10)); прокомментировал, проблема была решена. Но поскольку процессор не спит, процессор работает на все 100%. Когда клиент был принят, я добавил 10-секундный тайм-аут к клиенту recv с кодом ниже и комментарием и fcntl(temp_cs, F_SETFL, O_NONBLOCK);

struct timeval _timeval;
_timeval.tv_sec = 0;
_timeval.tv_usec = 10;
setsockopt(tcpClient.clientSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*) &_timeval, sizeof(_timeval));

Проблема продолжается, как в "this_thread :: sleep_for".

1 Ответ

1 голос
/ 10 марта 2020

Вы должны получать сокет одновременно, а не запрашивать каждый сокет и спать в течение 10 мс каждый раз, когда данные еще не готовы.

Правильный способ сделать это зависит от платформы

  • posix - выберите

  • linux - опрос, epoll, io_submit

  • windows - порты завершения ввода / вывода

Как правило, выбор, который является стандартом posix, будет достаточным для ваших нужд. Если вы хотите мультиплатформенность, вам также может понадобиться использовать сторонние библиотеки, такие как libevent и libev , которые уже упаковывают эти платформенные вызовы для вас.

Happy Coding!

...