1. libcurl 的參考文檔以下
css
CURLOPT_HEADERFUNCTION html
Pass a pointer to a function that matches the following prototype: size_t function( void *ptr, size_t size, size_t nmemb, void *userdata);. This function gets called by libcurl as soon as it has received header data. The header callback will be called once for each header and only complete header lines are passed on to the callback. Parsing headers is very easy using this. The size of the data pointed to by ptr is sizemultiplied with nmemb. Do not assume that the header line is zero terminated! The pointer named userdata is the one you set with the CURLOPT_WRITEHEADER option. The callback function must return the number of bytes actually taken care of. If that amount differs from the amount passed to your function, it'll signal an error to the library. This will abort the transfer and return CURL_WRITE_ERROR. linux
A complete HTTP header that is passed to this function can be up to CURL_MAX_HTTP_HEADER (100K) bytes. nginx
If this option is not set, or if it is set to NULL, but CURLOPT_HEADERDATA (CURLOPT_WRITEHEADER) is set to anything but NULL, the function used to accept response data will be used instead. That is, it will be the function specified with CURLOPT_WRITEFUNCTION, or if it is not specified or NULL - the default, stream-writing function. json
It's important to note that the callback will be invoked for the headers of all responses received after initiating a request and not just the final response. This includes all responses which occur during authentication negotiation. If you need to operate on only the headers from the final response, you will need to collect headers in the callback yourself and use HTTP status lines, for example, to delimit response boundaries. ubuntu
When a server sends a chunked encoded transfer, it may contain a trailer. That trailer is identical to a HTTP header and if such a trailer is received it is passed to the application using this callback as well. There are several ways to detect it being a trailer and not an ordinary header: 1) it comes after the response-body. 2) it comes after the final header line (CR LF) 3) a Trailer: header among the regular response-headers mention what header(s) to expect in the trailer. api
For non-HTTP protocols like FTP, POP3, IMAP and SMTP this function will get called with the server responses to the commands that libcurl sends. app
CURLOPT_WRITEHEADER curl
(This option is also known as CURLOPT_HEADERDATA) Pass a pointer to be used to write the header part of the received data to. If you don't use CURLOPT_WRITEFUNCTION or CURLOPT_HEADERFUNCTIONto take care of the writing, this must be a valid FILE * as the internal default will then be a plain fwrite(). See also the CURLOPT_HEADERFUNCTION option above on how to set a custom get-all-headers callback. ide
2. 完整code以下
- /*------------------------------------------------------------------------------------------
- 名稱: http_cloud_curl_callbak.c
- 利用libcurl的API的回調機制實現雲端通信. http://curl.haxx.se/libcurl/c/curl_easy_setopt.html
- CURLOPT_HEADERFUNCTION, CURLOPT_HEADERDATA: 只處理HTTP頭部數據,處理與下載數據回調的處理相同.
- sina的股票接口由於無HTTP頭部返回所以不能應用此.
- -------------------------------------------------------------------------------------------*/
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <curl/curl.h>
- #include <assert.h>
- #include "../http_cloud.h"
- #define CURL_HEADER (1) //0=利用 CURLOPT_WRITEFUNCTION, 1=利用 CURLOPT_HEADERFUNCTION
- #define DBG printf
- #define CURL_DBG (0) //1=開啓curl的調試
- //-----------------------------------------------------------------------------------------
- static void get_local_time(char *pc_str)
- {
- time_t now;
- struct tm *timenow;
- assert(pc_str != NULL);
- time(&now);
- timenow = localtime(&now);
- sprintf(pc_str, "%04d-%02d-%02dT%02d:%02d:%02d", timenow->tm_year+1900, timenow->tm_mon+1, timenow->tm_mday,
- timenow->tm_hour, timenow->tm_min, timenow->tm_sec);
- }
- size_t callback_get_head(void *ptr, size_t size, size_t nmemb, void *userp)
- {
- DBG("ptr = %s, userp = %s\n", ptr, userp);
- strcat(userp, ptr);
- return size * nmemb; //必須返回這個大小, 不然只回調一次
- }
- static char connect_cloud(char *pc_ret, const char *host_addr, const int portno,
- const char *pc_method, struct curl_slist *http_headers)
- {
- CURL *curl;
- CURLcode res = CURLE_GOT_NOTHING;
- assert((pc_ret != NULL) && (host_addr != NULL) && (http_headers != NULL) && (pc_method != NULL));
- //curl_global_init(CURL_GLOBAL_DEFAULT);
- curl = curl_easy_init();
- if (!curl) {
- fprintf(stderr, "--- curl init Error!\n");
- return 0;
- }
- curl_easy_setopt(curl, CURLOPT_URL, host_addr);
- curl_easy_setopt(curl, CURL_HTTP_VERSION_1_1, 1L); //HTTP 1.1
- curl_easy_setopt(curl, CURLOPT_TIMEOUT, 3); //設置超時, 單位爲秒
- //curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1); //屏蔽其它信號, makes libcurl NOT ask the system to ignore SIGPIPE signals
- #if (CURL_DBG == 1)
- curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); //調試用: 顯示交互明細,
- //curl_easy_setopt(curl, CURLOPT_HEADER, 1L); //調試用: 只顯示頭行的返回
- #endif
- #if 1
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, pc_method);
- #if (CURL_HEADER == 0)
- curl_easy_setopt(curl, CURLOPT_HEADER, 1); //下載數據包括HTTP頭部, The default value for this parameter is 0.
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, callback_get_head); //下載數據的回調函數
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, pc_ret); //下載數據的指針
- #else
- curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, callback_get_head); //頭部數據的回調函數
- curl_easy_setopt(curl, CURLOPT_HEADERDATA, pc_ret); //頭部數據的指針
- #endif
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, http_headers); //多行的HTTP頭部
- #endif
- res = curl_easy_perform(curl);
- if(res != CURLE_OK)
- fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
- curl_easy_cleanup(curl);
- return 1;
- }
- #if (YEELINK == 1)
- int yeelink_create_data(const int device_id, const int sensor_id, const float device_value)
- {
- char pc_ret[500], request[1024], pc_json[100], pc_time[30], pc_host_file[100], pc_header[100], pc_url[200], ret;
- int len;
- sprintf(pc_host_file, "v1.0/device/%d/sensor/%d/datapoints", device_id, sensor_id);
- sprintf(pc_header, "U-ApiKey: %s", YEELINK_API_KEY);
- get_local_time(pc_time);
- sprintf(pc_json, "{\"timestamp\":\"%s\",\"value\":%.2f}", pc_time, device_value);
- len = strlen(pc_json);
- //sprintf(request, "POST /%s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\n%s\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: Close\r\n\r\n%s\r\n",
- // pc_host_file, YEELINK_HOST, pc_header, len, pc_json);
- //DBG("request = %s\n", request);
- //組織請求頭部, 自動末尾添加'\r\n'
- struct curl_slist *http_headers = NULL;
- //sprintf(request, "POST /%s HTTP/1.1", pc_host_file);
- //http_headers = curl_slist_append(http_headers, request); //此部分在url中體現
- sprintf(request, "Host: %s", YEELINK_HOST);
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", pc_header);
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", "Accept: */*");
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "Content-Length: %d", len);
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", "Content-Type: application/x-www-form-urlencoded");
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", "Connection: Close");
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "\r\n%s", pc_json);
- http_headers = curl_slist_append(http_headers, request);
- #if 0
- struct curl_slist *tmp = http_headers;
- printf("HTTP request = ");
- while(tmp) {
- printf("%s\n", tmp->data);
- tmp = tmp->next;
- }
- #endif
- //發送請求, 再判斷返回值
- //url = api.yeelink.net/v1.0/device/391/sensor/497/datapoints
- sprintf(pc_url, "%s/%s", YEELINK_HOST, pc_host_file);
- ret = connect_cloud(pc_ret, pc_url, YEELINK_PORT, HTTP_POST, http_headers);
- //釋放 http_headers資源
- curl_slist_free_all(http_headers);
- DBG("yeelini ret = %s\n", pc_ret);
- return(ret);
- }
- #endif
- #if (LEWEI50 == 1)
- //curl --request POST http://www.lewei50.com/api/V1/Gateway/UpdateSensors/01 --data "[{\"Name\":\"T1\",\"Value\":\"23.08\"}]" --header "userkey:36be8ff22f794f1e8a0bee3336eef237"
- int lewei50_create_data(const char *device_id, const float device_value)
- {
- char pc_ret[500], request[1024], pc_json[100], pc_header[100], pc_url[200], *pc_data, ret;
- int len;
- assert(device_id != NULL);
- sprintf(pc_header, "userkey: %s", LEWEI50_USER_KEY);
- sprintf(pc_json, "[{\"Name\":\"%s\",\"Value\":\"%.2f\"}]", device_id, device_value);
- len = strlen(pc_json);
- //sprintf(request, "POST /%s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\n%s\r\nContent-Length: %d\r\nContent-Type: application/x-www-form-urlencoded\r\nConnection: Close\r\n\r\n%s\r\n",
- // LEWEI50_HOST_FILE, LEWEI50_HOST, pc_header, len, pc_json);
- //DBG("request = %s\n", request);
- //組織請求頭部, 自動末尾添加'\r\n'
- struct curl_slist *http_headers = NULL;
- //sprintf(request, "POST /%s HTTP/1.1", pc_host_file);
- //http_headers = curl_slist_append(http_headers, request); //此部分在url中體現
- sprintf(request, "Host: %s", LEWEI50_HOST);
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", pc_header);
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", "Accept: */*");
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "Content-Length: %d", len);
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", "Content-Type: application/x-www-form-urlencoded");
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "%s", "Connection: Close");
- http_headers = curl_slist_append(http_headers, request);
- sprintf(request, "\r\n%s", pc_json);
- http_headers = curl_slist_append(http_headers, request);
- //發送請求, 再判斷返回值
- //url = www.lewei50.com/api/V1/gateway/UpdateSensors/01
- sprintf(pc_url, "%s/%s", LEWEI50_HOST, LEWEI50_HOST_FILE);
- ret = connect_cloud(pc_ret, pc_url, LEWEI50_PORT, HTTP_POST, http_headers);
- //釋放 http_headers資源
- curl_slist_free_all(http_headers);
- //{"Successful":true,"Message":"Successful. "}, 模式不一樣此返回數據的位置也不一樣.
- DBG("lewei50 ret = %s\n", pc_ret);
- ret = 0;
- pc_data = strstr(pc_ret, HTTP_RET_OK);
- if (pc_data) {
- pc_data += strlen(HTTP_RET_OK);
- //只返回最後的 json數據
- pc_data = strstr(pc_data, "\r\n\r\n");
- if (pc_data) {
- pc_data += 4;
- ret = 1;
- DBG("lewei50 data = %s\n", pc_data);
- }
- }
- return(ret);
- }
- #endif
- //-------------------------------------------------------------------
- int main(void)
- {
- float f_value = 15.02;
- int i_tmp;
- time_t t;
- srand((unsigned)time(&t)); //初始化隨機種子, 不然隨機數不隨機
- i_tmp = rand();
- i_tmp -= (i_tmp >> 4 << 4);
- f_value += i_tmp;
- #if (YEELINK == 1)
- yeelink_create_data(YEELINK_DEVICE_ID, YEELINK_SENSOR_ID, f_value);
- #endif
- #if (LEWEI50 == 1)
- lewei50_create_data(LEWEI50_DEVICE_ID, f_value);
- #endif
- return 1;
- }
3. Makefile以下
- OPENWRT = 1
- ifeq ($(OPENWRT), 1)
- CC = ~/OpenWrt-SDK-ar71xx-for-linux-i486-gcc-4.6-linaro_uClibc-0.9.33.2/staging_dir/toolchain-mips_r2_gcc-4.6-linaro_uClibc-0.9.33.2/bin/mips-openwrt-linux-gcc
- CFLAGS += -I ~/openwrt-lib/include -L ~/openwrt-lib/lib
- LFLAGS += -lcurl -lcrypto -lz -lssl
- else
- CC = gcc
- LFLAGS += -lcurl
- endif
- CFLAGS += -Wall -O2
- #CFLAGS += -g
- #可執行文件名和相關的obj文件
- APP_BINARY = http_cloud
- SRCS += http_cloud_curl_callback.c
- OBJS = $(SRCS:.c=.o)
- all: APP_FILE
- APP_FILE: $(OBJS)
- $(CC) $(CFLAGS) $(OBJS) -o $(APP_BINARY) $(LFLAGS)
- .PHONY: clean
- clean:
- @echo "cleanning project"
- $(RM) *.a $(OBJS) *~ *.so *.lo $(APP_BINARY)
- @echo "clean completed"
4. 運行結果以下xxg@xxg-desktop:~/1-wire/http_cloud/libcurl$ ./http_cloudptr = HTTP/1.1 200 OK, userp = ptr = Server: nginx/1.1.19, userp = HTTP/1.1 200 OKptr = Date: Mon, 25 Nov 2013 01:29:37 GMT, userp = HTTP/1.1 200 OKServer: nginx/1.1.19ptr = Content-Type: text/html, userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTptr = Transfer-Encoding: chunked, userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlptr = Connection: close, userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlTransfer-Encoding: chunkedptr = X-Powered-By: PHP/5.3.10-1ubuntu3.6, userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: closeptr = Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/, userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6ptr = P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM", userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/ptr = , userp = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"yeelini ret = HTTP/1.1 200 OKServer: nginx/1.1.19Date: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: text/htmlTransfer-Encoding: chunkedConnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"ptr = HTTP/1.1 200 OK, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"ptr = Date: Mon, 25 Nov 2013 01:29:37 GMT, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKptr = Content-Type: application/json; charset=utf-8, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTptr = Content-Length: 44, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: application/json; charset=utf-8ptr = Connection: close, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: application/json; charset=utf-8Content-Length: 44ptr = Cache-Control: private, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: application/json; charset=utf-8Content-Length: 44Connection: closeptr = Set-Cookie: SERVERID=a4a5b2bbca16d8c8b2ba6d5b6e55f36e|1385342977|1385342977;Path=/, userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: application/json; charset=utf-8Content-Length: 44Connection: closeCache-Control: privateptr = , userp = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: application/json; charset=utf-8Content-Length: 44Connection: closeCache-Control: privateSet-Cookie: SERVERID=a4a5b2bbca16d8c8b2ba6d5b6e55f36e|1385342977|1385342977;Path=/{"Successful":true,"Message":"Successful. "}lewei50 ret = onnection: closeX-Powered-By: PHP/5.3.10-1ubuntu3.6Set-Cookie: CAKEPHP=kgqv6b4rnbbtn1h1ndda514936; expires=Tue, 03-Dec-2013 09:29:37 GMT; path=/P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"HTTP/1.1 200 OKDate: Mon, 25 Nov 2013 01:29:37 GMTContent-Type: application/json; charset=utf-8Content-Length: 44Connection: closeCache-Control: privateSet-Cookie: SERVERID=a4a5b2bbca16d8c8b2ba6d5b6e55f36e|1385342977|1385342977;Path=/lewei50 data = xxg@xxg-desktop:~/1-wire/http_cloud/libcurl$