内容简介:C语言实现GET请求调用API
使用 python
可以很容易地实现简单的 HTTP
请求,因为系统库封装了构建 HTTP
请求报文的底层操作,面向用户的是简单地函数调用,而通过 C语言 实现 HTTP
请求必须了解 HTTP
协议的原理,这里推荐经典的 《HTTP权威指南》 和入门的 《图解HTTP》
HTTP协议简介
HTTP报文分为请求报文和响应报文,报文分为三个部分:起始行,首部块和正文主题,这里着重谈谈请求报文的构建
另外,使用浏览器的调试工具->网络选项也能直观地分析每个HTTP请求的特征
C实现HTTP请求的细节
请求报文
请求报文的格式如下:
<method> <requests-URL> <version> <headers> <entire body>
method
代表对服务器资源获取的动作,常见的有 get
和 post
。
requests-URL
代表请求的统一资源定位符,也就是完整的网页链接。
version
是 HTTP
版本,目前已 HTTP1.1
最为常见。
headers
表示请求报文的首部,常见的防盗链 refer
,浏览器信息 user-agent
等等都在这里定义,注意这里的每一项属性参数以每行末尾的 \n\r
来分隔。
响应报文
和请求报文类似,只介绍其独特的地方
<version> <status> <reason-phrase> <headers> <entire body>
status
和 reason-phrase
表示状态码和原因短语,比较常见的就是 200 OK
, 404 Not Found
, 502 Bad Gateway
entire body
报文的实体部分,一般来说就是发往浏览器的整个 HTML
文件,当然还有图片,二进制文件等等其他一些资源。
GET请求
创建socket
//定义的缓冲区用来存放socket发送和接受的数据 也就是HTTP请求和响应报文 char buff[2048]; int sockfd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sockfd < 0 ) { printf( "create socket error!\n" ); exit( -1 ); } struct sockaddr_in serveraddr; memset( &serveraddr, 0, sizeof(serveraddr) ); serveraddr.sin_family = AF_INET; // HTTP服务 TCP 80端口 serveraddr.sin_port = htons( 80 ); // apis.map.qq.com 解析地址 inet_pton( AF_INET, "182.254.11.29", &serveraddr.sin_addr.s_addr ); if ( connect( sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr) ) < 0 ) { printf( "connect error!\n" ); exit( -2 ); } memset( buff, 0, sizeof(buff) );
测试用例还是上回 腾讯地图 的 API
,注册过后拿到开发密钥XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
注意下应用类型选择 浏览器 ,名字什么的可以随便填
根据API说明构建HTTP请求报文
snprintf( buff, sizeof(buff) - 1, "GET /ws/geocoder/v1/?location=%s,%s&key=XXXXX-XXXXX-XXXXX-XXXXX-XXXXX&get_poi=1&coord_type=3 HTTP/1.1\r\n" "Host: apis.map.qq.com\r\n" "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" "\r\n", argv[1], argv[2] );
发送请求
if ( write( sockfd, buff, strlen( buff ) ) != strlen( buff ) ) { return(-1); /* 发送出错返回-1 */ } memset( buff, 0, sizeof(buff) ); size_t size; if ( (size = read( sockfd, buff, sizeof(buff) ) ) < 0 ) /* 从服务器端读取 */ { return(-1); } printf( "HTTP响应报文:\n%s\n", buff ); close( sockfd ); }
响应实例
HTTP响应报文: HTTP/1.1 200 OK X-LIMIT: current_qps=1; limit_qps=5; current_pv=21; limit_pv=10000 Content-Type: application/json; charset=utf-8 Content-Length: 9528 Date: Mon, 29 May 2017 00:27:04 GMT Connection: keep-alive { "status": 0, "message": "query ok", "request_id": "6274752708360945856", "result": { "location": { "lat": 30.542301, "lng": 114.392483 }, "address": "湖北省武汉市武昌区东湖东路", "formatted_addresses": { "recommend": "武昌区东湖生态旅游风景区东湖", "rough": "武昌区东湖生态旅游风景区东湖" ...
响应报文长度可能会超过 buff
的长度,所以会显示不全
数据处理
拿到响应报文后,还需要把有用的地理信息从字符数组中提取出来,这里我只提取 recommend
和 rough
的值,如果需要解析整个 JSON
需要用到 cJson
库。
匹配处理
字符数组匹配子串用的是 strstr()
函数
int repResolve( char *rep, char *recmd, char* rough ) { //p q接受匹配后的起始地址 char * p = strstr( rep, "recommend" ); char * q = strstr( rep, "rough" ); if ( p ) { // recommend": " r到"一共13个字符 p = p + 13; // 地名" 一直匹配到引号的结束 while (!((*p) == '\"')) { //字符串复制 *recmd++ = *p++; } //字符串末尾补0 *recmd = '\0'; } if ( q ) { q += 9; while (!((*q) == '\"')) { *rough++ = *q++; } *rough = '\0'; } return(0); }
完整代码
#include <string.h> #include <arpa/inet.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> //填写你自己申请的APIkey const char* apikey=""; int repResolve( char *rep, char *recmd, char* rough ) { //p q接受匹配后的起始地址 char * p = strstr( rep, "recommend" ); char * q = strstr( rep, "rough" ); if ( p ) { // recommend": " r到"一共13个字符 p = p + 13; // 地名" 一直匹配到引号的结束 while (!((*p) == '\"')) { //字符串复制 *recmd++ = *p++; } //字符串末尾补0 *recmd = '\0'; } if ( q ) { q += 9; while (!((*q) == '\"')) { *rough++ = *q++; } *rough = '\0'; } return(0); } int main( int argc, char **argv ) { char buff[2048] = { '\0' }; char recmd[512] = { '\0' }; char rough[512] = { '\0' }; int sockfd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sockfd < 0 ) { printf( "create socket error!\n" ); exit( -1 ); } struct sockaddr_in serveraddr; memset( &serveraddr, 0, sizeof(serveraddr) ); serveraddr.sin_family = AF_INET; serveraddr.sin_port = htons( 80 ); inet_pton( AF_INET, "182.254.11.29", &serveraddr.sin_addr.s_addr ); /* inet_pton(AF_INET, "apis.map.qq.com", &serveraddr.sin_addr.s_addr); */ if ( connect( sockfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr) ) < 0 ) { printf( "connect error!\n" ); exit( -2 ); } memset( buff, 0, sizeof(buff) ); snprintf( buff, sizeof(buff) - 1, "GET /ws/geocoder/v1/?location=%s,%s&key=%s&get_poi=1&coord_type=3 HTTP/1.1\r\n" "Host: apis.map.qq.com\r\n" "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n" "\r\n", argv[1], argv[2] ,apikey ); if ( write( sockfd, buff, strlen( buff ) ) != strlen( buff ) ) { return(-1); } memset( buff, 0, sizeof(buff) ); size_t size; if ( (size = read( sockfd, buff, sizeof(buff) ) ) < 0 ) { return(-1); } /*步骤4:关闭socket*/ printf( "\nTCP响应长度: %d\n", size ); printf( "HTTP响应报文长度: %d\n", strlen( buff ) ); //printf( "HTTP响应报文:\n%s\n", buff ); printf("HTTP响应报文:\n%.*s\n", 290, buff); repResolve( buff, recmd, rough ); printf( "当前坐标: \n%s %s\n", argv[1], argv[2] ); printf( "当前位置: \n\t%s\n", recmd ); printf( "\t%s\n", rough ); close( sockfd ); }
第一个参数接受纬度,第二个参数接受精度,请求效果如下
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 使用 SpringAOP 获取一次请求流经方法的调用次数和调用耗时
- 使用 SpringAOP 获取一次请求流经方法的调用次数和调用耗时
- PHP如何发送GET/POST请求调用API
- 为何一次请求会有两次HttpServlet:service调用?
- 直观讲解-RPC调用和HTTP调用的区别
- 调用链系列一:解读UAVStack中的调用链技术
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Essential C++中文版
[美] Stanley B. Lippman / 侯捷 / 华中科技大学出版社 / 2001-8 / 39.80元
书中以4个面向来表现C++的本质:procedural(程序性的)、generic(泛型的)、object-based(个别对象的)、object-oriented(面向对象的),全书围绕着一系列逐渐繁复的程序问题,以及用以解决这些问题的语言特性。循此方式,读者不只学到C++的函数和结构,也会学习到它们的设计目的和基本原理。一起来看看 《Essential C++中文版》 这本书的介绍吧!