嵌入式 C语言 TCP Client 发送 post 请求,并接收服务器响应

前言

  • 需求,调用一次接口,向服务器发送一条post请求,并接收服务器响应。用的是目前所在公司的wifi单片机开发板。
  • 调用一次get_cloud_permission,就会向服务器发送一条post请求,并调用recv接收服务器响应。
  • 在HTTP POST请求中,查询参数为 uid=AAAAAAAAAAAAAAAAAAAA,查询参数作为请求体的一部分。

思路

  • 本次请求的url为 : http://example.xxx.com/api/user/usEquipment/public/queryOpenStatus
  1. 构建TCP client,用于连接服务器。

    1. 从域名中解析出服务器ip地址,http的端口通常为 80
    2. 调用socket创建client socket,且设置服务器 ip 以及 端口
    3. 调用connect连接服务器,连接不上时,程序会阻塞,检查ip以及端口是否正确。(先确保 tcp client 能连接上服务器,在调试后面的内容)
  2. 确保已连接上服务器后,发送post请求

    1. 构建http post 请求头 (下文代码中的request)
    2. 调用send发送 post 请求
  3. 调用recv接收服务器的响应。

  4. 关闭socket。

  • 总体上:

    1. 确保tcp已经连接。可以使用sscom5网络调试助手模拟一下,看看是否可以连接上网络调试助手。

    2. post 请求头,这里是我调试的比较久的地方,通常接收到服务器响应的消息不对,就是post 请求头没有构建好。

      • Content-Length: 查询参数的长度,我这里是uid=AAAAAAAAAAAAAAAAAAAA,长度为24

      • Content-Type: 用于指示请求体的媒体类型。这告诉服务器如何解释请求体的内容。

        • 几个常见Content-Type:的值 :

          1. application/x-www-form-urlencoded :这是默认的编码类型,用于提交表单数据,特别是在HTML表单中。数据以“键=值”对的形式被编码为文本,并且这些对之间用“&”字符分隔。
          2. multipart/form-data : 主要用于在HTTP请求中发送二进制数据,例如文件上传。它将请求体分割成多个部分,每个部分都可能有不同的Content-Type
          3. application/json:当你想发送JSON格式的数据时,应使用此类型。JSON数据通常以JavaScript对象的形式被编码,用于数据的交换。
          4. 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-----------

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值