LwIP 代码分析(Web访问) – 第十一集

/ 0评 / 0

现在随便的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的高层协议~ 属于应用层需要做的协议来的.

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注