我的Minecraft服务器程序遇到了这个令人讨厌的问题。 当您尝试登录我的Minecraft服务器时,我尝试从minecraft.com获取登录响应。一切都很好,并设置为验证令牌密钥和共享密钥。
我无法获得使用curl的get请求:(
这就是我所拥有的:
requestString.append("GET /session/minecraft/hasJoined?username=");
requestString.append(player.Username);
requestString.append("&");
requestString.append("serverId=");
requestString.append(player.loginHash);
requestString.append(" HTTP/1.0\r\n");
std::cout << requestString << std::endl;
curl_easy_setopt(curl, CURLOPT_URL, "https://sessionserver.mojang.com");
list = curl_slist_append(list, requestString.c_str());
list = curl_slist_append(list, "User-Agent: MinecraftServerPP\r\n");
list = curl_slist_append(list, "Connection: close\r\n");
list = curl_slist_append(list, "\r\n");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, list);
curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem");
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &_write_data);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response);
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
我从服务器得到了一个ok,但没有包含我需要的数据的json对象。
答案 0 :(得分:3)
为什么要将GET
请求行放在HTTP标头中?它根本不属于那里,事实上libCURL文档甚至指出了这一点:
请求中的第一行(包含该方法,通常是GET或POST)不是标题,并且无法使用此选项替换。 只有请求行后面的行是标题。在此标头列表中添加此方法行只会导致您的请求发送无效标头。使用CURLOPT_CUSTOMREQUEST更改方法。
您当前的代码正在发送与此类似的GET
请求:
GET / HTTP/1.1 Host: sessionserver.mojang.com GET /session/minecraft/hasJoined?username=<username>&serverId=<loginHash> HTTP/1.0 User-Agent: MinecraftServerPP Connection: close
这就是为什么你没有得到你期望的回应。您实际上并未请求/session/minecraft/hasJoined
资源,实际上您正在请求根/
资源。
您应该发送这样的GET
请求:
GET /session/minecraft/hasJoined?username=<username>&serverId=<loginHash> HTTP/1.0 Host: sessionserver.mojang.com User-Agent: MinecraftServerPP Connection: close
要实现这一点,您需要将完整的 URL传递给CURLOPT_URL
,并让libCURL为您处理GET
行。您可以使用CURLOPT_HTTPGET
确保实际使用GET
。
此外,如果要指定特定的HTTP版本,则应使用CURLOPT_HTTP_VERSION
。
此外,虽然 可以<{1}}使用CURLOPT_HTTPHEADER
和User-Agent
标题,但您应使用{{3>而是CURLOPT_USERAGENT
而不是。
请改为尝试:
Connection
现在,如上所述,您需要考虑其他逻辑来正确处理CURLOPT_FORBID_REUSE
:
传入指向要使用的URL的指针。该参数应为char *到零终止字符串,必须采用以下格式进行URL编码:
scheme:// host:port / path
有关格式的更多说明,请参阅
CURLOPT_URL
。libcurl不会验证语法或使用此变量,直到发出传输。即使您在此处设置了疯狂值,
url = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username="); url.append(player.Username); // <-- NOTE: see further below! url.append("&serverId="); url.append(player.loginHash); // <-- NOTE: see further below! std::cout << url << std::endl; curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L); curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1L); curl_easy_setopt(curl, CURLOPT_CAINFO, "cacert.pem"); curl_easy_setopt(curl, CURLOPT_USERAGENT, "MinecraftServerPP"); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &_write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
仍会返回curl_easy_setopt
。
特别是,RFC的这些部分与您的代码相关:
2.1。 %的编码
百分比编码机制用于表示组件中的数据八位字节,当该八位字节的对应字符超出允许的集合或被用作分隔符或内部的分隔符时,组件。百分比编码的八位字节被编码为字符三元组,由百分比字符&#34;%&#34;后跟两个十六进制数字,表示该八位字节的数值。例如,&#34;%20&#34;是二进制八位字节的百分比编码&#34; 00100000&#34; (ABNF:%x20),其中US-ASCII对应于空格字符(SP)。 RFC 3986描述了应用百分比编码和解码的时间。
pct-encoded =&#34;%&#34; HEXDIG HEXDIG
大写十六进制数字&#39; A&#39;通过&#39; F&#39;相当于小写数字&#39; a&#39;分别通过&#39; f&#39;如果两个URI仅在百分比编码八位字节中使用的十六进制数字的情况下不同,则它们是等效的。为保持一致性,URI生成器和规范化器应对所有百分比编码使用大写十六进制数字。
2.4。何时进行编码或解码
在正常情况下,URI中八位字节进行百分比编码的唯一时间是在从其组成部分生成URI的过程中。这是一种实现确定哪个保留字符将用作子组件分隔符,哪些可以安全地用作数据。生成后,URI始终采用百分比编码形式。
当取消引用URI时,必须解析和分离对特定于方案的解除引用过程(如果有)重要的组件和子组件,然后才能安全地解码这些组件中的百分比编码八位字节,否则数据可能是被误认为是组件分隔符。唯一的例外是对应于未保留集中字符的百分比编码八位字节,可以随时解码。例如,对应于波浪号(&#34;〜&#34;)字符的八位字节通常被编码为&#34;%7E&#34;通过较旧的URI处理实现; &#34;%7E&#34;可以用&#34;〜&#34;没有改变它的解释。
因为百分比(&#34;%&#34;)字符用作百分比编码八位字节的指示符,所以它必须被百分比编码为&#34;%25&#34;将该八位字节用作URI中的数据。实现不得多次对相同的字符串进行百分比编码或解码,因为对已经解码的字符串进行解码可能会导致错误解释百分比数据八位字节作为百分比编码的开头,反之亦然,在编码百分比编码的情况下百分比编码的字符串。
网址的CURLE_OK
组件在Section 2.4中定义:
query = *(pchar /&#34; /&#34; /&#34;?&#34;)
query
在section 3.4中定义:
pchar = unreserved / pct-encoded / sub-delims /&#34;:&#34; /&#34; @&#34;
pchar
在section 3.3中定义:
未保留= ALPHA / DIGIT /&#34; - &#34; /&#34;。&#34; /&#34; _&#34; /&#34;〜&#34;
unreserved
在section 2.3中定义:
sub-delims =&#34;!&#34; /&#34; $&#34; /&#34;&amp;&#34; /&#34;&#39;&#34; /&#34;(&#34; /&#34;)&#34; /&#34; *&#34; /&#34; +&#34; /&#34;,&#34; /&#34;;&#34; /&#34; =&#34;
因此,如果sub-delims
和player.Username
字符串中有任何字符
如果超出这些允许的字符,那么在将它们放入网址的查询字符串时,必须百分比编码。例如:
player.loginHash
std::string encodeForUrlQuery(std::string s)
{
static const char* allowed = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~!$&'()*+,;=:@/?";
// this is just an example, in practice you should first convert the
// input string to Unicode and charset-encode it, usually to UTF-8,
// and then percent-encode the resulting octets...
std::string::size_type idx = s.find_first_not_of(allowed);
while (idx != std::string::npos)
{
std::ostringstream oss;
oss << '%' << std::hex << std::setw(2) << std::setfill('0') << std::uppercase << (int)s[idx];
std::string encoded = oss.str();
s.replace(idx, 1, encoded);
idx = s.find_first_not_of(allowed, idx+encoded.length());
}
return s;
}