Механизм понимания проблем функции выбора в программировании сокетов c ++ - PullRequest
1 голос
/ 24 июня 2019

Я беру курс общения, и они дали нам c ++ tcp коды для клиента и сервера, где клиент спрашивает, сколько времени, а сервер отвечает.Сервер может поддерживать несколько соединений с несколькими клиентами.Сервер содержит структуру данных с именем socketState, которая содержит данные о конкретном сокете, поэтому они будут сохранены даже после того, как fd_set очистит свою память:

struct SocketState
{
    SOCKET id;          // Socket handle
    int recv;           // Receiving?
    int send;           // Sending?
    int sendSubType;    // Sending sub-type
    char buffer[128];
    int len;
}

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

Код сервера:

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
using namespace std;
#include <winsock2.h>
#include <string.h>
#include <time.h>
# include <ctime>
#include <ws2tcpip.h>
#include <assert.h>
#pragma comment(lib, "Ws2_32.lib")

struct SocketState
{
    SOCKET id;          // Socket handle
    int recv;           // Receiving?
    int send;           // Sending?
    int sendSubType;    // Sending sub-type
    char buffer[128];
    int len;
};

const int TIME_PORT = 27015;
const int MAX_SOCKETS = 60;
const int EMPTY = 0;
const int LISTEN = 1;
const int RECEIVE = 2;
const int IDLE = 3;
const int SEND = 4;
const int SEND_TIME = 1;
const int SEND_SECONDS = 2;

bool addSocket(SOCKET id, int what);
void removeSocket(int index);
void acceptConnection(int index);
void receiveMessage(int index);
void sendMessage(int index);

struct SocketState sockets[MAX_SOCKETS] = { 0 };
int socketsCount = 0;


void main()
{
    WSAData wsaData;
    if (NO_ERROR != WSAStartup(MAKEWORD(2, 2), &wsaData))
    {
        cout << "Time Server: Error at WSAStartup()\n";
        return;
    }

    SOCKET listenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);


    if (INVALID_SOCKET == listenSocket)
    {
        cout << "Time Server: Error at socket(): " << WSAGetLastError() << endl;
        WSACleanup();
        return;
    }

    sockaddr_in serverService;

    serverService.sin_family = AF_INET;
    serverService.sin_addr.s_addr = INADDR_ANY;
    serverService.sin_port = htons(TIME_PORT);

    if (SOCKET_ERROR == bind(listenSocket, (SOCKADDR*)& serverService, sizeof(serverService)))
    {
        cout << "Time Server: Error at bind(): " << WSAGetLastError() << endl;
        closesocket(listenSocket);
        WSACleanup();
        return;
    }

    if (SOCKET_ERROR == listen(listenSocket, 5))
    {
        cout << "Time Server: Error at listen(): " << WSAGetLastError() << endl;
        closesocket(listenSocket);
        WSACleanup();
        return;
    }
    addSocket(listenSocket, LISTEN);



    while (true)
    {
        fd_set waitRecv;
        FD_ZERO(&waitRecv);
        for (int i = 0; i < MAX_SOCKETS; i++)
        {
            if ((sockets[i].recv == LISTEN) || (sockets[i].recv == RECEIVE))
                FD_SET(sockets[i].id, &waitRecv);
        }


        fd_set waitSend;
        FD_ZERO(&waitSend);
        for (int i = 0; i < MAX_SOCKETS; i++)
        {
            if (sockets[i].send == SEND)
                FD_SET(sockets[i].id, &waitSend);
        }

        struct timeval timeout;
        timeout.tv_sec = 15;
        timeout.tv_usec = 0;
        int nfd = select(0, &waitRecv, &waitSend, NULL, NULL);
        if (nfd == SOCKET_ERROR)
        {
            cout << "Time Server: Error at select(): " << WSAGetLastError() << endl;
            WSACleanup();
            return;
        }
        for (int i = 0; i < MAX_SOCKETS && nfd > 0; i++)
        {
            if (FD_ISSET(sockets[i].id, &waitRecv))
            {
                nfd--;
                switch (sockets[i].recv)
                {
                case LISTEN:
                    acceptConnection(i);
                    break;

                case RECEIVE:
                    receiveMessage(i);
                    break;
                }
            }
        }

        for (int i = 0; i < MAX_SOCKETS && nfd > 0; i++)
        {
            if (FD_ISSET(sockets[i].id, &waitSend))
            {
                nfd--;
                switch (sockets[i].send)
                {
                case SEND:
                    sendMessage(i);
                    break;
                }
            }
        }

    }

    // Closing connections and Winsock.
    cout << "Time Server: Closing Connection.\n";
    closesocket(listenSocket);
    WSACleanup();
}

