我从来没有在C++中真正做过任何多线程或异步的事情,到目前为止我只使用cURL来完成单个同步请求。
为了更好地展示我想要做的事情,我写了一个简单的Javascript,它可以完成我想用C++中的cURL完成的任务。cURL:处理多个异步请求
function AddRequest(method, url, data, id) {
var httpObj = new ActiveXObject("Msxml2.XMLHTTP.6.0"); //new XMLHttpRequest();
httpObj.onreadystatechange = function() {
if (httpObj.readyState == 4)
ResponseCallback(httpObj, id);
};
httpObj.Open(method, url, true);
httpObj.Send(data);
}
function ResponseCallback(httpObj, id) {
WScript.Echo(id); //alert(id);
WScript.Echo(httpObj.ResponseText); //alert(httpObj.ResponseText);
}
//It could now be used like this:
AddRequest("GET","http://example.com/","",1);
AddRequest("GET","https://www.facebook.com","",2);
WScript.Echo("all requests sent"); //alert("all requests sent");
//these requests are all done at the same time
//and every time a request has finished it calls the ResponseCallback() function,
//telling it which request has finished
卷曲只是似乎是完全不同的,没有必要比的XmlHttpRequest更复杂,即使两者只是发送HTTP请求...
这里是我的第一种方法(基于hogren的答案):
#include "stdafx.hpp"
#include <iostream> //#include <stdio.h>
#include <curl.h>
#include <pthread.h>
#include <map>
#include <string>
using namespace std;
bool printing = false; //will allow us to prevent prints overlapping each other
struct requestStruct { //will allow us to pass more than one argument to the threaded functions
int id;
const char* url;
const char* method;
const char* body;
map<const char*, const char*> headers;
const char* proxy;
int timeout;
};
struct responseStruct { //will allow us to return more than one value from the Request function
long statusCode;
//map<const char*, const char*> headers;
const char* body;
};
size_t writeToString(void *ptr, size_t size, size_t count, void *stream) {
((string*)stream)->append((char*)ptr, 0, size* count);
return size* count;
}
static void *ResponseCallback(int id, struct responseStruct *response) {
long statusCode = response -> statusCode;
//map<const char*, const char*> headers = response -> headers;
const char* body = response -> body;
//while (printing) {} //wait for other threads to stop printing
printing = true; //tell other threads to not print anything
cout << id << " response received! Code: " << statusCode << endl << body << endl;
printing = false; //tell other threads printing is okay again
return NULL;
}
struct responseStruct HttpRequest(const char* url, const char* method, const char* body, map<const char*, const char*> &headers, const char* proxy, long timeout) {
CURL *curl;
curl = curl_easy_init();
long statusCode = 0;
map<const char*, const char*> respHeaders;
string respBody;
string _url(url);
string _method(method);
string _proxy(proxy);
struct curl_slist *headerList = NULL;
string headerString;
curl_easy_setopt(curl, CURLOPT_URL, url); //set url
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method); //set method
for (auto header=headers.begin(); header!=headers.end(); ++header) { //make header list
headerString = header->first;
headerString.append(": ").append(header->second);
headerList = curl_slist_append(headerList, headerString.c_str());
//cout << headerString << '\n';
}
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList); //set headers
if (_method == "POST" || _method == "PUT" || _method == "DELETE") //set body if the request method would allow it
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
if (_url.find(string("https://")) != string::npos) //set ssl verifypeer if it's an https url
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
if (_proxy != "") //set proxy
curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
if (timeout != 0) //set timeout
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); //follow redirects
//curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, writeToString);
//curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &respHeaders); //to receive response headers
//??
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeToString);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &respBody); //to receive response body
curl_easy_perform(curl); //send the request
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode); //get status code
struct responseStruct response;
response.statusCode = statusCode;
//response.headers;
response.body = respBody.c_str();
curl_easy_cleanup(curl);
return response;
}
static void *AddRequest(void *arguments) {
// get arguments:
struct requestStruct *args = (struct requestStruct*)arguments;
int id = args->id;
const char* url = args->url;
const char* method = args->method;
const char* body = args->body;
map<const char*, const char*> headers = args->headers;
const char* proxy = args->proxy;
int timeout = args->timeout;
// print arguments:
//while (printing) {} //wait for other threads to stop printing
//printing = true; //tell other threads to not print anything
// cout << id << endl << url << endl << method << endl;
//printing = false; //tell the other threads it's okay to print again now
struct responseStruct response = HttpRequest(url, method, body, headers, proxy, timeout);
ResponseCallback(id,&response);
pthread_exit(0);
return NULL;
}
int main() {
//map<const char*, const char*> headers;
//headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0";
//struct responseStruct response = HttpRequest("https://facebook.com", "GET", "", headers, "localhost:8888", 6000);
//cout << response.body << endl;
pthread_t threads[3];
struct requestStruct reqArguments[3];
map<const char*, const char*> headers;
headers["User-Agent"] = "Mozilla/5.0 (Windows NT 6.2; WOW64; rv:32.0) Gecko/20100101 Firefox/32.0";
const char* proxy = "";
reqArguments[0].id = 0;
reqArguments[0].url = "https://www.facebook.com/";
reqArguments[0].method = "GET";
reqArguments[0].headers = headers;
reqArguments[0].body = "";
reqArguments[0].proxy = proxy;
reqArguments[0].timeout = 6000;
pthread_create(&threads[0], NULL, &AddRequest, (void *)&reqArguments[0]); //create a thread on AddRequest() passing a full struct of arguments
reqArguments[1].id = 1;
reqArguments[1].url = "https://www.facebook.com/";
reqArguments[1].method = "GET";
reqArguments[1].headers = headers;
reqArguments[1].body = "";
reqArguments[1].proxy = proxy;
reqArguments[1].timeout = 6000;
pthread_create(&threads[1], NULL, &AddRequest, (void *)&reqArguments[1]); //create a thread on AddRequest() passing a full struct of arguments
reqArguments[2].id = 2;
reqArguments[2].url = "https://www.facebook.com/";
reqArguments[2].method = "GET";
reqArguments[2].headers = headers;
reqArguments[2].body = "";
reqArguments[2].proxy = proxy;
reqArguments[2].timeout = 6000;
pthread_create(&threads[2], NULL, &AddRequest, (void *)&reqArguments[2]); //create a thread on AddRequest() passing a full struct of arguments
getchar(); //prevent console from closing instantly
return 0;
}
我真的不知道,如果我做正确的整个并行线程的事..
有一些问题:
1.出于某种原因,只有第一个请求成功的人,甚至没有发送。
除非我取消注释主函数的前4行,它会在没有新线程的情况下直接请求,但我显然不想使用该代码。
2. HttpRequest()函数没有正确返回响应html代码,I only receive garbage。
我认为问题2可能是HttpRequest()的返回结构的指针相关问题,但我无法修复它。 :(
3.我最后一个也不是那么重要的问题是我不知道如何接收响应标题并将它们放在地图中。
btw:我正在编译Visual C++ 2010,我在调试。使用Fiddler
如果你想用curl在C++中编写异步的代码,你需要'multi'接口(http://curl.haxx.se/libcurl/c/libcurl-multi.html),而不是'easy'。它不会像在JavaScript中那么容易。 – Arthur 2014-09-05 13:29:58
我建议你看看http://cpp-netlib.org/。它是一个内置http客户端的C++ 11网络库。 – PovilasB 2014-09-07 13:08:32
你有没有尝试过类型化参数(struct requestStruct *)? – Silvester 2014-09-11 18:14:36