Jak poprawnie zamknąć połączenie - komunikacja za pomocą socketów

0

Witam
Nie potrafię zamknąć połączenia close() tak aby po zakończeniu procesów gdy wydam polecenie "netstat -a" to port nie był w stanie TIME_WAIT. W necie jakiś gość pisał że najpierw należy zamknąć klienta a potem serwer. Jednak po poszperaniu w necie stwierdzam że to nieprawda, zamykanie możesz rozpocząć od dowolnego endpointu. W każdym razie jak zamkniesz od strony endpointu A to on wysyla FIN i oczekuje ACKa. Potem jak zamykasz od strony endpointu B to on też wysyła FIN i oczekuje ACKa. Troche bezsensu bo przecież jak ma mu węzeł A odpowiedzieć jak już się zamknął. Co do SO_LINGER, ustawianie tutaj czekania na np. 10 [s] zamiast domyslnie 4 minut piszą w necie że jest to workaround i powinno sie taką implementację zrobić by połączenie poprawnie się zamknęło. Ja tego nie potrafię

void freeSockets(int sockfd, int sockfdClient=-1)
{
    close(sockfdClient);
    close(sockfd);
}

#ifdef LINUX_USED

short unsigned int SERVER_PORT = 3000;

void sockStreamAfInetExample()
{
    pid_t pid = fork();
    if(!pid) //Child process Server
    {
        socklen_t len;
        int sockfdClient = -1;
        sockaddr_in clientaddr;
        int sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(sockfd == -1)
        {
            perror("Unable to create AF_INET socket");
            exit(EXIT_FAILURE);
        }
        else
            std::cout << "Server::socket created succesfull on sock fd = " << sockfd << endl;

        int on = 1;
        if(setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) == -1)
        {
            perror("setsockopt(SO_REUSEADDR) failed");
            exit(EXIT_FAILURE);
        }


        //The typical reason to set a SO_LINGER timeout of zero is to avoid large numbers of connections sitting in the TIME_WAIT state,
        // tying up all the available resources on a server
        /*linger lin;
        lin.l_onoff=1;
        lin.l_linger=10; // wait 10 seconds after closesocket
        //how that socket should behave when data is queued to be sent and the closesocket function is called on the socket.
        if(setsockopt(sockfd, SOL_SOCKET, SO_LINGER, (void*)(&lin), sizeof(lin)) == -1) 
        {
            perror("setsockopt(SO_LINGER) failed");
            exit(EXIT_FAILURE);
        }*/

        sockaddr_in serveraddr = {};
        serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); //all available interfaces //inet_addr("127.0.0.1"); //htonl(INADDR_LOOPBACK);
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = htons(SERVER_PORT);
        if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(sockaddr_in)) == -1)
        {
            perror("Server bind failed");
            freeSockets(sockfd, sockfdClient);
            exit(EXIT_FAILURE);
        }

        if (listen(sockfd, 128) == -1)//128 queue size (max number of client connections in pending state on acceptance)
        {
            perror("Server listen failed");
            freeSockets(sockfd, sockfdClient);
            exit(EXIT_FAILURE);
        }

        while (1)
        {
            cout << "Server ready to accept next connection" << endl;
            //accept client connection and remove from queue
            len = sizeof(sockaddr_in);
            sockfdClient = accept(sockfd, (struct sockaddr *)&clientaddr, &len);
            if (sockfdClient == -1)
            {
                perror("Cannot accept socket");
                freeSockets(sockfd, sockfdClient);
                exit(EXIT_FAILURE);
            }

            cout << "Connection accepted from client" << endl;

            //Receive data from client
            string buff(1000, '\0'); //goto cannot cross initialized variables
            int bytes = read(sockfdClient, &buff[0], buff.size());
            if (bytes == -1)
            {
                perror("recv failed");
                freeSockets(sockfd, sockfdClient);
                exit(EXIT_FAILURE);
            }

            cout << "Server receives " << bytes << " bytes: " << &buff[0] << endl;

            break;
        }

        this_thread::sleep_for(chrono::seconds(2));
        freeSockets(sockfd, sockfdClient);
        cout << "Server closed" << endl;
        this_thread::sleep_for(chrono::seconds(2)); //maybe wait some time
        exit(0);
    }
    else //Parent process Client
    {
        int sockfd = -1;

        sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (sockfd == -1)
        {
            perror("Cannot create client socket");
            exit(EXIT_FAILURE);
        }

        this_thread::sleep_for(chrono::seconds(1));
        sockaddr_in serveraddr = {};
        serveraddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); //htonl(INADDR_ANY); //all available interfaces //inet_addr("127.0.0.1"); //htonl(INADDR_LOOPBACK);
        serveraddr.sin_family = AF_INET;
        serveraddr.sin_port = htons(SERVER_PORT);
        if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(sockaddr_in)) == -1)
        {
            perror("Connect failed");
            freeSockets(sockfd);
            exit(0);
        }
        else
            std::cout << "Client::connect succesfull on sock fd = " << sockfd << endl;

        std::array<string, 10> table =
        {
            "Msg priority 0",
            "Msg priority 1",
            "Msg priority 2",
            "Msg priority 3",
            "Msg priority 4",
            "Msg priority 5",
            "Msg priority 6",
            "Msg priority 7",
            "Msg priority 8",
            "Msg priority 9"
        };
        for (int i = 0; i<1; ++i) //the message will be delivered as one without boundaries
        {
            //this_thread::sleep_for(chrono::seconds(5)); //each time close, socket creation and connect is required
            int bytes = write(sockfd, table.at(i).c_str(), table.at(i).size());
            if (bytes == -1)
            {
                perror("Client write error");
                freeSockets(sockfd);
                exit(0);
            }
            cout << "Client sent " << bytes << " bytes: " << table.at(i).c_str() << endl;
        }

        freeSockets(sockfd);
        cout << "Client closed" << endl;

        int status = 0;
        wait(&status); 
    }
}
0

A spróbowałeś shutdown zamiast close?

0

Tak spróbowałem przed closed po stronie klienta i po stronie serwera. Dałem jako argument 1 czyli 1 endpoint informuje 2-gi endpoint że już nic nie bedzie wysyłal. I to też nie pomogło.

0

TIME_WAIT jest jak najbardziej poprawnym stanem. Tego typu ustawienia zmienia się w kernelu.

1 użytkowników online, w tym zalogowanych: 0, gości: 1