2012-04-23 8 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

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... 
     } 
    } 
} 
関連する問題