Linux平台C语言通过http协议POST form-data数据

前言

form-data数据格式是http协议POST传输方式中的一种,常用于传输较大的文件。

目录

  • 键值对结构体
  • POST form-data数据

C代码

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <malloc.h>
#include <math.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include "cJSON/cJSON.h"

// 键值对结构体
typedef struct {
    char *key;
    char *value;
} KeyValue;

/**
 * post 请求:以表单方式提交数据
 * <p>
 * 由于 multipart/form-data 不是 http 标准内容,而是属于扩展类型,
 * 因此需要自己构造数据结构,具体如下:
 * <p>
 * 1、首先,设置 Content-Type
 * <p>
 * Content-Type: multipart/form-data; boundary=${bound}
 * <p>
 * 其中${bound} 是一个占位符,代表我们规定的分割符,可以自己任意规定,
 * 但为了避免和正常文本重复了,尽量要使用复杂一点的内容
 * <p>
 * 2、设置主体内容
 * <p>
 * --${bound}
 * Content-Disposition: form-data; name="userName"
 * <p>
 * --${bound}
 * Content-Disposition: form-data; name="file"; filename="测试.excel"
 * Content-Type: application/octet-stream
 * <p>
 * 文件内容
 * --${bound}--
 * <p>
 * 其中${bound}是之前头信息中的分隔符,如果头信息中规定是123,那这里也要是123;
 * 可以很容易看到,这个请求提是多个相同部分组成的:
 * 每一部分都是以--加分隔符开始的,然后是该部分内容的描述信息,然后一个回车换行,然后是描述信息的具体内容;
 * 如果传送的内容是一个文件的话,那么还会包含文件名信息以及文件内容类型。
 * 上面第二部分是一个文件体的结构,最后以--分隔符--结尾,表示请求体结束
 *
 */
