Многократные Send () и Recv () используют Winsock2 - PullRequest
1 голос
/ 24 июля 2010

Я работаю над небольшим сетевым проектом с использованием Winsock2.Я использую соединение TCP и на самом деле работаю с IRC в качестве примера, поскольку IRC довольно прост.Я подключаюсь к серверу и отправляю начальный буфер, чтобы сервер распознал соединение.Это прекрасно работает.

Меня беспокоит то, что я не могу снова записать в сокет.Кажется, моя программа зависает, если я не использую shutdown () (на SD_SEND) после отправки исходного буфера.

Итак, следующие данные (основанные на RFC 1459), которые я хочу отправить, - это информация USER и NICKТем не менее, я чувствую, что использование shutdown () является причиной моей текущей проблемы.Есть ли способ переинициализировать сокет записи?

Спасибо!

ДОБАВЛЕННЫЙ КОД

Обратите внимание, что они расположены внутри класса, поэтому он все еще можетбыть слегка затемненнымЯ пишу это в более простом примере, используя элементы, которые у меня есть.Все правильно определено, поэтому, если я забуду определить вещи, я приношу свои извинения, но многие из моих повторяющихся переменных определены для области видимости класса.

int main(int argc,char *argv[])
{
        int iResult;
        SOCKET Connection;
    iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
    if(iResult != 0)
        throw "Startup failed!";

    // Prep stuff
    ZeroMemory(&hints,sizeof(hints)); // This struct is defined addrinfo
    hints.ai_family   = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    // Now resolve server addr
    iResult = getaddrinfo(argv[1],argv[2],&hints,&result);
    if(iResult != 0)
        throw "getaddrinfo() failed!";

    // Now try to connect
    for(ptr=result;ptr != NULL;ptr = ptr->ai_next)
    {
        Connection = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol); // defined in that "hints" struct. argument number 2
        if(Connection == INVALID_SOCKET)
        {
            freeaddrinfo(result);
            WSACleanup();
            throw "Error at socket();";
        }

        // Connect to server
        iResult = connect(Connection, ptr->ai_addr, (int)ptr->ai_addrlen);
        if(iResult != 0)
        {
            closesocket(Connection);
            Connection = INVALID_SOCKET;
            continue;
        }
        break;
    }

    freeaddrinfo(result);

    // Send initial buffer so server know you're there :)
    iResult = send(Connection, "", 1, 0);
    if(iResult == SOCKET_ERROR)
    {
        close();
        throw "Could not send initial buffer!";
    }

    // Close this connection for the inital buffer
    iResult = shutdown(Connection, SD_SEND);
    if(iResult == SOCKET_ERROR)
    {
        close();
        throw "Could not close initial buffer socket!";
    }

        bool connected = true;

        // This is taken from my read function within the class
        // BEGIN READ FUNCTION
    iResult = 0; // Reset
    std::string data = ""; // Capture the output and send it all at once!

    // This only works if we're connected sweet cakes <3
    if(connected)
    {
        do
        {
            iResult = recv(socket, recvbuf, BUFLEN, 0);
            if(iResult > 0)
            {
                // Working properly
                // Save all data even if there is more than BUFLEN sent
                continue;
            }
            else if(iResult == 0)
                // Connection closed properly
                break;
            else
                printf("ERROR!");
        } while(iResult > 0);
    }
    data += recvbuf;
    ZeroMemory(&recvbuf,sizeof(recvbuf));
        // Function returns std::string but essentially this is what happens
        printf("%s",data.c_str());
        // END READ FUNCTION 

        // BEGIN WRITE FUNCTION
    iResult = 0; // Reset
        SOCKET socket = Connection; // Write function arg 1
        char *data; // Write function arg 2

    iResult = send(socket,data,(int)strlen(data),0);
    if(iResult == SOCKET_ERROR)
    {
        close();
        printf("Could not write data: %ld",WSAGetLastError()); 
                return 1;
    }

    // Data sent, let's close the write socket
    iResult = shutdown(socket, SD_SEND);
    if(iResult != 0)
    {
        close();
        printf("Could not close write socket!");
                return 1;
    }

    //return iResult;
        // END WRITE FUNCTION

        // Now that will produce "Could not write data: 0" for any value of data
        // So realistically I want to send the USER and NICK data, then read 
        // and probably process a PING string from the server and send my PONG response

        return 0;
}

