【问题标题】:Infinite read from socket从套接字无限读取
【发布时间】:2012-04-23 20:02:26
【问题描述】:

从套接字读取分块数据(来自 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 不返回管理。有什么正确的方法可以部分读取分块数据吗? (答案是分块数据)。

【问题讨论】:

    标签: c++ http sockets chunked


    【解决方案1】:

    处理 HTTP 请求的正确方法是使用为您管理套接字连接的更高级别的库。在 C++ 中,一个例子是 pion-net;还有其他类似 Mongoose(它是 C,但可以在 C++ 中使用)。

    【讨论】:

      【解决方案2】:

      理论上无限数据是可能的,而实际实现因流程而异。

      • 方法 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
      

      }

      您将不得不在某个时候中断并处理您的数据将其分派到另一个线程以释放内存。

      【讨论】:

        【解决方案3】:

        如果通过“分块数据”您指的是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...
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-09-27
          • 2011-10-29
          • 2011-05-07
          • 2019-04-02
          • 2013-10-12
          • 1970-01-01
          相关资源
          最近更新 更多