#include "stdint.h" #include "stdio.h" #include "string.h" #include "lwip/opt.h" #include "lwip/arch.h" #include "lwip/api.h" #include "lwip/inet.h" #include "lwip/sockets.h" #include "lwip/dns.h" #include "httpclient.h" uint8_t httpSendBuffer[1024]; uint8_t httpRecvBuffer[512]; /** * @brief 从socket缓存中读取一行http数据 * @param sock: 已经连接到服务器的sock编号 * @param buf: 保存数据的buffer * @param size: buf的最大可用长度 * @retval 读取到的数据的长度,包括两个字节的换行符 */ int http_getLine(int sock, uint8_t *buf, int size) { int i = 0; char c = '\0'; int n; while((i < (size - 1)) && (c != '\n')) { n = recv(sock, &c, 1, 0); if(n <= 0) c = '\n'; buf[i++] = c; } buf[i] = '\0'; return i; //返回读取的到的数据长度 } /** * @brief 解析http响应行 * @param pbuf: 响应行的数据 * @retval 其他值: 返回http请求状态 -1: 解析失败 */ int http_parseRequestLine(uint8_t *pbuf) { int b, s, g; if((strncmp((char *)pbuf, "HTTP/1.1 ", strlen("HTTP/1.1 ")) == 0) || (strncmp((char *)pbuf, "http/1.1 ", strlen("http/1.1 ")) == 0)) { pbuf += strlen("HTTP/1.1 "); b = pbuf[0] - '0'; s = pbuf[1] - '0'; g = pbuf[2] - '0'; return (b * 100 + s * 10 + g); } return -1; } /** * @brief DNS解析回调函数 * @note 在解析域名成功后,会调用这个函数,然后可以读取到对应的IP地址 * @param name: 域名 * @param host_ip: 域名对应的ip地址 * @param callback_arg: 传递的参数 * @retval None */ void http_dns_found(const char *name, ip_addr_t *host_ip, void *callback_arg) { *(ip_addr_t *)callback_arg = *host_ip; HTTP_PRINTF("%s:%s\r\n",name, ipaddr_ntoa(host_ip)); } /** * @brief 连接到http服务器的函数 * @note 连接到http服务器 * @param host: 服务器的域名或者ip地址 * @param port: 服务器端口号 * @param hostIsIp: host代表的是 域名,还是ip地址 0: host为域名 1: host为ip地址 * @retval -1:连接服务器失败 -2: 域名解析失败 >=0: 连接成功,返回值为 sock编号 */ int http_clientConnectToServer(char *host, int port, int hostIsIp) { int timeout; struct sockaddr_in serverAddr; int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock < 0) return -2; //如果传入的host参数是域名,解析域名 if(hostIsIp == 0) { ip_addr_t addr; addr.addr = 0; dns_gethostbyname(host, &addr, http_dns_found, &addr); //等待dns解析完成 timeout = 0; while((addr.addr == 0) && (timeout < 2000)) { vTaskDelay(100); timeout += 10; } if(timeout >= 2000) { HTTP_PRINTF(("dns get failure \n")); return -2; } serverAddr.sin_addr.s_addr = inet_addr(inet_ntoa(addr)); } else serverAddr.sin_addr.s_addr = inet_addr((char*)host); serverAddr.sin_len = sizeof(serverAddr); serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(port); memset(&serverAddr.sin_zero, 0, sizeof(serverAddr.sin_zero)); //连接服务器 if(connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) { HTTP_PRINTF("connect server error \r\n"); return -1; } //设置接收数据超时时间 timeout = 3000; setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(int)); HTTP_PRINTF("connect server success \r\n"); return sock; } /** * @brief 关闭socket端口 * @param sock: 已经连接到服务器的sock编号 */ void http_clientClose(int sock) { close(sock); } /** * @brief 连接到发送GET请求 * @note 组装GET数据包,并将GET请求发送出去 * @param sock: 已经连接到服务器的sock编号 * @param host: 服务器的域名或者ip地址 * @param url: 请求资源的地址 * @retval -1: 发送请求失败 1:发送成功 */ int http_clientPacketRequest_GET(int sock, char *host, char *url) { int len; memset(httpSendBuffer, 0, sizeof(httpSendBuffer)); //组建请求行 sprintf((char *)httpSendBuffer, "GET "); if(url == NULL) strcat((char *)httpSendBuffer, "/"); else strcat((char *)httpSendBuffer, url); strcat((char *)httpSendBuffer, " HTTP/1.1\r\n"); //组建请求头部 strcat((char *)httpSendBuffer, "Host: "); strcat((char *)httpSendBuffer, host); strcat((char *)httpSendBuffer, "\r\n"); strcat((char *)httpSendBuffer, "Connection: close\r\n"); strcat((char *)httpSendBuffer, "Accept: application/json\r\n"); strcat((char *)httpSendBuffer, "User-Agent: stm32f207\r\n"); strcat((char *)httpSendBuffer, "Cache-Control: no-cache\r\n"); //添加一个空白行 strcat((char *)httpSendBuffer, "\r\n"); len = strlen((char *)httpSendBuffer); HTTP_PRINTF("%s", (char *)httpSendBuffer); // //发送请求报文 len = write(sock, httpSendBuffer, len); if(len <= 0) { return -1; } return 1; } /** * @brief http_clientReadResponse_GET * @note 等待服务器返回GET响应 * @param sock: 已经连接到服务器的sock编号 * @retval -1: 发送请求失败 1:发送成功 */ int http_clientReadResponse_GET(int sock, uint8_t *pbuf, int *datlen) { int len, ret; int length = 0; //读取响应行 len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer)); if(len <= 0) return -1; HTTP_PRINTF("%s", (char *)httpRecvBuffer); ret = http_parseRequestLine(httpRecvBuffer); //读取响应头 do { len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer)); HTTP_PRINTF("%s", (char *)httpRecvBuffer); if(len <= 2) { if(len == 2) break; //读取到了空行 else return -1; } }while(len > 0); //读取响应主体内容 length = 0; do { len = recv(sock, httpRecvBuffer, sizeof(httpRecvBuffer), 0); if(len > 0) { memcpy(pbuf + length, httpRecvBuffer, len); length += len; } }while(len > 0); *datlen = length; return ret; } /* * @brief http_clientPacketRequest_POST * @note 组建和发送POST请求头数据包 * @param sock: 已经连接到服务器的sock编号 * @param host: 服务器域名或者IP地址 * @param url: 请求的资源位置 * @param pbuf: 需要post的数据缓存 * @param datalen 需要post的数据长度 * @retval -1: 发送失败 1:发送成功 */ char httpTmpBuffer[64]; int http_clientPacketRequest_POST(int sock, char *host, char *url, int datalen) { int len; memset(httpSendBuffer, 0, sizeof(httpSendBuffer)); //组建请求行 sprintf((char *)httpSendBuffer, "POST "); if(url == NULL) strcat((char *)httpSendBuffer, "/"); else strcat((char *)httpSendBuffer, url); strcat((char *)httpSendBuffer, " HTTP/1.1\r\n"); //组建请求头部 memset(httpTmpBuffer, 0, sizeof(httpTmpBuffer)); sprintf(httpTmpBuffer, "Host: %s\r\n", host); strcat((char *)httpSendBuffer, httpTmpBuffer); strcat((char *)httpSendBuffer, "Connection: close\r\n"); strcat((char *)httpSendBuffer, "Accept: application/json\r\n"); // strcat((char *)httpSendBuffer, "User-Agent: stm32f207\r\n"); strcat((char *)httpSendBuffer, "Cache-Control: no-cache\r\n"); strcat((char *)httpSendBuffer, "Content-Type: application/json\r\n"); memset(httpTmpBuffer, 0, sizeof(httpTmpBuffer)); sprintf(httpTmpBuffer, "Content-Length: %d\r\n", datalen); strcat((char *)httpSendBuffer, httpTmpBuffer); //添加一个空白行 strcat((char *)httpSendBuffer, "\r\n"); len = strlen((char *)httpSendBuffer); HTTP_PRINTF("%s", (char *)httpSendBuffer); //发送请求报文 len = write(sock, httpSendBuffer, len); if(len <= 0) { return -1; } return 1; } /* * @brief http_clientPacketBody_POST * @note 发送post数据 * @param sock: 已经连接到服务器的sock编号 * @param pbuf: 需要post的数据缓存 * @param datalen 需要post的数据长度 * @retval -1: 发送失败 1:发送成功 */ int http_clientPacketBody_POST(int sock, uint8_t *pbuf, int datalen) { int len; while(datalen > 1000) { len = write(sock, pbuf, 1000); if(len <= 0) { return -1; } datalen -= 1000; pbuf += 1000; } if(datalen > 0) { len = write(sock, pbuf, datalen); if(len <= 0) { return -1; } } return 1; } /* * @brief http_clientReadResponse_POST * @note 读取和解析POST返回的响应 * @param sock: 已经连接到服务器的sock编号 * @retval 响应的返回值 */ int http_clientReadResponse_POST(int sock, uint8_t *pbuf, int *datlen) { int len, ret; int length = 0; //读取响应行 len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer)); if(len <= 0) return -1; HTTP_PRINTF("%s", (char *)httpRecvBuffer); ret = http_parseRequestLine(httpRecvBuffer); //读取响应头 do { len = http_getLine(sock, httpRecvBuffer, sizeof(httpRecvBuffer)); HTTP_PRINTF("%s", (char *)httpRecvBuffer); if(len <= 2) { if(len == 2) break; //读取到了空行 else return -1; } }while(len > 0); //读取响应主体内容 length = 0; do { len = recv(sock, httpRecvBuffer, sizeof(httpRecvBuffer), 0); if(len > 0) { memcpy(pbuf + length, httpRecvBuffer, len); length += len; } }while(len > 0); *datlen = length; return ret; } /* * @brief http_clientGet * @note 客户端发送GET请求 * @param host: 服务器的域名或者IP地址 * @param url: 访问服务器资源的位置 * @param port: 服务器的端口号 * @param hostIsIp: 第一个参数是域名还是ip地址 0:域名 1: ip地址 * @param pbuf: 接收服务器响应的数据 * @param datalen: 服务器响应数据的长度 * @retval 负值: GET请求异常, 正值: http协议的返回码 */ int http_clientGet(char *host, char *url, uint16_t port, uint8_t hostIsIp, uint8_t *pbuf, int *datalen) { int sock = -1, ret; sock = http_clientConnectToServer(host, port, hostIsIp); if(sock < 0) goto __httpError; ret = http_clientPacketRequest_GET(sock, host, url); if(sock < 0) goto __httpError; ret = http_clientReadResponse_GET(sock, pbuf, datalen); if(sock < 0) goto __httpError; __httpError: if(sock >= 0) http_clientClose(sock); return ret; } /* * @brief http_clientGet * @note 客户端发送GET请求 * @param host: 服务器的域名或者IP地址 * @param url: 访问服务器资源的位置 * @param port: 服务器的端口号 * @param hostIsIp: 第一个参数是域名还是ip地址 0:域名 1: ip地址 * @param postbuf: 需要发往服务器的数据 * @param postlen: 发送数据的长度 * @param rtnbuf: 接收服务器响应的数据 * @param rtnlen: 服务器响应数据的长度 * @retval 负值: GET请求异常, 正值: http协议的返回码 */ int http_clientPost(char *host, char *url, uint16_t port, uint8_t hostIsIp, uint8_t *postbuf, int postlen, uint8_t *rtnbuf, int *rtnlen) { int sock, ret; sock = http_clientConnectToServer(host, port, hostIsIp); if(sock < 0) goto __httpError; ret = http_clientPacketRequest_POST(sock, host, url, postlen); if(ret < 0) goto __httpError; ret = http_clientPacketBody_POST(sock, postbuf, postlen); if(ret < 0) goto __httpError; ret = http_clientReadResponse_POST(sock, rtnbuf, rtnlen); __httpError: if(sock >= 0) http_clientClose(sock); return ret; }