我正在尝试了解HTTP2协议的工作原理。我看到Apple将它用作推送通知服务器。我正在使用Discover HTTP中的框架规范。
作为测试,我编写了应该与该服务器通信的代码。但是,我不断收到我错过了"设置"帧。
我的代码如下:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dlfcn.h>
#include <unistd.h>
#include <openssl/ssl.h>
#define SOCKET_ERROR -1
struct Frame //Represents and HTTP2 Frame.
{
char len[24];
char type[8];
char flags[8];
char identifier[31];
char payload[];
} __attribute__((packed));
void writeFrame(SSL* ssl)
{
//First thing after connecting is to send the PREFACE.
std::string preface = "PRI * HTTP/2.0\r\n\r\n";
preface += "SM\r\n\r\n";
SSL_write(ssl, preface.c_str(), preface.length());
//Now to send the first frame. Aka the SETTINGS frame.
Frame* frame = (Frame *)malloc(sizeof(Frame));
memset(frame, 0, sizeof(Frame));
int frameSize = 100;
memcpy(frame->len, &frameSize, sizeof(frameSize));
memcpy(frame->type, "SETTINGS", strlen("SETTINGS"));
memcpy(frame->identifier, "SETTINGS", strlen("SETTINGS"));
SSL_write(ssl, frame, sizeof(Frame));
//Read server response.
char buffer[10000];
memset(buffer, 0, sizeof(buffer));
int dataLen;
while ((dataLen = SSL_read(ssl, buffer, sizeof(buffer)) > 0))
{
int i = 0;
while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
std::cout << buffer[i];
i += 1;
}
}
//The above gives me the error:
//First received frame was not SETTINGS.
//Hex dump for first 5 bytes: 6400000000
//Try to POST to the server now.
std::string payload = "POST /3/device HTTP/2.0\r\n";
payload += ":method:POST\r\n";
payload += ":scheme: https\r\n";
payload += "cache-control: no-cache\r\n";
payload += "user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.95 Safari/537.36\r\n";
payload += "content-type: text/plain;charset=UTF-8\r\n\r\n";
int sentBytes = SSL_write(ssl, payload.c_str(), payload.length());
if (sentBytes < payload.length() || sentBytes == SOCKET_ERROR)
{
return;
}
memset(buffer, 0, sizeof(buffer));
while ((dataLen = SSL_read(ssl, buffer, sizeof(buffer)) > 0))
{
int i = 0;
while (buffer[i] >= 32 || buffer[i] == '\n' || buffer[i] == '\r') {
std::cout << buffer[i];
i += 1;
}
}
}
int main(int argc, const char * argv[]) {
SSL_library_init();
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
std::string address = "api.development.push.apple.com";
struct addrinfo hints = {0};
struct addrinfo* result = nullptr;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(address.c_str(), nullptr, &hints, &result);
int sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (sock == -1)
{
return -1;
}
struct sockaddr_in sockAddr;
sockAddr.sin_addr = reinterpret_cast<struct sockaddr_in*>(result->ai_addr)->sin_addr;
sockAddr.sin_family = result->ai_family;
sockAddr.sin_port = htons(443);
freeaddrinfo(result);
if (connect(sock, reinterpret_cast<sockaddr *>(&sockAddr), sizeof(sockAddr)) == SOCKET_ERROR)
{
close(sock);
return -1;
}
SSL_CTX* ctx = SSL_CTX_new(TLSv1_2_method());
SSL* ssl = SSL_new(ctx);
SSL_set_fd(ssl, sock);
SSL_connect(ssl);
writeFrame(ssl);
SSL_CTX_free(ctx);
SSL_shutdown(ssl);
SSL_free(ssl);
close(sock);
return 0;
}
如何将SETTINGS
帧和其他帧发送到服务器?我错过了什么?
答案 0 :(得分:3)
你有几个错误。
首先,Frame
数据结构,其中数组字段的长度是错误的。
您似乎已经以位为单位复制了它们的长度,但在Frame
数据结构中以字节为单位进行了报告。
你想要这个:
struct Frame {
char len[3];
char type;
char flags;
char identifier[4];
char payload[];
}
此外,框架的类型不是字符串,也不是标识符。
最后,请求的格式完全错误,类似于HTTP / 1.1,而HTTP / 2格式完全不同,基于HPACK。
我建议您在编写更多代码之前仔细阅读HTTP/2 specification。