内容简介: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中的调用链技术
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。