- 我的博客:https://blog.csdn.net/qq_37388044
- 我的知乎:https://www.zhihu.com/people/bbtganmin
- 联系方式:知乎私信
转载或者引用本文内容请注明来源及原作者!
目录
- 一、前言
- 二、什么是http?
- 三、浏览器是如何访问地址的?
- 四、HTTP请求报文格式
- 五、HTTP响应报文格式
- 六、代码
- 1、通过域名获取ip
- 2、创建socket并连接服务器
- 3、合成并发送请求报文,接收响应
- 最后说下GET和POST的区别
一、前言
之前在做嵌入式开发时遇到一个项目,需要用http与服务器通信,移植了一个http库,编译后发现固体太大,flash都差点不够放了,要实现的功能也就简单的信息获取和上报而已。于是我决定用socket简单的实现这些功能。
二、什么是http?
超文本传输协议(HyperTextTransferProtocol,缩写:HTTP)是一种用于分布式、协作式和超媒体信息系统的应用层协议。基于TCP/IP通信协议来传递数据。
三、浏览器是如何访问地址的?
我们平常在浏览器地址栏输入http网址后,浏览器会做以下动作:
- 从网址(url)中取出域名,然后通过DNS服务器获取到对应的IP地址。
- 通过IP地址连接到服务器(默认端口为80)。
- 浏览器合以HTTP协议合成请求报文,并发送给服务器。
- 服务器返回响应报文。
- 服务器断开TCP。
- 浏览器以HTTP协议解析响应报文,得到内容并做出相应的处理(一般会得到html文本,浏览器通过html生成页面并显示出来)。
四、HTTP请求报文格式
例如, 下面的网址:
http://www.zhihu.com/people/cheng-tian-zhe
合成请求报文应该是:
GET /people/cheng-tian-zhe HTTP/1.1
Host: www.zhihu.com
五、HTTP响应报文格式
在这里,我们只关注状态码、Content-Length字段、主体:
- 状态码200表示成功。
- Content-Length可以获取主体的大小,以便申请内存接收主体内容。(不一定有)
- 主体就是我们要得到的数据。
六、代码
下面是一个获取服务器时间的例子,esp8266通过http请求服务器时间。
请求报文:
GET /bbt/nowtime HTTP/1.1
Host: xxx.xxx.com:80
//这里需要有个"\r\n"
响应报文:
Content-Type:application/json;charset=UTF-8
Date:Tue, 17 Nov 2020 08:42:27 GMT
... //这里省略部分字段
Connection:close
{"datas":"2020-11-17 16:41:05","resp_code":0,"resp_msg":"SUCCESS","successed":true}
1、通过域名获取ip
int bbtGetIpByHostname(char *host_name, char *first_ip)
{
char **pptr;
const char *first;
struct hostent *hptr;
char str[32] = {'\0'};
if((hptr = gethostbyname(host_name)) == NULL)
{
ESP_LOGE(TAG, " gethostbyname error for host:%s", host_name);
return -1;
}
switch(hptr->h_addrtype)
{
case AF_INET:
case AF_INET6:
pptr=hptr->h_addr_list;
for(; *pptr!=NULL; pptr++)
ESP_LOGI(TAG, " address:%s", inet_ntop(hptr->h_addrtype, *pptr, str, sizeof(str)));
first = inet_ntop(hptr->h_addrtype, hptr->h_addr, str, sizeof(str));
if(first)
memcpy(first_ip, str, 32);
break;
default:
ESP_LOGI(TAG, "unknown address type");
break;
}
return 0;
}
2、创建socket并连接服务器
int socket_open_http(char *host_name, char *ip_out)
{
int socketId;
struct sockaddr_in serv_addr;
int status;
if (bbtGetIpByHostname(host_name, ip_out))
return -1;
socketId = socket(AF_INET, SOCK_STREAM, 0);
if((int)socketId < 0)
{
ESP_LOGE(TAG, "Create a socket failed!");
return -1;
}
memset(&serv_addr, 0, sizeof(serv_addr));
unsigned long net_ip = inet_addr(ip_out);
if(net_ip == INADDR_NONE) {
ESP_LOGE(TAG, "net_ip failed!");
return -1;
}
serv_addr.sin_addr.s_addr = net_ip;
serv_addr.sin_port = htons(80);
serv_addr.sin_family = AF_INET;
status = connect(socketId, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
if(status!=0) {
ESP_LOGE(TAG, "Connecting failed!");
close(socketId);
return -1;
}
ESP_LOGI(TAG, "connect sucess");
return socketId;
}
3、合成并发送请求报文,接收响应
#define BBT_HTTP_HOST_NAME "xxx.xxx.com"
#define BBT_HTTP_TIME_PATH "/bbt/nowtime"
#define BBT_HTTP_TIMEOUT_S 2
#define TCP_BUFFSIZE 1460
int bbtGetServerTime(void)
{
char http_ip[32] = {0};
int http_skt = socket_open_http(BBT_HTTP_HOST_NAME, http_ip);
if (http_skt <= 0)
return -1;
struct timeval timeout = {BBT_HTTP_TIMEOUT_S, 0};
setsockopt(http_skt, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout, sizeof(struct timeval));
setsockopt(http_skt, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout, sizeof(struct timeval));
char *http_request;
const char *GET_TIME =
"GET %s HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"\r\n";
int get_len = asprintf(&http_request, GET_TIME, BBT_HTTP_TIME_PATH, http_ip, 80);
if (get_len < 0) {
ESP_LOGE(TAG, "GET request buffer error");
goto GET_TIME_ERR;
}
int send_len = send(http_skt, http_request, get_len, 0);
free(http_request);
if (send_len < 0)
goto GET_TIME_ERR;
char tcp_buf[TCP_BUFFSIZE] = {0};
memset(tcp_buf, 0x00, TCP_BUFFSIZE);
int buff_len = recv(http_skt, tcp_buf, TCP_BUFFSIZE, 0);
if (buff_len < 0) {
ESP_LOGE(TAG, "receive data error!");
goto GET_TIME_ERR;
}
close(http_skt);
printf("tcp_buf = \n%s\n", tcp_buf);
return 0;
GET_TIME_ERR:
close(http_skt);
return -1;
}
最后说下GET和POST的区别
GET和POST都是HTTP协议中的常用请求的方法,那么它们究竟有什么不同?
事实上,对于简单的http数据请求来说,两者之间并没有多大的区别。但当时项目负责服务器的同学提供的接口一般都用POST,而不用GET,这是为什么呢?
具体原因有很多,他跟我说了主要的几点:
- GET请求的参数是有长度限制的,而POST没有。
- GET请求的参数只能是ASCII字符,而POST没有限制。
- GET请求的参数是直接显示在URL上,相比之下POST更安全。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)