前言
- 需求,调用一次接口,向服务器发送一条post请求,并接收服务器响应。用的是目前所在公司的wifi单片机开发板。
- 调用一次
get_cloud_permission
,就会向服务器发送一条post请求,并调用recv接收服务器响应。 - 在HTTP POST请求中,查询参数为
uid=AAAAAAAAAAAAAAAAAAAA
,查询参数作为请求体的一部分。
思路
- 本次请求的url为 :
http://example.xxx.com/api/user/usEquipment/public/queryOpenStatus
-
构建TCP client,用于连接服务器。
- 从域名中解析出服务器ip地址,http的端口通常为 80
- 调用
socket
创建client socket,且设置服务器 ip 以及 端口 - 调用
connect
连接服务器,连接不上时,程序会阻塞,检查ip以及端口是否正确。(先确保 tcp client 能连接上服务器,在调试后面的内容)
-
确保已连接上服务器后,发送post请求
- 构建http post 请求头 (下文代码中的request)
- 调用
send
发送 post 请求
-
调用
recv
接收服务器的响应。 -
关闭socket。
-
总体上:
-
确保tcp已经连接。可以使用sscom5网络调试助手模拟一下,看看是否可以连接上网络调试助手。
-
post 请求头,这里是我调试的比较久的地方,通常接收到服务器响应的消息不对,就是post 请求头没有构建好。
-
Content-Length:
查询参数的长度,我这里是uid=AAAAAAAAAAAAAAAAAAAA
,长度为24 -
Content-Type:
用于指示请求体的媒体类型。这告诉服务器如何解释请求体的内容。-
几个常见
Content-Type:
的值 :application/x-www-form-urlencoded
:这是默认的编码类型,用于提交表单数据,特别是在HTML表单中。数据以“键=值”对的形式被编码为文本,并且这些对之间用“&”字符分隔。multipart/form-data
: 主要用于在HTTP请求中发送二进制数据,例如文件上传。它将请求体分割成多个部分,每个部分都可能有不同的Content-Type
application/json
:当你想发送JSON格式的数据时,应使用此类型。JSON数据通常以JavaScript对象的形式被编码,用于数据的交换。text/plain
:用于发送纯文本数据。
-
-
Host
: 请求的域名example.xxx.com
-
User-Agent:
用于标识发送HTTP请求的客户端应用或设备 -
Connection :
用于控制网络连接的持久性, keep-alive : 持久连接, close: 如果客户端或服务器想要在请求/响应后立即关闭连接,而不是使用持久连接 -
PSOT 请求头 格式 :查询参数和上一字段必须空一行。
char* uid=AAAAAAAAAAAAAAAAAAAA; snprintf(request, sizeof(request), "POST /api/user/usEquipment/public/queryOpenStatus HTTP/1.1\r\n" "Host: example.xxx.com\r\n" "User-Agent: Custom-HTTP-Client/1.0\r\n" "Content-Type: application/x-www-form-urlencoded\r\n" "Content-Length: %d\r\n" // 注意:这里需要计算请求体的长度 "Connection: close\r\n\r\n" //两个 \r\n\r\n "uid=%s", strlen(uid)+4, uid); // 请求体内容 +4是uid= 的长度
-
-
代码
#define CLOUD_PERMISSION_HOST "example.xxx.com"
typedef struct _push_tcpclient{
int socket;
int remote_port;
char remote_ip[16];
struct sockaddr_in _addr;
int connected;
} push_tcpclient;
void get_ipaddr(char* addr,char* ip_domain);
void get_ipaddr(char* addr,char* ip_domain) //从域名获取ip
{
int isdomain = 0;
int ipaddr = inet_addr(ip_domain);
if (ipaddr == INADDR_NONE) {
struct hostent *host = lwip_gethostbyname(ip_domain, 1);
if (host == NULL) {
os_printf("can not resolve domain name:%s\n", ip_domain);
strerror(errno);
return;
}
ipaddr = *(int *)host->h_addr;
isdomain = 1;
x16_to_ipaddr(addr,ipaddr);
os_printf("\nipaddr =====> %x %s\r\n",ipaddr,addr);
g_get_ip_ok = 1;
}
}
int cloud_tcp_client_create(push_tcpclient *p_client,const char *host,int port)
{
os_printf("===> %s,%d\n",__FUNCTION__,__LINE__);
struct hostent *he;
char ip_buf[24];
int nNetTimeout=10 * 1000; // 10s
if(p_client == NULL || host == NULL) {
return -1;
}
memset(p_client,0,sizeof(push_tcpclient));
get_ipaddr(ip_buf,CLOUD_PERMISSION_HOST);
os_printf("host:%s domain ip:%s\n",host,ip_buf);
p_client->remote_port = port;
strcpy(p_client->remote_ip,ip_buf);
p_client->_addr.sin_family = AF_INET;
p_client->_addr.sin_port = htons( p_client->remote_port);
p_client->_addr.sin_addr.s_addr = inet_addr(p_client->remote_ip);
os_printf("connect======> host: %s ,ip :%s ,port :%d\n",host,p_client->remote_ip,p_client->remote_port);
p_client->socket = socket(AF_INET,SOCK_STREAM,0);
if( p_client->socket == -1) {
os_printf("socket error\n");
return -3;
}
//超时设置
if(setsockopt(p_client->socket,SOL_SOCKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int))<0)
return -4;
if(setsockopt(p_client->socket,SOL_SOCKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int))<0)
return -5;
return 1;
}
int cloud_tcpclient_connect(push_tcpclient *pclient)
{
if(pclient->connected == 1)
{
return 2;
}
if(connect(pclient->socket,(struct sockaddr *)&pclient->_addr,sizeof(struct sockaddr)) < 0) {
os_printf("+++++++++++++++\nconnect error == > ip :%s port :%d \r\n+++++++++++++++\n",pclient->remote_ip,pclient->remote_port);
return -999;
}
os_printf("connect success\n");
pclient->connected == 1;
return 0;
}
int cloud_tcpclient_send(push_tcpclient *p_client,char* post_str,int size)
{
int sent = 0;
os_printf("==> post_str: %s\n",post_str);
sent = send(p_client->socket,post_str,size,0);
if(sent < 0) {
os_printf("tcp client send err!!\r\n");
return -998;
}
return 1;
}
int cloud_tcpclient_recv(push_tcpclient *pclient,char*jbuf)
{
int ret = -1;
ret = recv(pclient->socket, jbuf,400,0);
if (ret < 0) {
os_printf("===> recv err !!\r\n");
return ret;
} else {
os_printf("===> recv data : %s ,len = %d\n",jbuf,ret);
}
return ret;
}
int push_tcpclient_close(push_tcpclient *pclient)
{
close(pclient->socket);
pclient->connected = 0;
return 0;
}
extern char* p_uid;
int get_cloud_permission(void)
{
os_printf("===> %s,%d\n",__FUNCTION__,__LINE__);
int ret = -1;
push_tcpclient client = {0};
char request[400];
char* p_id = "ZB5SH2FP8LN3H7JM111A";
char *uid = NULL; ;
uid = p_id;//测试用
// uid = p_uid; //最终版本应该打开这里。
//构建post请求
snprintf(request, sizeof(request),
"POST /api/user/usEquipment/public/queryOpenStatus HTTP/1.1\r\n"
"Host: example.xxx.com\r\n"
"User-Agent: Custom-HTTP-Client/1.0\r\n"
"Content-Type: application/x-www-form-urlencoded\r\n"
"Content-Length: %d\r\n" // 注意:这里需要计算请求体的长度
"Connection: close\r\n\r\n"
"uid=%s", strlen(uid)+4, uid); // 请求体内容
//创建socket
ret = cloud_tcp_client_create(&client,CLOUD_PERMISSION_HOST,80);
if(ret < 0) {
os_printf("cloud_tcp_client_create error,ret===>%d\n",ret);
return ret;
}
os_printf("tcp connect...\n");
//连接
if(client.connected == 0) {
if(cloud_tcpclient_connect(&client) < 0) {
ret = -2;
return ret;
}
}
os_printf("tcp connect success!!\n");
//发送post请求
int len = strlen(request);
os_printf("len == > %d\n",len);
ret = cloud_tcpclient_send(&client,request,len);
if (ret < 0) {
return ret;
}
//接收服务器响应
memset(request,0,sizeof(request));
os_printf("====> begin recv\n");
ret = cloud_tcpclient_recv(&client,request);
os_printf("====> recv end\n");
char response = request[ret-1];
os_printf("====> response : %c\n",response);
push_tcpclient_close(&client);
return 99;//可判断返回值是不是99,是99说明程序正常
}
可以看下我测试时的log
最后可以看到服务器的返回数据,服务器收到正确的uid的时候,就会返回1
-----------END-----------