2013-07-30 30 views
-1

當我想使用連接到主機的同一個套接字發送()和recv()第二次則recv()將沒有任何緩衝使用相同的套接字第二次發送和recv沒有結果?

基本上林返回0做:

  1. 連接到網站
  2. 發送數據包
  3. 接收數據包
  4. 再次發送的數據包(我覺得這個工作,因爲它沒有給我SOCKET_ERROR)
  5. 再次收到數據包(這個人是返回0,從而「關閉連接」)

源代碼:http://pastebin.com/sm5k5GAe

,你可以看到,我有sock2,這時候我使用的第二發送/ recv的是做工精細,但是我會做更多的溝通,並具有所有這些插座和連接它們將是愚蠢的還挺我猜

+0

你有沒有考慮找了一下'的recv()'返回零種手段? – EJP

回答

1

只有2個條件,導致recv()返回0是:

  1. 如果您提供了一個0長度的緩衝區。

  2. 如果對方已經優雅地關閉了連接。您可以使用數據包嗅探器(例如Wireshark)來驗證這一點。

您正在發送HTTP 1.1請求,其中包含一個Connection: close標題。這會告訴HTTP服務器在發送響應後關閉連接的結束。這是完全正常的行爲。只需讀取響應,關閉連接的結尾,然後在發送下一個請求之前重新連接。

如果你想保持連接打開,因此您可以通過單一連接發送多個請求,發送Connection: keep-alive頭而不是,或只是省略Connection頭完全因爲你要發送HTTP 1.1請求和keep-alive是默認行爲HTTP 1.1。

無論採用哪種方式,您都必須查看服務器的實際響應Connection標題,以確定是否要關閉連接的結束。對於HTTP 0.9或1.0響應,如果沒有Connection: keep-alive標題,則必須假定連接將被關閉。對於HTTP 1.1響應,如果沒有Connection: close標題,則必須假定該連接保持打開狀態。但是您必須做好準備來處理連接可能仍然會關閉的可能性,例如通過中間路由器/防火牆,因此如果下一個請求由於連接錯誤而失敗,請重新連接。這樣說,你最好不要手動實現HTTP,而是使用預先創建的庫來處理這些細節,比如libcurl

更新:

嘗試一些更喜歡這個:

#include <winsock2.h> 
#include <windows.h> 
#include <iostream> 
#include <string> 
#include <vector> 
#include <sstream> 
#include <fstream> 

using namespace std; 

SOCKET sock; 
string currentHost; 

bool checkConnection(string Hostname); 
int readFromSock(char *data, int datalen, bool disconnectOK = false); 
int readData(string &workBuffer, char *data, int datalen, bool disconnectOK = false); 
bool readLine(string &workBuffer, string &line); 
bool doRequest(string Hostname, string Request, string &Reply); 

bool GetReply(string Host, string &Reply); 
bool GetLogin(string Reply, string MyStr, string &Login); 
bool GetSession(string Reply, string MyStr, string &Session); 
bool GetLoginReply(string Host, string Username, string Password, string Login, string Session, string &Reply); 

bool Login(string Host, string Resource, string Username, string Password); 
bool IsConnected(string Reply, string MyStr); 

#pragma comment (lib, "ws2_32.lib") 
#pragma warning(disable:4996) 

int main() 
{ 
    string sLoginSite, sUsername, sPassword; 
    char buffer[32]; 

    //Initialize Winsock 
    WSADATA WsaData; 
    if(WSAStartup(MAKEWORD(2,2), &WsaData) != 0) 
    { 
     cout << "WinSock Startup Failed" << endl; 
     system("pause"); 
     return 1; 
    } 

    sock = INVALID_SOCKET; 

    //Get Login Site 
    cout << "Travian login site e.g. ts1.travian.com: "; 
    cin >> buffer; 
    sLoginSite = buffer; 
    cout << endl; 

    //Get Username 
    cout << "Username: "; 
    cin >> buffer; 
    sUsername = buffer; 

    //Get Password 
    cout << "Password: "; 
    cin >> buffer; 
    sPassword = buffer; 
    cout << endl; 

    // Perform Login 
    if (!Login(sLoginSite, sUsername, sPassword)) 
    { 
     cout << "Error while Logging in" << endl; 
     system("pause"); 
     return 1; 
    } 

    cout << "Successfully connected to the account \"" << sUsername << "\" at " << sLoginSite << endl; 
    system("pause"); 

    if (sock != INVALID_SOCKET) 
     closesocket(sock); 

    WSACleanup(); 

    return 0; 
} 