Надеюсь, это прояснит ситуацию!

РЕДАКТИРОВАТЬ

Я думаю, я понял, что происходит не так.Я внес исправления, перечисленные ниже, в мой код;Спасибо, парни.Тем не менее, это мой цикл чтения, который мешает с вещами.Даже после того, как у него есть вся информация, кажется, что он ожидает закрытия соединения, прежде чем отправит вывод.Есть идеи?Мой вывод в настоящее время выглядит следующим образом (записанные байты / итоговые значения - это то, что я добавил, чтобы убедиться, что все идет по проводам правильно)

Bytes Written: 41
Bytes Total: 41
Data: ERROR :Closing Link: raged123[127.0.0.1] 6667 (Ping timeout)
...
:irc.foonet.com NOTICE AUTH :*** Found your hostname (cached)
PING :2ED39CE5
[A bunch of funny characters]WinSock 2.0

Так что, похоже, истекло время ожидания, потому что PING не получил PONGоднако со временем я не могу отправить PONG без предварительной обработки запроса PING, а это значит, что мне нужно будет прочитать выходные данные до того, как соединение будет закрыто.Есть идеи?

Ответы [ 4 ]

3 голосов
/ 24 июля 2010

Могу ли я предложить забавный документ на эту тему? 6 и 7 главы Руководство Биджа по сетевому программированию

У него есть несколько примеров.

2 голосов
/ 24 июля 2010

Не должно быть необходимости отправлять «начальный буфер», как вы сделали. Сервер получит уведомление при подключении клиента, это не зависит от того, отправляет ли клиент что-либо. (И, в частности, протокол IRC говорит, что сервер начнет отправлять вам сообщения, как только вы подключитесь.)

Звонок на shutdown() очень подозрительный. Почему вы ожидали, что должны это сделать? Выключение сокета - это то, что вы делаете, когда вы закончите с подключением, а не когда вы только начинаете. Вы должны полностью удалить это.

Я не уверен, что типа recvbuf, но похоже, что вы используете его неправильно. К чему-то, что может быть добавлено к std::string, вероятно, также не может быть вызвано ZeroMemory(), без одного или другого из тех, кто ошибается. Вы также не используете iResult, которое является фактическим количеством байтов, полученных от сервера.

Ваша функция записи также содержит вызов shutdown(), который вы должны удалить.

1 голос
/ 24 июля 2010

По словам человека отправить (2)

В случае успеха эти вызовы возвращают количество отправленных символов. По ошибке, -1 возвращается, и errno устанавливается соответствующим образом.

Возможно, что при отправке не отправляется полный буфер сразу, вы должны использовать цикл вокруг него.

Однако это может быть не вашей реальной проблемой, поскольку вы отправляете пустую строку ...

Я бы настоятельно рекомендовал использовать Wireshark , чтобы вы могли проверить, что происходит на провод

0 голосов
/ 14 сентября 2012
data += recvbuf;

Это не может работать. string::operator+= невозможно узнать, сколько байтов было получено. Эта функция ожидает строку в стиле C, а не произвольный кусок байтов.

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

В результате ваш переход от чтения к записи происходит по существу в случайное время, определяемое капризами времени TCP и тем, как сервер решает сегментировать свои выходные данные. Поскольку серверу разрешено сегментировать свои выходные данные, как ему угодно (по протоколу ясно, что клиент не может полагаться на сегментацию для анализа протокола, а вместо этого должен полагаться на линейно-ориентированный характер), поведение вашей программы непредсказуемо.

...