2012-04-23 86 views
0

從套接字讀取分塊數據(來自http請求)的正確方法是什麼?無限讀取套接字

sf::TcpSocket socket; 
socket.connect("0.0.0.0", 80); 

std::string message = "GET /address HTTP/1.1\r\n"; 
socket.send(message.c_str(), message.size() + 1); 

// Receive an answer from the server 
char buffer[128]; 
std::size_t received = 0; 
socket.receive(buffer, sizeof(buffer), received); 
std::cout << "The server said: " << buffer << std::endl; 

但服務器發送無限數據並且socket.receive不返回管理。任何正確的方法來逐塊讀取分塊數據? (答案是分塊數據)。

回答

2

處理HTTP請求的正確方法是使用管理套接字連接的更高級別庫。在C++中,一個例子是pion-net;還有其他人也喜歡Mongoose(這是C語言,但可以在C++中使用)。

1

好的無限數據在理論上是可能的,而實際的實現因過程而不同。

  • 方法1 - 通常很多協議都在最初的幾個字節(4個字節)發送的大小,你可以有一個while循環

{

int i = 0, ret = 1; 
unsigned char buffer[4]; 
while (i<4 && ret == 0) 
    socket.receive(buffer + i, 1 , ret); 

// have a while loop to read the amount of data you need. Malloc the buffer accordingly 

}

  • 方法2 - 或者你的情況下,你不知道長度(無限)

{

char *buffer = (char *)malloc(TCP_MAX_BUF_SIZE); 
std::size_t total = 0, received = 0; 
while (total < TCP_MAX_BUF_SIZE && return >= 0) { 
    socket.receive(buffer, sizeof(buffer), received); 
    total += received; 
} 

//do something with your data 

}

你將不得不在somepoint打破和處理您的數據就派遣釋放的另一個線程的內存。

1

如果通過「chunked data」指向Transfer-Encoding: chunked HTTP頭,則需要讀取每個塊並解析塊頭以知道每個塊需要讀取多少數據並知道最後一個塊的時間接收。因爲分塊數據具有定義的結構,所以您不能盲目地致電socket.receive()。閱讀RFC 2616 Section 3.6.1瞭解更多詳情。

你需要做更多的東西一樣以下(略去了錯誤處理 - 不要忽略它在你真正的代碼):

std::string ReadALine(sf::TcpSocket &socket) 
{ 
    std::string result; 

    // read from socket until a LF is encountered, then 
    // return everything up to, but not including, the 
    // LF, stripping off CR if one is also present... 

    return result; 
} 

void ReadHeaders(sf::TcpSocket &socket, std::vector<std::string> &headers) 
{ 
    std::string line; 

    do 
    { 
     line = ReadALine(socket); 
     if (line.empty()) return; 
     headers.push_back(line); 
    } 
    while (true); 
} 

std::string UpperCase(const std::string &s) 
{ 
    std::string result = s; 
    std::for_each(result.begin(), result.end(), toupper);    
    return result; 
} 

std::string GetHeader(const std::vector<std::string> &headers, const std::string &s) 
{ 
    std::string prefix = UpperCase(s) + ":"; 

    for (std::vector<std::string>::iterator iter = headers.begin(), end = headers.end(); iter != end; ++iter) 
    { 
     if (UpperCase(i)->compare(0, prefix.length(), prefix) == 0) 
      return i->substr(prefix.length()); 
    } 

    return std::string(); 
} 

sf::TcpSocket socket; 
socket.connect("0.0.0.0", 80); 

std::string message = "GET /address HTTP/1.1\r\nHost: localhost\r\n\r\n"; 
socket.send(message.c_str(), message.length()); 

std:vector<std::string> headers; 

std::string statusLine = ReadALine(sockeet); 
ReadHeaders(socket, headers); 

// Refer to RFC 2616 Section 4.4 for details about how to properly 
// read a response body in different situations... 

int statusCode; 
sscanf(statusLine.c_str(), "HTTP/%*d.%*d %d %*s", &statusCode); 

if (
    ((statusCode/100) != 1) && 
    (statusCode != 204) && 
    (statusCode != 304) 
    ) 
{ 
    std::string header = GetHeader(headers, "Transfer-Encoding"); 

    if (UpperCase(header).find("CHUNKED") != std::string::npos) 
    { 
     std::string extensions; 
     std::string_size_type pos; 
     std::size_t chunkSize; 

     do 
     { 
      line = ReadALine(socket); 
      pos = line.find(";"); 
      if (pos != std::string::npos) 
      { 
       extensions = line.substr(pos+1); 
       line.resize(pos); 
      } 
      else 
       extensions.clear(); 

      chunkSize = 0; 
      sscanf(UpperCase(line).c_str(), "%X", &chunkSize); 
      if (chunkSize == 0) 
       break; 

      socket.receive(someBuffer, chunkSize); 
      ReadALine(socket); 

      // process extensions as needed... 
      // copy someBuffer into your real buffer... 
     } 
     while (true); 

     std::vector<std::string> trailer; 
     ReadHeaders(socket, trailer); 

     // merge trailer into main header... 
    } 
    else 
    { 
     header = GetHeader(headers, "Content-Length"); 

     if (!header.empty()) 
     { 
      uint64_t contentLength = 0; 
      sscanf(header.c_str(), "%Lu", &contentLength); 

      // read from socket until contentLength number of bytes have been read... 
     } 
     else 
     { 
      // read from socket until disconnected... 
     } 
    } 
}