// ensure connection to Hostname 
bool checkConnection(string Hostname) 
{ 
    // Switching to a different hostname? If so, disconnect... 
    if (currentHost != Hostname) 
    { 
     if (sock != INVALID_SOCKET) 
     { 
      closesocket(sock); 
      sock = INVALID_SOCKET; 
     } 

     currentHost = Host; 
    } 

    // TODO: make sure the socket is actually still connected. If not, disconnect... 
    if (sock != INVALID_SOCKET) 
    { 
     /* 
     if (no longer connected) 
     { 
      closesocket(sock); 
      sock = INVALID_SOCKET; 
     } 
     */ 
    } 

    // Create a new connection? 
    if (sock == INVALID_SOCKET) 
    { 
     // resolve the Hostname... 

     struct hostent *host = gethostbyname(Hostname.c_str()); 
     if(!host) 
     { 
      cout << "Can't Resolve Hostname" << endl; 
      return false; 
     } 

     // Connect to the Hostname... 

     SOCKET newSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
     if (newSock == INVALID_SOCKET) 
     { 
      cout << "Error creating Socket" << endl; 
      return false; 
     } 

     SOCKADDR_IN SockAddr; 
     ZeroMemory(&SockAddr, sizeof(SockAddr))); 
     SockAddr.sin_port = htons(80); 
     SockAddr.sin_family = AF_INET; 
     SockAddr.sin_addr.s_addr = *((unsigned long*)host->h_addr); 

     cout << "Connecting" << endl; 
     if(connect(newSock, (SOCKADDR*) &SockAddr, sizeof(SockAddr)) != 0) 
     { 
      closesocket(newSock); 
      cout << "Can't Connect to Hostname" << endl; 
      return false; 
     } 

     sock = newSock; 
     cout << "Successfully connected to " << Hostname << "!" << endl; 
    } 

    // Ready 
    return true; 
} 

// read raw data from the socket directly 
// returns how many bytes were actually read, -1 on error, or 0 on disconnect 
int readFromSock(char *data, int datalen, bool disconnectOK) 
{ 
    int read = 0; 

    while (datalen > 0) 
    { 
     // more data is expected... 

     int ret = recv(sock, data, datalen, 0); 
     if (ret == SOCKET_ERROR) 
     { 
      cout << "recv failed: " << WSAGetLastError() << endl; 
      closesocket(sock); 
      sock = INVALID_SOCKET; 
      return -1; 
     } 

     if (ret == 0) 
     { 
      cout << "server disconnected" << endl; 
      closesocket(sock); 
      sock = INVALID_SOCKET; 

      // if the caller is OK with a disconnect occurring, exit without error... 
      if (disconnectOK) 
       break; 

      return -1; 
     } 

     // move forward in the output buffer 
     data += ret; 
     datalen -= ret; 

     // increment the result value 
     read += ret; 
    } 

    // done 
    return read; 
} 

// read raw data from an in-memory buffer, reading from the socket directly only when the buffer is empty. 
// returns how many bytes were actually read, -1 on error, or 0 on disconnect 
int readData(string &workBuffer, char *data, int datalen, bool disconnectOK) 
{ 
    int read = 0; 

    int len; 
    char buffer[512]; 

    while (datalen > 0) 
    { 
     // more data is expected... 

     len = workBuffer.length(); 
     if (len > 0) 
     { 
      // the work buffer has cached data, move to the output buffer... 

      if (len > datalen) len = datalen; 
      workBuffer.copy(data, len); 
      workBuffer.erase(len); 

      // move forward in the output buffer 
      data += len; 
      datalen -= len; 

      // increment the return value 
      read += len; 
     } 
     else 
     { 
      // the work buffer is empty, read from the socket and cache it... 

      len = readFromSock(buffer, sizeof(buffer), disconnectOK); 
      if (ret == -1) 
       return -1; 

      // disconnected? 
      if (ret == 0) 
       break; 

      // append new data to the work buffer... 
      workBuffer += string(buffer, ret); 
     } 
    } 

    // done 
    return read; 
} 

