2010-07-23 2 views
1

Я работаю над небольшим сетевым проектом с использованием Winsock2. Я использую TCP-соединение и фактически работаю с IRC в качестве примера, поскольку IRC довольно прост. То, что я делаю, - это подключение к серверу и отправка начального буфера, чтобы сервер распознал соединение. Это прекрасно работает.Multipe Send() и Recv() с использованием Winsock2

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

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

Спасибо!

ADDED КОД

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

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; 
} 

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

EDIT

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

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, что означает, что мне нужно будет прочитать вывод до закрытия соединения. Есть идеи?

+0

Просьба показать короткий, простой пример, демонстрирующий проблему, которую вы видите. В противном случае у нас нет полной картины. –

+0

Обновлено, спасибо! – RageD

ответ

2

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

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

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

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

+0

А ты прав. Я изменил recvbuf от char recvbuf [LENGTH] до char recvbuf, извините. Я удалил «shutdown()», но я получаю тот же результат. Теперь программа не висит на неопределенный срок, однако она занимает около 20 секунд, хотя выводит сообщение 10053 (программное обеспечение вызвало прерывание соединения). Сервер завершен; Я нахожусь на нем с помощью std IRC-клиента. Я также добавил цикл, как было предложено в последнем комментарии ниже, в то время как (записано <(int) strlen (data)), где написано init в 0, и каждый цикл написан = написан + iResult; Есть идеи? – RageD

1

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

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

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

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

Я настоятельно рекомендую использовать Wireshark, так что вы можете проверить, что идет вниз к проводу

0
data += recvbuf; 

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

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

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