C语言tcp模拟http

背景

在单片机等平台常常没有资源使用curl等库去实现http请求,通常做法是使用tcp发请求体模拟发起http请求。本文给出一个简单的请求代码,目前只实现了post请求,其他请求就是请求体有细微差别,可自行拓展。

实现

http_tcp.h

#ifndef __HTTP_TCP_H_
#define __HTTP_TCP_H_

#include <stdio.h>
#include <string.h>
#include <time.h>

#include "lwip/sockets.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>

#include "log.h"

typedef enum{
  HTTP_GET = 0,
  HTTP_POST,
  HTTP_HEAD,
  HTTP_PUT,
  HTTP_DELETE,
  HTTP_OPTIONS,
  HTTP_TRACE,
  HTTP_CONNECT,
}HTTP_TYPE;

int domainNameResolution( char *domainName, char *ip );

int tcpEmulatesHttpRequests( char *ip, int port, HTTP_TYPE type,char *uri, char *request_body, char *result );

#endif

http_tcp.c

#include "http_tcp.h"

#define HTTP_POST_HEAD "POST %s HTTP/1.1\r\n\
Accept-Language: zh-CN,zh;q=0.9\r\n\
Host:%s\r\n\
Connection: close\r\n\
Content-Length:%ld\r\n\
Content-Type:application/x-www-form-urlencoded;charset=UTF-8\r\n\
\r\n\
%s\r\n\
"

/**
 * @brief 域名解析,获取域名对应的ip
 * @param[in] domainName 域名
 * @param[out] ip 获取的ip地址
 * @retval 0 success
 * @retval <0 fail
 * */
int domainNameResolution( char *domainName, char *ip )
{
  if( ( ip == NULL ) || ( domainName == NULL ) )
  {
    logError("ip and domain name can't be NULL!!!");
    return -1;
  }
  const struct addrinfo hints = {
    .ai_family = AF_INET,
    .ai_socktype = SOCK_STREAM,
  };
  struct addrinfo *res;
  struct in_addr *addr;
  int err = getaddrinfo( domainName, NULL, &hints, &res);
  if(err != 0 || res == NULL) {
    logError( "DNS lookup failed err=%d res=%p", err, res);
    return -1;
  }
  addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
  sprintf( ip, "%s", inet_ntoa(*addr) );

  return 0;
}

/**
 * @brief 使用TCP模拟请求HTTP
 * @param[in] ip 请求的IP地址
 * @param[in] port 请求的IP端口
 * @param[in] type 请求类型,根据不同的请求类型构造请求头
 * @param[in] uri  资源标识符
 * @param[in] request_body 请求参数
 * @param[out] result 请求返回结果
 * @retval 0 success
 * @retval <0 fail
 * */
int tcpEmulatesHttpRequests( char *ip, int port, HTTP_TYPE type,char *uri, char *request_body, char *result )
{
    logInfo("will connect gateway ssid : %s port:%d",
             ip, port);

    //新建socket
    int connect_socket = socket(AF_INET, SOCK_STREAM, 0);
    if (connect_socket < 0)
    {
        logError("socket errno %d,%s", errno, strerror(errno) );
        close(connect_socket);

        return -1;
    }

    //配置连接服务器信息:端口+ip
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(port);
    server_addr.sin_addr.s_addr = inet_addr(ip);
    logInfo("connectting server...");

    //连接服务器
    if (connect(connect_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        //打印报错信息
        logError("connect failed!");
        close(connect_socket);

        if( ( errno == 113 ) /*&& ( getPowerStatus() != 0 )*/ )
        {
            /* esp_sleep_enable_timer_wakeup( 1 ); // 重启,重新联网 */
            /* esp_deep_sleep_start(); */
        }

        return -1;
    }

    logInfo("connect success!");

    struct timeval timeout;
    fd_set readSet;
    int err = -1;
    do{
        timeout.tv_sec = 20;
        timeout.tv_usec = 0;
        if (setsockopt( connect_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) == -1)
        {
            logError("setsockopt( SO_SNDTIMEO ) failed, %s", strerror(errno));
            break;
        }

        char send_buf[1024] = {0};
        memset( send_buf, 0, sizeof(send_buf) );
        switch( type ){
        case HTTP_POST:
          snprintf( send_buf, sizeof( send_buf ), HTTP_POST_HEAD, uri, ip, strlen(request_body), request_body );
          break;
        default:
          break;
        }
        logInfo("client send:\n%s\n",send_buf);
        int ret = send(connect_socket,send_buf, strlen(send_buf)+1, 0 );
        if( ret <= 0 )
        {
            logError("%s: ret = %d, errno %d, %s", __func__, ret, errno, strerror(errno) );
            if( ( errno == 113 ) /*&& ( getPowerStatus() != 0 )*/ )
            {
                /* esp_sleep_enable_timer_wakeup( 1 ); // 重启,重新联网 */
                /* esp_deep_sleep_start(); */
            }
            break;
        }

        FD_ZERO( &readSet );
        FD_SET( connect_socket, &readSet );
        timeout.tv_sec = 20;
        timeout.tv_usec = 0;
        ret = select( connect_socket+1, &readSet, 0, 0, &timeout );
        if (ret <= 0) {
          logE("socket select failure!!! ret = %d, %d, %s", ret, errno, strerror(errno));
          break;
        }

        if (!FD_ISSET(connect_socket, &readSet)) {
          logE("connect_socket = %d is not read", connect_socket);
          break;
        }

        /* ret = read(connect_socket,result, 1024); */
        ret = recv(connect_socket,result, 1024, 0);
        if( ret <= 0 )
        {
            logError("%s: ret = %d, errno %d, %s", __func__, ret, errno, strerror(errno) );
            if( ( errno == 113 ) /*&& ( getPowerStatus() != 0 )*/ )
            {
                /* esp_sleep_enable_timer_wakeup( 1 ); // 重启,重新联网 */
                /* esp_deep_sleep_start(); */
            }
            break;
        }
        err = 0;
    }while(0);

    close(connect_socket);

    return err;
}

其中log.h见https://editor.csdn.net/md/?articleId=119873836一文

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值