从服务器端关闭主套接字

时间:2017-06-21 20:25:58

标签: c++ sockets server

所以我正在编写一个非常基本的TCP服务器,它只是将客户端发送的消息回送给客户端。我有一个设置,其中服务器在while循环中运行并等待新客户端连接或使用select()方法从一个现有客户端发送消息。 我的问题是: 我如何从服务器端关闭主套接字并基本上关闭服务器。我不是要求确切的代码,而是更多的标准做法。

对于某些情况: 从长远来看,我想象连接到我的服务器的多个客户端,我需要从服务器端正常关闭服务器。

更多上下文:服务器代码。

#define TRUE 1
#define FALSE 0
#define PORT 55555

int main(int argc, char* argv[])
{
    int opt = TRUE;
    int masterSocket, addrlen, newSocket, maxClients = 10, clientSockets[maxClients], 
        activity, valread, sd, maxSd;
    struct sockaddr_in address;
    char buffer[1025];

    fd_set readfds;

    const char *message = "ECHO DAMON v1.0 \r\n";

    /* Initialize array with 0 so it's not read */
    for(int i = 0; i < maxClients ; i++)
    {
        clientSockets[i] = 0;
    }

    /* Create master socket */
    if((masterSocket = socket(AF_INET, SOCK_STREAM,0)) == 0)
    {
        perror("Error when trying to create master socket.\n");
        exit(EXIT_FAILURE);
    }

    /* Set master socket to allow multiple connections */
    if( setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt)) < 0)
    {
        perror("Could not set sockopt");
        exit(EXIT_FAILURE);
    }

    /* Socket type */
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    if( bind(masterSocket, (struct sockaddr*)&address, sizeof(address)) < 0)
    {
        perror("Error, could not bind master socket. \n");
        exit(EXIT_FAILURE);
    }

    printf("Listening on %d. \n", PORT);

    if( listen(masterSocket, 3) < 0)
    {
        perror("Error, could not listen.\n");
        exit(EXIT_FAILURE);
    }

    addrlen = sizeof(address);
    puts("Waiting for connections...\n"); //just a printf variant

    /* END INIT */

    while(TRUE)
    {
        /* Clear socket set */
        FD_ZERO(&readfds);
        /* Add master socket to set */
        FD_SET(masterSocket, &readfds);

        /* Add child sockets to set, will be 0 first iteration */
        for(int i = 0; i < maxClients ; i++)
        {
            sd = clientSockets[i]; // sd = socket descriptor
            /* If valid socket descriptor */
            if(sd > 0)
            {
                FD_SET(sd, &readfds);
            }
            /* Get highest fd number, needed for the select function (later) */
            if(sd > maxSd)
            {
                maxSd = sd;
            }
        }//end for-loop

        /* Wait for activity on any socket */
        activity = select(maxSd +1, &readfds, NULL, NULL, NULL);

        if((activity < 0) && (errno != EINTR))
        {
            printf("Error on select.\n"); //no need for exit.
        }

        /* If the bit for the file descriptor fd is set in the 
        file descriptor set pointed to by fdset */
        /* If something happend in the master socket, its a new connection */
        if(FD_ISSET(masterSocket, &readfds))
        {
            if((newSocket = accept(masterSocket, (struct sockaddr*)&address, (socklen_t*)&addrlen)) < 0)
            {
                perror("Could not accept new socket.\n");
                exit(EXIT_FAILURE);
            }
            /* Print info about connector */
            printf("New connection, socket fd is %d, ip is: %s, port: %d\n", newSocket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));

            if( send(newSocket, message, strlen(message), 0) != strlen(message))
            {
                perror("Could not sent welcome message to new socket.\n");
            }

            puts("Welcome message sen successfully.\n");

            /* Add new socket to array of clients */
            for(int i = 0; i < maxClients; i++)
            {
                if(clientSockets[i] == 0)
                {
                    clientSockets[i] = newSocket;
                    printf("Adding socket to list of client at index %d\n", i);
                    break;
                }
            }
        }//end masterSocket if

        /* Else something happend at client side */
        for(int i = 0; i < maxClients; i++)
        {
            sd = clientSockets[i];

            if(FD_ISSET(sd, &readfds))
            {   /* Read socket, if it was closing, else read value */
                if((valread = read( sd, buffer, 1024)) == 0)
                {
                    getpeername( sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
                    printf("Host disconnected, ip %s, port %d.\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port));

                    close(sd);
                    clientSockets[i] = 0;
                }
            }
            else
            {
                buffer[valread] = '\0';
                send(sd, buffer, strlen(buffer), 0);
            }
        }
    }
    return 0;
}

1 个答案:

答案 0 :(得分:1)

如果您计划通过类似在您的应用中添加break语句来优雅地退出应用程序:

if (exit_condition)
{
    break;
}

您可以将此循环放在主函数的末尾:

  /* close connections gracefully */
    closesocket(masterSocket);
    masterSocket = 0;                 /* optional, see comment below */
    for(int i = 0; i < maxClients; i++)
    {
        if (clientSockets[i] != 0)
        {
            shutdown(clientSockets[i]);
            closesocket(clientSockets[i]);
            clientSockets[i] = 0;     /* optional, except if you also have a SIGINT handler */
        }
    }

如果你想用Ctrl-C来处理退出,你会在这里找到有关如何设置SIGINT处理程序来处理Ctr-C的详细信息: Catch Ctrl-C in C。然后将上面的循环放在你的处理程序中。您的套接字相关变量必须在全局范围内声明,因为您的套接字只能从当前代码中的main()中看到。