前言
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;
}
觉得有用的话,可以给我点奖励哦!(微信)