现在随便的API都是Web的,让我们这些嵌入式平台好累啊.
我写了个test.php,内容简单.
<?php echo json_encode($_GET); echo json_encode($_POST);
根据TCP的协议,很简单写的出HTTP访问的方法.我用的是netconn,需要操作系统支持.
uint8_t *pageBuf;
int get_webpage(const char *url)
{
uint16_t i, j, k;
char *server_addr = pvPortMalloc(strlen(url) - 7);
char *web_addr = NULL;
ip_addr_t server_ip;
struct netconn *conn;
struct netbuf *inBuf;
struct pbuf *q;
char *request = NULL;
err_t err;
uint16_t recvPos = 0;
uint8_t *recvBuf;
while(gnetif.ip_addr.addr == 0x00)
{
return HTTP_ROUTE_NOT_FOUND;
}
if(strncmp((const char *)url, "http://", 7) == 0) /* 只处理http协议 */
{
/* 1)提取服务器部分 */
for(i = 4; url[i]; ++i)
{
if (url[i] == ':' && url[i + 1] == '/' && url[i + 2] == '/')
{
for (i = i + 3, j = 0; url[i] > 32 && url[i] < 127 && url[i] != '/';
++i, ++j)
{
server_addr[j] = url[i];
if (url[i] == '@') /* 服务器基础认证符号,我们做不了,遇到就错误. */
{
return HTTP_AUTH_NOT_SUPPORT;
}
}
server_addr[j] = ' ';
web_addr = pvPortMalloc(strlen(url) - 7 - strlen(server_addr));
for (k = 7 + j; k < (strlen(url)); k++) /* 后半部分提取 */
{
web_addr[k - 7 - j] = url[k];
}
web_addr[k - 7 - j] = 0x20; /* 末尾加截断 */
while (--j)
{
if (server_addr[j] == ':')
{
server_addr[j] = ' ';
}
}
}
}
/* 2)查询IP */
netconn_gethostbyname(server_addr, &server_ip);
/* 3)构造访问头 */
request = pvPortMalloc(strlen(url) + 128); /* 头所需内存大小. */
if(request == NULL) return HTTP_OUT_OF_RAM;
sprintf(request, "GET %s HTTP/1.0
Host: %s
User-Agent: Mozilla/5.0 (lwip;STM32) TaterLi
12", web_addr, server_addr);
vPortFree(web_addr);
vPortFree(server_addr);
/* 4)开始访问 */
conn = netconn_new(NETCONN_TCP);
err = netconn_connect(conn, &server_ip, 80); /* 目前也只能支持80端口,比较常用,不考虑特殊情况. */
if (err == ERR_OK)
{
netconn_write(conn, request, strlen((char *)request), NETCONN_COPY);
vPortFree(request);
inBuf = netbuf_new();
conn->recv_timeout = 3000;
recvPos = 0;
if(netconn_recv(conn, &inBuf) == ERR_OK) /* HTTP 1.0 天然不拆包 */
{
recvBuf = pvPortMalloc(inBuf->p->tot_len);
if(recvBuf == NULL)
{
return HTTP_OUT_OF_RAM;
}
for(q = inBuf->p; q != NULL; q = q->next) //遍历完整个pbuf链表
{
memcpy(recvBuf + recvPos, q->payload, q->len);
recvPos += q->len;
}
}
else
{
return HTTP_OUT_OF_RAM;
}
if(inBuf != NULL) netbuf_delete(inBuf);
netconn_close(conn);
netconn_delete(conn);
/* 5)分析数据(分析HTTP头,暂时不打算支持301之类的) */
for(i = 0; recvBuf[i]; ++i)
{
if (recvBuf[i] == '2' && recvBuf[i + 1] == '0' && recvBuf[i + 2] == '0')
{
/* 证明200 OK */
for(; recvBuf[i]; ++i)
{
/* 响应头的结束也是两个回车 */
if(recvBuf[i] == '
' && recvBuf[i + 1] == '
' && recvBuf[i + 2] == '
' && recvBuf[i + 3] == '
')
{
/* 6)复制正文内容 */
i = i + 5;
k = strlen((const char *)recvBuf) - i;
if(k == 0) return HTTP_NO_CONTENT;
pageBuf = pvPortMalloc(k);
if(pageBuf == NULL)
{
vPortFree(recvBuf);
return HTTP_OUT_OF_RAM;
}
memcpy((char *)pageBuf, (const char *)recvBuf + i, k); /* 用HTTP1.0是没http chunked response的.方便处理,否则还要分段接收网页,大的网页反正MCU也接不下. */
vPortFree(recvBuf);
return HTTP_OK;
}
}
}
}
return HTTP_NOT_200OK;
}
else
{
return HTTP_SERVER_REFUSE;
}
}
else
{
return HTTPS_NOT_SUPPORT;
}
}
错误码定义:
#define HTTP_OK 0 #define HTTPS_NOT_SUPPORT -1 #define HTTP_AUTH_NOT_SUPPORT -2 #define HTTP_SERVER_REFUSE -3 #define HTTP_ROUTE_NOT_FOUND -4 #define HTTP_OUT_OF_RAM -5 #define HTTP_NOT_200OK -6 #define HTTP_NO_CONTENT -7
访问主要函数:
void tcpget_thread(void *arg)
{
while(gnetif.ip_addr.addr == 0x00)
{
vTaskDelay(1000);
}
while(1)
{
get_webpage("http://ticks.applinzi.com/test.php?price=123&hello=world");
if(pageBuf != NULL)
{
vPortFree(pageBuf);
vTaskDelay(100);
}
vTaskDelay(1000);
}
}
成功获取网页上的json内容.
HTTP其实是TCP的高层协议~ 属于应用层需要做的协议来的.