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管理を返しません。チャンクされたデータを部分的に読み取る正しい方法はありますか?(答えはチャンク化されたデータです)。

4

3 に答える 3

2

HTTPリクエストを処理する正しい方法は、ソケット接続を管理する高レベルのライブラリを使用することです。C ++では、1つの例はpion-netです。Mongoose(Cですが、C ++で使用しても問題ありません)のようなものもあります。

于 2012-04-23T20:15:07.310 に答える
1

「チャンク データ」によってTransfer-Encoding: chunkedHTTP ヘッダーを参照している場合は、各チャンクを読み取り、チャンク ヘッダーを解析して、各チャンクで読み取るデータの量と、最後のチャンクがいつ受信されたかを知る必要があります。socket.receive()チャンク化されたデータには構造が定義されているため、盲目的に を呼び出すことはできません。詳細については、 RFC 2616 セクション 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...
        }
    }
}
于 2012-04-24T00:43:27.187 に答える
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

}

ある時点で中断してデータを処理する必要があります。メモリを解放する別のスレッドにディスパッチします。

于 2012-04-23T20:53:52.177 に答える