在单片机等平台常常没有资源使用curl等库去实现http请求,通常做法是使用tcp发请求体模拟发起http请求。本文给出一个简单的请求代码,目前只实现了post请求,其他请求就是请求体有细微差别,可自行拓展。
http_tcp.h
#ifndef __HTTP_TCP_H_
#define __HTTP_TCP_H_
#include
#include
#include
#include "lwip/sockets.h"
#include
#include
#include
#include "log.h"
typedef enum{
HTTP_GET = 0,
HTTP_POST,
HTTP_HEAD,
HTTP_PUT,
HTTP_DELETE,
HTTP_OPTIONS,
HTTP_TRACE,
HTTP_CONNECT,
}HTTP_TYPE;
int domainNameResolution( char *domainName, char *ip );
int tcpEmulatesHttpRequests( char *ip, int port, HTTP_TYPE type,char *uri, char *request_body, char *result );
#endif
http_tcp.c
#include "http_tcp.h"
#define HTTP_POST_HEAD "POST %s HTTP/1.1\r\n\
Accept-Language: zh-CN,zh;q=0.9\r\n\
Host:%s\r\n\
Connection: close\r\n\
Content-Length:%ld\r\n\
Content-Type:application/x-www-form-urlencoded;charset=UTF-8\r\n\
\r\n\
%s\r\n\
"
/**
* @brief 域名解析,获取域名对应的ip
* @param[in] domainName 域名
* @param[out] ip 获取的ip地址
* @retval 0 success
* @retval <0 fail
* */
int domainNameResolution( char *domainName, char *ip )
{
if( ( ip == NULL ) || ( domainName == NULL ) )
{
logError("ip and domain name can't be NULL!!!");
return -1;
}
const struct addrinfo hints = {
.ai_family = AF_INET,
.ai_socktype = SOCK_STREAM,
};
struct addrinfo *res;
struct in_addr *addr;
int err = getaddrinfo( domainName, NULL, &hints, &res);
if(err != 0 || res == NULL) {
logError( "DNS lookup failed err=%d res=%p", err, res);
return -1;
}
addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
sprintf( ip, "%s", inet_ntoa(*addr) );
return 0;
}
/**
* @brief 使用TCP模拟请求HTTP
* @param[in] ip 请求的IP地址
* @param[in] port 请求的IP端口
* @param[in] type 请求类型,根据不同的请求类型构造请求头
* @param[in] uri 资源标识符
* @param[in] request_body 请求参数
* @param[out] result 请求返回结果
* @retval 0 success
* @retval <0 fail
* */
int tcpEmulatesHttpRequests( char *ip, int port, HTTP_TYPE type,char *uri, char *request_body, char *result )
{
logInfo("will connect gateway ssid : %s port:%d",
ip, port);
//新建socket
int connect_socket = socket(AF_INET, SOCK_STREAM, 0);
if (connect_socket < 0)
{
logError("socket errno %d,%s", errno, strerror(errno) );
close(connect_socket);
return -1;
}
//配置连接服务器信息:端口+ip
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = inet_addr(ip);
logInfo("connectting server...");
//连接服务器
if (connect(connect_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
//打印报错信息
logError("connect failed!");
close(connect_socket);
if( ( errno == 113 ) /*&& ( getPowerStatus() != 0 )*/ )
{
/* esp_sleep_enable_timer_wakeup( 1 ); // 重启,重新联网 */
/* esp_deep_sleep_start(); */
}
return -1;
}
logInfo("connect success!");
struct timeval timeout;
fd_set readSet;
int err = -1;
do{
timeout.tv_sec = 20;
timeout.tv_usec = 0;
if (setsockopt( connect_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1)
{
logError("setsockopt( SO_SNDTIMEO ) failed, %s", strerror(errno));
break;
}
char send_buf[1024] = {0};
memset( send_buf, 0, sizeof(send_buf) );
switch( type ){
case HTTP_POST:
snprintf( send_buf, sizeof( send_buf ), HTTP_POST_HEAD, uri, ip, strlen(request_body), request_body );
break;
default:
break;
}
logInfo("client send:\n%s\n",send_buf);
int ret = send(connect_socket,send_buf, strlen(send_buf)+1, 0 );
if( ret <= 0 )
{
logError("%s: ret = %d, errno %d, %s", __func__, ret, errno, strerror(errno) );
if( ( errno == 113 ) /*&& ( getPowerStatus() != 0 )*/ )
{
/* esp_sleep_enable_timer_wakeup( 1 ); // 重启,重新联网 */
/* esp_deep_sleep_start(); */
}
break;
}
FD_ZERO( &readSet );
FD_SET( connect_socket, &readSet );
timeout.tv_sec = 20;
timeout.tv_usec = 0;
ret = select( connect_socket+1, &readSet, 0, 0, &timeout );
if (ret <= 0) {
logE("socket select failure!!! ret = %d, %d, %s", ret, errno, strerror(errno));
break;
}
if (!FD_ISSET(connect_socket, &readSet)) {
logE("connect_socket = %d is not read", connect_socket);
break;
}
/* ret = read(connect_socket,result, 1024); */
ret = recv(connect_socket,result, 1024, 0);
if( ret <= 0 )
{
logError("%s: ret = %d, errno %d, %s", __func__, ret, errno, strerror(errno) );
if( ( errno == 113 ) /*&& ( getPowerStatus() != 0 )*/ )
{
/* esp_sleep_enable_timer_wakeup( 1 ); // 重启,重新联网 */
/* esp_deep_sleep_start(); */
}
break;
}
err = 0;
}while(0);
close(connect_socket);
return err;
}
其中log.h见https://editor.csdn.net/md/?articleId=119873836一文