C ssl_读取(ssl、缓冲区、sizeof(缓冲区))未正确读取json返回

C ssl_读取(ssl、缓冲区、sizeof(缓冲区))未正确读取json返回,c,json,openssl,C,Json,Openssl,我正在使用嵌入式linux、C和openSSL。我正在使用GET访问REST服务器,并期望得到json对象响应。服务器获取正确的REST Get并正确处理它。但是,由于某些原因,bytes=ssl_read()没有正确接收json对象。我知道服务器正在发送正确的json对象,因为我可以用Postman或Curl脚本点击服务器,得到正确的json对象。此外,记录器(调试)显示服务器使用正确的json对象正确响应。如果我修改服务器端点代码以使用字符串响应并返回jsonObject.ToString(

我正在使用嵌入式linux、C和openSSL。我正在使用GET访问REST服务器,并期望得到json对象响应。服务器获取正确的REST Get并正确处理它。但是,由于某些原因,bytes=ssl_read()没有正确接收json对象。我知道服务器正在发送正确的json对象,因为我可以用Postman或Curl脚本点击服务器,得到正确的json对象。此外,记录器(调试)显示服务器使用正确的json对象正确响应。如果我修改服务器端点代码以使用字符串响应并返回jsonObject.ToString(),我会得到正确的响应,但它是一个字符串。为什么我没有得到一个好的json对象?这是一个简短的代码列表

 // Define some parameters
 int sockfd, bytes_read;
 struct sockaddr_in dest;
 struct hostent *hostent;
 unsigned short svrport = 8443;

 char hdr[4000];
 char buffer[4000];

 // Initialise Crypto library
 ERR_load_crypto_strings();
 OpenSSL_add_all_algorithms();
 OPENSSL_config(NULL);

 // Create GET status HTTP Client Message String
 bzero(hdr, sizeof(hdr));
 strcpy(hdr, "GET /status HTTP/1.1\r\n");
 strcat(hdr, "Host: xxx.xxx.xxx.xxx\r\n");
 strcat(hdr, "X-BMID-ID: 1234-5678\r\n");
 strcat(hdr, "Authorization: ");
 strcat(hdr, sttb64e);
 strcat(hdr, "\r\n");
 strcat(hdr, "Accept: application/json\r\n");
 strcat(hdr, "Content-Type: application/json\r\n");
 strcat(hdr, "Connection: close\r\n");
 strcat(hdr, "\r\n");  

 // Initialize server address/port struct
 bzero(&dest, sizeof(dest));
 dest.sin_family = AF_INET;
 dest.sin_port = htons(svrport);
 dest.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");

 if ( dest.sin_addr.s_addr == INADDR_NONE ) {
        printf("Incorrect Address Expression\n");
        return 0;
 }

 // Connect Socket
 if ( connect(sockfd, (struct sockaddr*)&dest, sizeof(dest)) != 0 ) {
      printf("Socket Connection Failed\n");
      close(sockfd);
      return 0;
 }

 // Now initialize SSL Engine
 SSL_METHOD const *method;
 SSL_CTX *ctx;
 SSL *ssl;

 SSL_library_init();
 OpenSSL_add_ssl_algorithms();
 SSL_load_error_strings();
 method = TLSv1_2_client_method();
 ctx = SSL_CTX_new(method);
 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);

 // Make sure SSL Engine is ok
 if (ctx == NULL) {
    printf("SSL Engine initialization failed\n");
    ERR_print_errors_fp(stderr);
    close(sockfd);
    return 0;
 }

 // Now create the SSL connection state
 ssl = SSL_new(ctx);
 SSL_set_fd(ssl, sockfd);

 // Check to see if the SSL socket was connected
if (SSL_connect(ssl) == FAIL) {
    printf("SSL socket failed to connect\n");
    return 0;
 }

 // Now Send Secure Message
 bzero(buffer, sizeof(buffer));
 SSL_write(ssl, hdr, strlen(hdr));
 bytes = SSL_read(ssl, buffer, sizeof(buffer));
 buffer[bytes] = 0;
 if (verbose) {
    printf("Received Header:\n");
    printf("%s\n", buffer);
 }
 bzero(buffer, sizeof(buffer));
 bytes = SSL_read(ssl, buffer, sizeof(buffer)); <=== **** Error is Here ****
 buffer[bytes] = 0;
 printf("Received Body:%s\n\n", buffer);
 printf("Buffer Length = %i\n\n", bytes);
 SSL_free(ssl);

 // Shut down connection
 close(sockfd);
 SSL_CTX_free(ctx);