// reads a LF-delimited line of text from an in-memory buffer, reading from the socket directly only when the buffer is empty. 
// returns whether a full line was actually read. 
bool readLine(string &workBuffer, string &line) 
{ 
    // clear the output... 
    line = ""; 

    int found, len, start = 0; 
    char buffer[512]; 

    do 
    { 
     // check if a LF is already cached. Ignore previously searched text.. 
     found = workBuffer.find("\n", start); 
     if (found != string::npos) 
     { 
      len = found; 

      // is the LF preceded by a CR? If so, do include it in the output... 
      if (len > 0) 
      { 
       if (workBuffer[len-1] == '\r') 
        --len; 
      } 

      // output the line, up to but not including the CR/LF... 
      line = workBuffer.substr(0, len); 
      workBuffer.erase(found); 

      break; 
     } 

     // the work buffer does not contain a LF, read from the socket and cache it... 

     len = readFromSock(buffer, sizeof(buffer)); 
     if (len <= 0) 
     { 
      closesocket(sock); 
      sock = INVALID_SOCKET; 
      return false; 
     } 

     // append new data to the work buffer and search again... 
     start = workBuffer.length(); 
     workBuffer += string(buffer, len); 
    } 
    while (true); 

    // done 
    return true; 
} 

// perform an HTTP request and read the reply 
// returns whether the reply was actually read 
bool doRequest(string Hostname, string Request, string &Reply) 
{ 
    // clear the output 
    Reply = ""; 

    char buffer[512]; 
    string str, workBuffer; 
    string sContentLength, sTransferEncoding, sConnection; 
    bool doClose; 

    // make sure there is a connection, reconnecting if needed... 
    if (!checkConnection(Hostname)) 
     return false; 

    // send the request... 

    char *data = Request.c_str(); 
    int len = Request.length(); 
    int ret; 

    do 
    { 
     ret = send(sock, data, len, 0); 
     if (ret == SOCKET_ERROR) 
     { 
      cout << "Send Error" << endl; 
      closesocket(sock); 
      sock = INVALID_SOCKET; 
      return false; 
     } 

     // move forward in the input buffer... 
     data += ret; 
     len -= ret; 
    } 
    while (len > 0); 

    // read the response's status line... 
    if (!readLine(workBuffer, str)) 
     return false; 

    // TODO: parse out the line's values, ie: "200 OK HTTP/1.1" 
    int ResponseNum = ...; 
    int VersionMajor = ...; 
    int VersionMinor = ...; 

    // only HTTP 1.0 responses have headers... 
    if (VersionMajor >= 1) 
    {  
     // read the headers until a blank line is reached... 

     do 
     { 
      // read a header 
      if (!readLine(workBuffer, str)) 
       return false; 

      // headers finished? 
      if (str.length() == 0) 
       break; 

      // TODO: do case-insensitive comparisons 
      if (str.compare(0, 15, "Content-Length:") == 0) 
       sContentLength = str.substr(15); 
      else if (str.compare(0, 18, "Transfer-Encoding:") == 0) 
       sTransferEncoding = str.substr(18); 
      else if (str.compare(0, 18, "Connection:") == 0) 
       sConnection = str.substr(11); 
     } 
     while (true); 

     // If HTTP 1.0, the connection must closed if the "Connection" header is not "keep-alive" 
     // If HTTP 1.1+, the connection must be left open the "Connection" header is not "close" 

     // TODO: do case-insensitive comparisons 
     if ((VersionMajor == 1) && (VersionMinor == 0)) 
      doClose = (sConnection.compare"keep-alive") != 0); 
     else 
      doClose = (sConnection.compare("close") == 0); 
    } 
    else 
    { 
     // HTTP 0.9 or earlier, no support for headers or keep-alives 
     doClose = true; 
    } 

    // TODO: do case-insensitive comparisons 
    if (sTransferEncoding.compare(sTransferEncoding.length()-7, 7, "chunked") == 0) 
    { 
     // the response is chunked, read the chunks until a blank chunk is reached... 

     do 
     { 
      // read the chunk header... 
      if (!readLine(workBuffer, str)) 
       return false; 

      // ignore any extensions for now... 
      int found = str.find(";"); 
      if (found != string::npos) 
       str.resize(found); 

      // convert the chunk size from hex to decimal... 
      size = strtol(str.c_str(), NULL, 16); 

      // chunks finished? 
      if (size == 0) 
       break; 

      // read the chunk's data... 
      do 
      { 
       len = size; 
       if (len > sizeof(buffer)) len = sizeof(buffer); 

       if (!readData(workBuffer, buffer, len)) 
        return false; 

       // copy the data to the output 
       Reply += string(buffer, len); 
       size -= len; 
      } 
      while (size > 0); 

      // the data is followed by a CRLF, skip it... 
      if (!readLine(workBuffer, str)) 
       return false; 
     } 
     while (true); 

     // read trailing headers... 

     do 
     { 
      // read a header... 
      if (!readLine(workBuffer, str)) 
       return false; 

      // headers finished? 
      if (str.length() == 0) 
       break; 

      // process header as needed, overwriting HTTP header if needed... 
     } 
     while (true); 
    } 
    else if (sContentLength.length() != 0) 
    { 
     // the response has a length, read only as many bytes as are specified... 

     // convert the length to decimal... 
     len = strtol(sContentLength.c_str(), NULL, 10); 

     if (len > 0) 
     { 
      // read the data... 

      do 
      { 
       ret = len; 
       if (ret > sizeof(buffer)) ret = sizeof(buffer); 

       ret = readData(workBuffer, buffer, ret); 
       if (ret <= 0) 
        return false; 

       // copy the data to the output 
       Reply += string(buffer, ret); 
       len -= ret; 
      } 
      while (len > 0); 
     } 
    } 
    else 
    { 
     // response is terminated by a disconnect... 

     do 
     { 
      len = readData(workBuffer, buffer, sizeof(buffer), true); 
      if (len == -1) 
       return false; 

      // disconnected? 
      if (len == 0) 
       break; 

      // copy the data to the output 
      Reply += string(buffer, len); 
     } 
     while (true); 

     doClose = true; 
    } 

    // close the socket now? 
    if (doClose) 
    { 
     closesocket(sock); 
     sock = INVALID_SOCKET; 
    } 

    // TODO: handle other responses, like 3xx redirects... 

    return ((ResponseNum/100) == 2); 
} 