bool addSocket(SOCKET id, int what)
{
    for (int i = 0; i < MAX_SOCKETS; i++)
    {
        if (sockets[i].recv == EMPTY)
        {
            sockets[i].id = id;
            sockets[i].recv = what;
            sockets[i].send = IDLE;
            sockets[i].len = 0;
            socketsCount++;
            return (true);
        }
    }
    return (false);
}

void removeSocket(int index)
{
    sockets[index].recv = EMPTY;
    sockets[index].send = EMPTY;
    socketsCount--;
}

void acceptConnection(int index)
{
    SOCKET id = sockets[index].id;
    struct sockaddr_in from;        // Address of sending partner
    int fromLen = sizeof(from);

    SOCKET msgSocket = accept(id, (struct sockaddr*) & from, &fromLen);
    if (INVALID_SOCKET == msgSocket)
    {
        cout << "Time Server: Error at accept(): " << WSAGetLastError() << endl;
        return;
    }
    cout << "Time Server: Client " << inet_ntoa(from.sin_addr) << ":" << ntohs(from.sin_port) << " is connected." << endl;

    //
    // Set the socket to be in non-blocking mode.
    //
    unsigned long flag = 1;
    if (ioctlsocket(msgSocket, FIONBIO, &flag) != 0)
    {
        cout << "Time Server: Error at ioctlsocket(): " << WSAGetLastError() << endl;
    }

    if (addSocket(msgSocket, RECEIVE) == false)
    {
        cout << "\t\tToo many connections, dropped!\n";
        closesocket(id);
    }
    return;
}

void receiveMessage(int index)
{
    SOCKET msgSocket = sockets[index].id;

    int len = sockets[index].len;
    int bytesRecv = recv(msgSocket, &sockets[index].buffer[len], sizeof(sockets[index].buffer) - len, 0);

    if (SOCKET_ERROR == bytesRecv)
    {
        cout << "Time Server: Error at recv(): " << WSAGetLastError() << endl;
        closesocket(msgSocket);
        removeSocket(index);
        return;
    }
    if (bytesRecv == 0)
    {
        closesocket(msgSocket);
        removeSocket(index);
        return;
    }
    else
    {
        sockets[index].buffer[len + bytesRecv] = '\0'; //add the null-terminating to make it a string
        cout << "Time Server: Recieved: " << bytesRecv << " bytes of \"" << &sockets[index].buffer[len] << "\" message.\n";

        sockets[index].len += bytesRecv;

        if (sockets[index].len > 0)
        {
            if (strncmp(sockets[index].buffer, "TimeString", 10) == 0)
            {
                sockets[index].send = SEND;
                sockets[index].sendSubType = SEND_TIME;
                memcpy(sockets[index].buffer, &sockets[index].buffer[10], sockets[index].len - 10);
                sockets[index].len -= 10;
                return;
            }
            else if (strncmp(sockets[index].buffer, "SecondsSince1970", 16) == 0)
            {
                sockets[index].send = SEND;
                sockets[index].sendSubType = SEND_SECONDS;
                memcpy(sockets[index].buffer, &sockets[index].buffer[16], sockets[index].len - 16);
                sockets[index].len -= 16;
                return;
            }
            else if (strncmp(sockets[index].buffer, "Exit", 4) == 0)
            {
                closesocket(msgSocket);
                removeSocket(index);
                return;
            }
        }
    }

}

void sendMessage(int index)
{
    int bytesSent = 0;
    char sendBuff[255];

    SOCKET msgSocket = sockets[index].id;
    if (sockets[index].sendSubType == SEND_TIME)
    {
        // Answer client's request by the current time string.

        // Get the current time.
        time_t timer;
        time(&timer);
        // Parse the current time to printable string.
        strcpy(sendBuff, ctime(&timer));
        sendBuff[strlen(sendBuff) - 1] = 0; //to remove the new-line from the created string
    }
    else if (sockets[index].sendSubType == SEND_SECONDS)
    {
        // Answer client's request by the current time in seconds.

        // Get the current time.
        time_t timer;
        time(&timer);
        // Convert the number to string.
        _itoa((int)timer, sendBuff, 10);
    }
    bytesSent = send(msgSocket, sendBuff, (int)strlen(sendBuff), 0);
    if (SOCKET_ERROR == bytesSent)
    {
        cout << "Time Server: Error at send(): " << WSAGetLastError() << endl;
        return;
    }

    cout << "Time Server: Sent: " << bytesSent << "\\" << strlen(sendBuff) << " bytes of \"" << sendBuff << "\" message.\n";

    sockets[index].send = IDLE;
}

