HTTPCLIENT源码分析
—mt7686 httpclient模块
http是应用层的协议,在传输层使用tcp协议,可以从其源码分析,中间层就是调用socket(基于lwip协议)编程加上http协议实现,下面看源码。想看看其源码的可以从下面目录下载。
下载目录:点击这里
路径在middleware/third_party/httpclient中。
源码分析
以前已经介绍过httpclient api的使用了,此处不在赘述,本文主要介绍下面代码的中间层实现。
static HTTPCLIENT_RESULT httpclient_common(httpclient_t *client, char *url, int method, httpclient_data_t *client_data)
{
HTTPCLIENT_RESULT ret = HTTPCLIENT_ERROR_CONN;
ret = httpclient_connect(client, url);
if (!ret) {
ret = httpclient_send_request(client, url, method, client_data);
if (!ret) {
ret = httpclient_recv_response(client, client_data);
}
}
httpclient_close(client);
return ret;
}
1.httpclient_connect函数
创建tcp连接过程,此处贴出主要代码。
//解析url,确认目的地址
httpclient_parse_url(url, scheme, sizeof(scheme), host, sizeof(host), &(client->remote_port), path, HTTPCLIENT_MAX_URL_LEN);
//检查http or https后,确认端口
if (client->remote_port == 0) {
if (client->is_http) {
client->remote_port = HTTP_PORT;
} else {
client->remote_port = HTTPS_PORT;
}
}
//以tcp,创建tcp连接
if (client->is_http)
ret = httpclient_conn(client, host);
//分析httpclient_conn;创建套接字,填充地址结构,连接
//填充地址结构
memset( &hints, 0, sizeof( hints ) );
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
snprintf(port, sizeof(port), "%d", client->remote_port) ;
if ( getaddrinfo( host, port , &hints, &addr_list ) != 0 ) {
DBG("getaddrinfo != 0, return HTTPCLIENT_UNRESOLVED_DNS");
return HTTPCLIENT_UNRESOLVED_DNS;
}
//创建套接字
client->socket = (int) socket( cur->ai_family, cur->ai_socktype,
cur->ai_protocol );
//连接
connect( client->socket, cur->ai_addr, (int)cur->ai_addrlen )
至此,函数如果正确返回,就创建tcp连接成功了,接下来就是发送接收数据了,最终应该调用的send(),recv()函数接口。
2.httpclient_send_request函数
了解这个函数接口,必须了解下http报文结构了,这里以请求报文为例。
发送http数据之前,都会先发送报文首部数据,然后再看这个函数实现。
HTTPCLIENT_RESULT httpclient_send_request(httpclient_t *client, char *url, int method, httpclient_data_t *client_data)
{
int ret = HTTPCLIENT_ERROR_CONN;
if (client->socket < 0) {
return (HTTPCLIENT_RESULT)ret;
}
//发送报文首部
ret = httpclient_send_header(client, url, method, client_data);
if (ret != 0) {
return (HTTPCLIENT_RESULT)ret;
}
if (method == HTTPCLIENT_POST || method == HTTPCLIENT_PUT) {
//发送用户数据
ret = httpclient_send_userdata(client, client_data);
}
DBG("httpclient_send_request() result:%d, client:%p", ret, client);
return (HTTPCLIENT_RESULT)ret;
}
HTTP 首部字段是构成 HTTP 报文的要素之一。在客户端与服务器之
间以 HTTP 协议进行通信的过程中,无论是请求还是响应都会使用首
部字段,它能起到传递额外重要信息的作用。代码实现不做解释了。
注意:关于这个api,想发送数据,跟到函数最后肯定调用lwip协议栈中send函数。
如下,httpclient最终只要发送数据都会调用下面接口
send(sock_fd, data + written_len, length - written_len, 0);
3.httpclient_recv_response函数
接收http响应的函数接口,同理,接收的也是一个响应报文,除了接收数据后,对接收的报文也需要解析,这就是这个函数的具体功能了。
响应报文如下:
接收数据:
httpclient_recv(client, data + len, 1, header_buf_len - len - 1, &new_trf_len);
最终调用lwip协议栈中的接口recv
recv(client->socket, buf + readLen, max_len - readLen, 0);
关于http请求与响应的报文首部,想详细了解和学习的,可以取查阅《图解http》。