// Login to Hostname with Username and Password 
// returns whether login was successful or not 
bool Login(string Hostname, string Username, string Password) 
{ 
    string sLoginForm, sReply, sSessionID, sLoginID, sLoginReply; 

    //Get Login Form HTML 
    if (!GetReply(Hostname, sReply)) 
    { 
     cout << "Reply Error" << endl; 
     return false; 
    } 

    //Save Reply 
    ofstream Data; 
    Data.open("Reply.txt"); 
    Data << sReply; 
    Data.close(); 

    //Get Session ID from HTML 
    if (!GetSession(sReply, "sess_id", sSessionID)) 
    { 
     cout << "Session ID Error" << endl; 
     return false; 
    } 

    //Get Login ID from HTML 
    if (!GetLogin(sReply, "<input type=\"hidden\" name=\"login\" value=", sLoginID)) 
    { 
     cout << "Login ID Error" << endl; 
     return false; 
    } 

    // perform final Login 
    if (!GetLoginReply(Hostname, Username, Password, sLoginID, sSessionID, sLoginReply)) 
    { 
     cout << "Login Reply Error" << endl; 
     return false; 
    } 

    /* 
    if(!IsConnected(sLoginReply, "HTTP/1.1 303 See Other")) 
    { 
     cout << "Invalid Username or Password" << endl; 
     return false; 
    } 
    */ 

    //Save Reply 
    Data.open("login.txt"); 
    Data << sLoginReply; 
    Data.close(); 

    // done 
    return true; 
} 

bool GetReply(string Hostname, string &Reply) 
{ 
    string str; 

    str = "GET/HTTP/1.1\r\n"; 
    str += "Host: " + Hostname + "\r\n" 
    str += "Connection: keep-alive\r\n" 
    str += "\r\n"; 

    return doRequest(Hostname, str, Reply); 
} 

bool GetSession(string Reply, string MyStr, string &Session) 
{ 
    int found = Reply.find(MyStr); 
    if(found == string::npos) 
     return false; 

    Session = Reply.substr(found+MyStr.Length(), 32); 
    return true; 
} 

bool GetLogin(string Reply, string MyStr, string &Login) 
{ 
    int found = Reply.find(MyStr); 
    if(found == string::npos) 
     return false; 

    Login = Reply.substr(found+MyStr.length(), 10) 
    return true; 
} 

bool GetLoginReply(string Hostname, string Username, string Password, string Login, string Session, string &Reply) 
{ 
    string str, special; 

    special = "name=" + Username + "&password=" + Password + "&s1=Login&w=1600%3A900&login=" + Login; 

    string temp; 
    stringstream ss; 
    ss << special.length(); 
    ss >> temp; 

    str = "POST /dorf1.php HTTP/1.1\r\n"; 
    str += "Host: " + Hostname + "\r\n"; 
    str += "Cookie: sess_id=" + Session + "; highlightsToggle=false; WMBlueprints=%5B%5D\r\n"; 
    str += "Content-Type: application/x-www-form-urlencoded\r\n"; 
    str += "Content-Length: " + temp + "\r\n" 
    str += "\r\n"; 
    str += special; 

    return doRequest(Hostname, str, Reply); 
} 

bool IsConnected(string Reply, string MyStr) 
{ 
    return (Reply.find(MyStr) != string::npos); 
}