HttpResponse *postFormData(const char *ip, int port, const char *subUrl, KeyValue **filePathMap, int filePathMapSize,
        KeyValue **keyValueMap, int keyValueMapSize) {
    //LOGD("postFormData(): IP= %s , PORT= %d , SUB_URL= %s", ip, port, subUrl);
    const long long _NOW = 0;

    HttpResponse *_httpResponse = (HttpResponse *) malloc(sizeof(HttpResponse));
    memset(_httpResponse, 0, sizeof(HttpResponse));
    _httpResponse->code = strdup("500");

    int _sockfd;
    int _len;
    struct sockaddr_in _address;
    int _ret;

    char *_sendStr = (char *) malloc(1024 * 1024);
    memset(_sendStr, 0, 1024 * 1024);

    unsigned char *_bodyData = (unsigned char *) malloc(1024 * 1024);
    memset(_bodyData, 0, 1024 * 1024);
    int _bodyDataSize = 0;

    char _str1[1024] = {0};
    char _boundary[1024] = {0};

    char *_recvStr = (char *) malloc(1024 * 1024);
    memset(_recvStr, 0, 1024 * 1024);

    _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    _address.sin_family = AF_INET;
    _address.sin_addr.s_addr = inet_addr(ip);
    _address.sin_port = htons(port);

    // 超时时间设置
    struct timeval _tm;
    _tm.tv_sec = 0;
    _tm.tv_usec = 500000;
    // 设置发送超时
    setsockopt(_sockfd, SOL_SOCKET, SO_SNDTIMEO, &_tm, sizeof(struct timeval));
    // 设置接收超时
    setsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO, &_tm, sizeof(struct timeval));

    _len = sizeof(_address);
    _ret = connect(_sockfd, (struct sockaddr *) &_address, _len);
    if (_ret < 0) {
        //LOGE("postFormData(): connect ERROR, ERROR= %s, IP= %s , PORT= %d", strerror(errno), ip, port);
        free(_sendStr);
        _sendStr = NULL;

        free(_bodyData);
        _bodyData = NULL;

        free(_recvStr);
        _recvStr = NULL;

        return _httpResponse;
    }

    // 分隔符,可以任意设置,这里设置为 MyBoundary+ 时间戳(尽量复杂点,避免和正文重复)
    sprintf(_boundary, "MyBoundary%lld", _NOW);

    // 构造body
    // 发送普通参数
    for (int i = 0; i < keyValueMapSize; i++) {
        // 写分隔符--${boundary},并回车换行
        sprintf(_str1, "--%s\r\n", _boundary);
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
        // 写描述信息:Content-Disposition: form-data; name="参数名",并两个回车换行
        sprintf(_str1, "Content-Disposition: form-data; name=\"%s\"\r\n\r\n", keyValueMap[i]->key);
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
        // 写具体内容:参数值,并回车换行
        sprintf(_str1, "%s\r\n", keyValueMap[i]->value);
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
    }

    // 发送文件类型参数
    for (int i = 0; i < filePathMapSize; i++) {
        // 写分隔符--${boundary},并回车换行
        sprintf(_str1, "--%s\r\n", _boundary);
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
        // 写 Content-Disposition: form-data; name="参数名"; filename="文件名",并回车换行
        sprintf(_str1, "Content-Disposition: form-data; name=\"%s\"; filename=\"%s\"\r\n", filePathMap[i]->key, filePathMap[i]->value);
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
        // 写 Content-Type: application/octet-stream,并两个回车换行
        strcpy(_str1, "Content-Type: application/octet-stream\r\n\r\n");
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
        // 写文件
        unsigned char *_data = (unsigned char *) malloc(1024 * 1024);
        memset(_data, 0, 1024 * 1024);

        FILE *_fp = fopen(filePathMap[i]->value, "rb");
        if (_fp != NULL) {
            int _size = fread(_data, 1, 1024 * 1024, _fp);
            if (_size > 0) {
                memcpy(_bodyData + _bodyDataSize, _data, _size);
                _bodyDataSize += _size;
            }

            fclose(_fp);
        }

        free(_data);
        _data = NULL;

        strcpy(_str1, "\r\n");
        memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
        _bodyDataSize += strlen(_str1);
    }

    // 写结尾的分隔符--${boundary}--,然后回车换行
    sprintf(_str1, "--%s--\r\n", _boundary);
    memcpy(_bodyData + _bodyDataSize, _str1, strlen(_str1));
    _bodyDataSize += strlen(_str1);

    strcpy(_sendStr, "");
    strcat(_sendStr, "POST ");
    strcat(_sendStr, subUrl);
    strcat(_sendStr, " HTTP/1.1\r\n");

    strcat(_sendStr, "Host: ");
    strcat(_sendStr, ip);
    strcat(_sendStr, ":");
    sprintf(_str1, "%d", port);
    strcat(_sendStr, _str1);
    strcat(_sendStr, "\r\n");

    strcat(_sendStr,"Connection: keep-alive\r\n");

    // 设置 Content-Type 为 multipart/form-data; boundary=${boundary}
    strcat(_sendStr, "Content-Type: multipart/form-data; boundary=");
    strcat(_sendStr, _boundary);
    strcat(_sendStr, "\r\n");

    strcat(_sendStr, "Content-Length: ");
    sprintf(_str1, "%d", _bodyDataSize);
    strcat(_sendStr, _str1);
    strcat(_sendStr, "\r\n\r\n");

    _len = write(_sockfd, _sendStr, strlen(_sendStr));
    //LOGD("postFormData(): write LENGTH= %d", _len);
    _len = write(_sockfd, _bodyData, _bodyDataSize);
    //LOGD("postFormData(): write LENGTH= %d", _len);

    int _readSize = 0;
    while (1) {
        _len = read(_sockfd, _str1, 1024);
        //LOGD("postFormData(): read LENGTH= %d", _len);
        if (_len <= 0) {
            break;
        }

        memcpy(_recvStr + _readSize, _str1, _len);
        _readSize += _len;

        // 超时时间设置,后续包耗时较小
        _tm.tv_sec = 0;
        _tm.tv_usec = 10000;
        // 设置接收超时
        setsockopt(_sockfd, SOL_SOCKET, SO_RCVTIMEO, &_tm, sizeof(struct timeval));
    }

    free(_sendStr);
    _sendStr = NULL;

    free(_bodyData);
    _bodyData = NULL;

    close(_sockfd);

    if (strlen(_recvStr) == 0) {

    } else {
        releaseHttpResponse(_httpResponse);
        _httpResponse = parseResponse(_recvStr);
    }

    free(_recvStr);
    _recvStr = NULL;

    return _httpResponse;
}

觉得有用的话,可以给我点奖励哦!(微信)
在这里插入图片描述

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lichaofan2008

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值