正如我所说,这段代码运行良好,除了没有在缓冲区中存储正确的json对象。所讨论的ssl_read()位于清单的末尾,并标记为,您需要阅读有关传输编码“chunked”的内容,它不仅仅是纯文本。您需要
sizeof(buffer)
,但正确的方法是读取,直到您找到一个
0
,它作为文本从服务器发送到一行(据我所记得的),这意味着块是完整的

我为目前正在进行的一个项目创建了一个简单的HTTP库。我首先对HTTP头进行分析,主要关注三件事

  • Content-Length头:如果存在,只需获取值并在一次读取中从服务器读取,尽管这意味着OpenSSL的读取循环并不那么简单

  • 传输编码标题:在这里,如果内容长度不存在,您可以确定如何读取数据

  • 内容编码标题:用于确定内容是否被压缩,例如使用gzip

  • 在我的特殊情况下,这就足够了。但这只是协议的一小部分,一般来说并不合适。虽然,也许这对你有用

    因此,您需要首先从服务器获取所有标题,读取每一行,直到出现空行。在获得标题之后,您可以使用这些信息来决定如何读取响应主体。基本上,您希望实现一个简单的HTTP客户机,至少应该实现HTTP协议的基本内容

    这是一个很大的工作,我想如果你能安装OpenSSL,你很可能会使用libcurl,但我不确定,因为我从来没有使用过嵌入式linux


    注意:如果您了解字符串是如何工作的,您就不会像那样使用
    strcat()

    您需要阅读有关传输编码“chunked”的内容,它不仅仅是纯文本。您需要
    sizeof(buffer)
    ,但正确的方法是读取,直到您找到一个
    0
    ,它作为文本从服务器发送到一行(据我所记得的),这意味着块是完整的

    我为目前正在进行的一个项目创建了一个简单的HTTP库。我首先对HTTP头进行分析,主要关注三件事

  • Content-Length头:如果存在,只需获取值并在一次读取中从服务器读取,尽管这意味着OpenSSL的读取循环并不那么简单

  • 传输编码标题:在这里,如果内容长度不存在,您可以确定如何读取数据

  • 内容编码标题:用于确定内容是否被压缩,例如使用gzip

  • 在我的特殊情况下,这就足够了。但这只是协议的一小部分,一般来说并不合适。虽然,也许这对你有用

    因此,您需要首先从服务器获取所有标题,读取每一行,直到出现空行。在获得标题之后,您可以使用这些信息来决定如何读取响应主体。基本上,您希望实现一个简单的HTTP客户机,至少应该实现HTTP协议的基本内容

    这是一个很大的工作,我想如果你能安装OpenSSL,你很可能会使用libcurl,但我不确定,因为我从来没有使用过嵌入式linux

    注意:如果您了解字符串的工作原理,就不会像那样使用
    strcat()

    两件事:

  • 除非您要实现HTTP 1.1规范的所有要求,否则不要声称支持HTTP/1.1

  • 您不能假设第一次调用
    SSL\u read
    会得到标题,下一次调用会得到正文。您必须实际实现HTTP协议。SSL不理解HTTP,也不知道头或主体是什么,也不知道如何读取它们

  • 老实说,你的礼仪知识水平表明你在这方面远远超出了你的深度。您真的应该找到一个满足您需求的现有HTTP客户机实现,而不是尝试自己实现。即使每次测试它时它都能正常工作,也可能会犯很多微妙的错误,而每一个对此不熟悉的人几乎都会犯这些错误

    例如:

     bytes = SSL_read(ssl, buffer, sizeof(buffer));
     buffer[bytes] = 0;
    
    如果第一行之后的
    bytes==sizeof(buffer)
    会怎么样?第二行将写入缓冲区的末尾。

    两件事:

  • 除非您要实现HTTP 1.1规范的所有要求,否则不要声称支持HTTP/1.1

  • 您不能假设第一次调用
    SSL\u read
    会得到标题,下一次调用会得到正文。您必须实际实现HTTP协议。SSL不理解HTTP,也不知道头或主体是什么,也不知道如何读取它们

  • 老实说,
     bytes = SSL_read(ssl, buffer, sizeof(buffer));
     buffer[bytes] = 0;