Winsock: recv () на сервере блокируется, но клиент уже прошел мимо send () - PullRequest
1 голос
/ 12 октября 2019

Я работаю над проектом для школы и столкнулся со следующей проблемой. Мой сервер блокируется на recv (), несмотря на то, что мой клиент уже отправил свое полное сообщение.

Это то, что я хочу, чтобы произошло:

Server        Client
recv() <----  send()
send() ---->  recv()

Это то, что происходит:

Server        Client
recv() <----  send()
recv() -----  recv()

Немного предыстории

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

Код

Вот код, который я считаю уместным:

Клиент:

    bytesSent = 0;
    retVal = send(sock, phrase, msgLen, 0);
    bytesSent = retVal;
    while (bytesSent < msgLen) {
        retVal = send(sock, phrase + bytesSent, msgLen - bytesSent, 0);
        if (retVal == SOCKET_ERROR) {
            DisplayFatalErr("send() function failed.");
            exit(1);
        }
        bytesSent += retVal;
        // May need to re-call send in order to keep sending the data.

    }

...

    bytesRead = 0;
    while (bytesRead < msgLen) {
        retVal = recv(sock, rcvBuffer, RCVBUFSIZ - 1, 0);
        if (retVal <= 0) {
            DisplayFatalErr("recv() function failed.");
            exit(1);
        }
        bytesRead += retVal;
        for (int i = 0; i < retVal; i++) {
            printf("%c", rcvBuffer[i]);
        }
    }

Сервер:

    char* rcvBuffer[RCVBUFSIZ]; // RCVBUFSIZ = 50
    char* msg = "";
    int bytesRead = 0;
    do {
        if ((bytesRead = recv(clientSock, rcvBuffer, RCVBUFSIZ - 1, 0)) == 0) {
            break;
        }
        if (bytesRead < 0) {
            return -1;
        }
        char* msgConcatenated;
        int msgLen = strlen(msg);
        msgConcatenated = malloc(msgLen + bytesRead);
        if (msgConcatenated != NULL) {
            int newMsgLen = strlen(msgConcatenated);
            strncpy_s(msgConcatenated, newMsgLen, msg, msgLen);
            strncat_s(msgConcatenated, newMsgLen, rcvBuffer, bytesRead);
            msg = msgConcatenated;
        }
    } while (bytesRead != 0);

Дайте мне знать, если мне потребуется предоставить дополнительную информацию.

1 Ответ

0 голосов
/ 12 октября 2019

При использовании TCP , чтобы сообщить другому концу сокета о том, что больше не будет отправлено данных, должен быть отправлен пакет с установленным флагом FIN. Это достигается в Winsock путем вызова функции shutdown с SD_SEND в качестве второго параметра. Это приведет к тому, что программа на другом конце сокета больше не будет блокироваться при вызове recv. Вместо этого recv вернет 0, указывая, что соединение было изящно закрыто (если не осталось данных, которые еще не были прочитаны). См. документацию Microsoft по функции отключения для получения дополнительной информации. Эта страница документации также содержит некоторую полезную информацию о изящном закрытии сокета.

Кроме того, как было отмечено в комментариях, ваш код содержит утечка памяти вследующая строка:

msg = msgConcatenated

В этой строке вы переназначаете msg без предварительного освобождения памяти, на которую указывает msg. К сожалению, устранить эту утечку памяти нелегко, потому что вы не можете просто позвонить free на msg перед ее переназначением. Это связано с тем, что на первой итерации цикла msg также может указывать на что-то еще, кроме динамически выделяемой памяти. Следовательно, чтобы устранить утечку, вам также необходимо отслеживать тип памяти, на который указывает msg, или всегда указывать на динамически распределенную память, даже когда строка пуста (т. Е. Когда она содержит только завершающий символ). нулевой символ).

...