И это мой код клиента:

#define  _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
using namespace std;
#include <winsock2.h> 
#include <string.h>
#include <iomanip>
#include <time.h>
# include <ctime>
#include <ws2tcpip.h>
#include <assert.h>
#pragma comment(lib, "Ws2_32.lib")

const int TIME_PORT = 27015;


void main()
{

    // Initialize Winsock (Windows Sockets).

    WSAData wsaData;
    if (NO_ERROR != WSAStartup(MAKEWORD(2, 2), &wsaData))
    {
        cout << "Time Client: Error at WSAStartup()\n";
        return;
    }

    // Client side:
    // Create a socket and connect to an internet address.

    SOCKET connSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (INVALID_SOCKET == connSocket)
    {
        cout << "Time Client: Error at socket(): " << WSAGetLastError() << endl;
        WSACleanup();
        return;
    }

    sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port = htons(TIME_PORT);

    if (SOCKET_ERROR == connect(connSocket, (SOCKADDR*)& server, sizeof(server)))
    {
        cout << "Time Client: Error at connect(): " << WSAGetLastError() << endl;
        closesocket(connSocket);
        WSACleanup();
        return;
    }


    // Send and receive data.

    int bytesSent = 0;
    int bytesRecv = 0;
    char sendBuff[255];
    char recvBuff[255];
    int option = 0;

    while (option != 3)
    {
        cout << "\nPlease choose an option:\n";
        cout << "1 - Get current time.\n";
        cout << "2 - Get current time in seconds.\n";
        cout << "3 - Exit.\n";
        cout << ">> ";

        cin >> option;

        if (option == 1)
            strcpy(sendBuff, "TimeString");
        else if (option == 2)
            strcpy(sendBuff, "SecondsSince1970");
        else if (option == 3)
            strcpy(sendBuff, "Exit");

        bytesSent = send(connSocket, sendBuff, (int)strlen(sendBuff), 0);
        if (SOCKET_ERROR == bytesSent)
        {
            cout << "Time Client: Error at send(): " << WSAGetLastError() << endl;
            closesocket(connSocket);
            WSACleanup();
            return;
        }
        cout << "Time Client: Sent: " << bytesSent << "/" << strlen(sendBuff) << " bytes of \"" << sendBuff << "\" message.\n";

        // Gets the server's answer for options 1 and 2.
        if (option == 1 || option == 2)
        {
            bytesRecv = recv(connSocket, recvBuff, 255, 0);
            if (SOCKET_ERROR == bytesRecv)
            {
                cout << "Time Client: Error at recv(): " << WSAGetLastError() << endl;
                closesocket(connSocket);
                WSACleanup();
                return;
            }
            if (bytesRecv == 0)
            {
                cout << "Server closed the connection\n";
                return;
            }

            recvBuff[bytesRecv] = '\0'; //add the null-terminating to make it a string
            cout << "Time Client: Recieved: " << bytesRecv << " bytes of \"" << recvBuff << "\" message.\n";
        }
        else if (option == 3)
        {
            // Closing connections and Winsock.
            cout << "Time Client: Closing Connection.\n";
            closesocket(connSocket);
            WSACleanup();
        }
    }
}

Теперь моя главная проблема заключается в том, что всякий раз, когда я отправляю сообщение от клиента или клиентов через командную строку Windows, или из Putty, илиЕсли исходить из кода клиента Visiual Studio, функция выбора продолжает блокироваться (за исключением случаев, когда я использую тайм-аут, но затем возвращает 0), и я не могу понять, почему.Я предполагаю, что это основная проблема понимания механизма выбора, хотя я читал об этой функции в нескольких источниках.Также, когда я пытаюсь подключиться к серверу с помощью команды «telnet port number» на cmd, я просто получаю пустой черный экран.

В документации выбора указано, что первый аргумент игнорируется.Но, насколько я знаю, он представляет количество дескрипторов сокетов, которые fdset может отслеживать.Тогда как, несмотря на это, я могу получить доступ к этому атрибуту?Или, может быть, мне не нужно, и есть другое решение для состояния блокировки?

Если кто-то может помочь, я был бы благодарен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...