笔者尝试C语言使用Wininet库进行网络编程时,我尝试使用 `InternetSetCookieA()` 或 `HttpAddRequestHeadersA()` 设置 cookie。
HttpAddRequestHeadersA(Request, headers, header_len, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);
InternetSetCookieA(url, NULL, cookie);
然而,编译后运行的结果显示返回了一组JSON信息,其中code是401,msg字符串按UTF-8编码还原为:“请登录后操作!”。似乎这两行语句并不能成功设置cookie。
是请求头和cookies的内容不够详细吗?
于是,我将Chrome浏览器开发者工具中的请求头内容完全复制到C语言代码的headers字符串中,并将浏览器的cookie内容复制到C代码的cookie字符串中。然后进行编译和运行...结果仍然显示相同的错误信息:请登录后操作!
令人苦恼的是,同样的请求头和cookies内容运用在 Python 代码里可以成功登录网站,在POSTMAN 里使用同样的请求头和cookies,也能返回正确的成功登录信息。为什么 C 的wininet就是不能登录?
为进行调试排查,我尝试在`HttpSendRequest()`语句之后添加`HttpQueryInfo()`进行获取headers的内容:
header_buffer_size = 4096;
char* headers_buffer = (char*)malloc(header_buffer_size);
if (HttpQueryInfo(Request, HTTP_QUERY_RAW_HEADERS_CRLF|HTTP_QUERY_FLAG_REQUEST_HEADERS, headers_buffer, &header_buffer_size, NULL)) {
printf("发送请求后的headers内容:\n%s\n", headers_buffer);
} else {
printf("获取headers失败!\n");
}
free(headers_buffer);
这样可以输出headers的实际内容(下图的 headers 内容照抄Chrome浏览器的请求头),可以看到服务器已返回一大串 cookies 内容。
我明明已设定cookies,服务器也返回 cookies 信息,令人百思不得其解的是服务器端为何不能正确处理用户登录,把wininet库的网络请求拒之门外?难道wininet库真的如此差劲?
后来在海外网寻求帮助,得悉以下几点重要信息:
1、wininet.h 库与底层操作系统紧密集成。
2、wininet 库的 cookies 管理与Windows的 Internet Explorer (IE) 浏览器共享 cookies
3、因为 wininet.h 库使用与 Internet Explorer 相同的 cookie 存储机制,允许它共享 cookie 并维护会话状态。
4、如果发送HTTP请求使用不同的库或框架,结果可能会有所不同,具体取决于它处理 cookie 和会话管理的方式。
试了一下,先使用 IE 登录网站,然后执行我的 C 代码,编译运行后显示:code: 200, msg: success,真的成功登录了!
然后关闭 IE,把 C 代码里所有cookies设定的语句注释掉,编译运行,仍显示成功登录!
哪尼?wininet 的 cookies 实际上是由 IE 管理的!
关于上面引用的第4点:“如果发送HTTP请求使用不同的库或框架,结果可能会有所不同,具体取决于它处理 cookie 和会话管理的方式。”,这段话意思是,可以使用其他库如大家熟知的 libcurl 发送网络请求,处理 cookie 的方式不一样。确实 libcurl 有自己的一套管理 cookies 方法,而且是跨平台的库,不需要与Windows的 IE 浏览器共享 cookies。