HTTP是很常见的协议,虽然用得很多,但对细节的了解却是很浅,这回通过向服务端上传文件信息来理解细节。网络库的选择:1、WinHTTP是windows下常用的库;2、CURL是广受喜爱的开源库。对于我来说,libcurl最大的优点是使用方便,可以把注意力更多的集中到业务层上,提高工作效率,避免重造轮子;缺点是略大(MD编译有264KB,MT编译有340KB),不像WinHTTP可以由windows操作系统集成。下边展示如何使用这两种网络库实现表单POST文件。

一、libcurl

     库编译:把include、lib的代码拿出来,使用静态库,设置预定义宏BUILDING_LIBCURL。
     测试工程:设置预定义宏CURL_STATICLIB;添加依赖lib:Ws2_32.lib Wldap32.lib。
     主要代码
void curl_post_cswuyg()
{
curl_global_init(CURL_GLOBAL_ALL);
CURL* hCurl = curl_easy_init();
if(hCurl != NULL)
{
//也许有Expect: 100-continue,去掉它
curl_slist* pOptionList = NULL;
pOptionList = curl_slist_append(pOptionList, "Expect:");
curl_easy_setopt(hCurl, CURLOPT_HTTPHEADER, pOptionList); curl_httppost* pFormPost = NULL;
curl_httppost* pLastElem = NULL;
//上传文件,指定本地文件完整路径
curl_formadd(&pFormPost, &pLastElem, CURLFORM_COPYNAME, "ufile01", CURLFORM_FILE, "C:\\temp\\upload_test_curl_cswuyg.txt", CURLFORM_CONTENTTYPE, "application/octet-stream", CURLFORM_END); //上传自定义文件内容的文件,CURLFORM_BUFFER指定服务端文件名
//http://curl.haxx.se/libcurl/c/curl_formadd.html
char* file_info = "file_info——cswuyg";
int record_length = std::string(file_info).length();
curl_formadd(&pFormPost, &pLastElem,
CURLFORM_COPYNAME, "ufile01",
CURLFORM_BUFFER, "cswuyg_test.txt",
CURLFORM_BUFFERPTR, file_info,
CURLFORM_BUFFERLENGTH, record_length,
CURLFORM_END);
//不加一个结束的hfs服务端无法写入文件,一般不存在这种问题,这里加入只是为了测试.
//curl_formadd(&pFormPost, &pLastElem, CURLFORM_COPYNAME, "end", CURLFORM_COPYCONTENTS, "end", CURLFORM_END);
curl_easy_setopt(hCurl, CURLOPT_HTTPPOST, pFormPost);
curl_easy_setopt(hCurl, CURLOPT_URL, "http://127.0.0.1/hfs_up/"); CURLcode res = curl_easy_perform(hCurl);
if(res != CURLE_OK)
{
std::wcout << "Error" << std::endl;
}
curl_formfree(pFormPost);
curl_easy_cleanup(hCurl);
} curl_global_cleanup();
}
     wireshark抓包,127.0.0.1地址无法抓到,网络上有解决方案。示意图:
头域设置分割符:
 MIME部分:
 

二、WinHTTP

     库:winhttp.lib
     主要代码
void cswuyg_winhttp_post()
{
HINTERNET hSession = ::WinHttpOpen(L"cswuyg post test/1.0", WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, NULL);
HINTERNET hConnect = ::WinHttpConnect(hSession, L"127.0.0.1", INTERNET_DEFAULT_HTTP_PORT, );
if (hConnect == NULL)
{
int i = ERROR_WINHTTP_INCORRECT_HANDLE_TYPE;
return;
}
const wchar_t* lpszAcceptedType[] = {L"*/*", NULL};
HINTERNET hRequest = ::WinHttpOpenRequest(hConnect, L"POST", L"hfs_up", L"HTTP/1.1", WINHTTP_NO_REFERER, lpszAcceptedType, );
if (hRequest == NULL)
{
return;
}
DWORD dwTime = ;
::WinHttpSetOption(hRequest, WINHTTP_OPTION_CONNECT_TIMEOUT, &dwTime, sizeof(DWORD));
std::wstring strHeader = L"Content-Type: multipart/form-data; boundary=--boundary_cswuygtest\r\n";
::WinHttpAddRequestHeaders(hRequest, strHeader.c_str(), strHeader.length(), WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE); std::string strMIME = "----boundary_cswuygtest\r\n";
strMIME += "Content-Disposition: form-data; name=\"ufile01\"; filename=\"upload_test_http_cswuyg.txt\"\r\n";
strMIME += "Content-Type:application/octet-stream\r\n\r\n";
strMIME += "1\r\n";
/////////////////////////////
//除了这个表单之外,后头还必须要有另外的表单信息,否则hfs服务端那边无法写入文件,这里仅为测试。
//strMIME += "----boundary_cswuygtest\r\n" ;
//strMIME += "Content-Disposition: form-data; name=\"name\"\r\n\r\n";
//strMIME += "temp.txt\r\n";
/////////////////////////
strMIME += "----boundary_cswuygtest--\r\n";
/////////////////////////
//可以直接写入
//::WinHttpSendRequest(hRequest, strHeader.c_str(), strHeader.length(), (LPVOID)strMIME.c_str(), strMIME.length(), strMIME.length(), 0); //也可以后面分步写入
::WinHttpSendRequest(hRequest, NULL, , NULL, , strMIME.length(), );
DWORD dwWritten = ;
while(!strMIME.empty())
{
::WinHttpWriteData(hRequest, strMIME.c_str(), strMIME.length(), &dwWritten);
if (strMIME.length() > dwWritten)
{
strMIME.substr(dwWritten);
}
else
{
strMIME = "";
} }
::WinHttpReceiveResponse(hRequest, NULL); char buf[] = { };
DWORD dwToRead = ;
DWORD dwHaveRead = ;
::WinHttpReadData(hRequest, buf, dwToRead, &dwHaveRead);
std::cout << buf << std::endl;
::WinHttpCloseHandle(hRequest);
::WinHttpCloseHandle(hConnect);
::WinHttpCloseHandle(hSession);
}
     wireshark抓包
头域设置:
  表单设置:
 

三、总结

     这两个例子用hfs做服务器测试可以发现在上传文件夹中上传了文件,代码都很简单。测试过程中的最重要的是能尽快的反馈结果,所以能自己搭建一个http服务器很重要,hfs工具,非常简单便捷。
  POST表单最重要的是了解表单的格式。首先要在头域增加Content-Type说明,如:“content-type:multipart/from-data; boundary=--cswuyg”。然后http body填充的数据要以MIME格式上传,每部分数据的开头都是由“--”+boundary开始的,结尾以"--"+boundary+"--"结束。
  如果要上传文件,需要跟服务器约定好格式,譬如上边例子约定在content-Disposition中有filename键的则是上传文件信息,该片的content为文件数据,这应该是比较通用的约定。
 
附上部分资料:

MIME:http://baike.baidu.com/view/160611.htm
MIME来源 : Multipurpose Internet Mail Extensions 多用途互联网邮件扩展。服务器会将它们发送的多媒体数据的类型告诉浏览器,而通知手段就是说明该多媒体数据的MIME类型。
GZIP文件:application/x-gzip
任意的二进制数据:application/octet-stream.
普通文本:text/plain

wireshark:
MIME Multipart Media Encapsulation 代表使用表单上传的所有数据
Encapsulated multipart part 代表表单中不同项的数据
Boundary 用来隔开表单中不同部分的数据
每部分数据的开头都是由“--”+boundary开始的。
如果使用wireshark抓包,而http使用的不是默认的80端口,那么需要抓TCP包,然后再强制Decode为http。
 
HTTP协议的文档:
 
chroimumde breakpad_sender代码中也有post表单上传文件部分,只不过它是用WinInet写的。
 
2015 补充上传文件时的抓包信息:

通过HTTP协议上传文件的更多相关文章

  1. Android用http协议上传文件

    http协议上传文件一般最大是2M,比较适合上传小于两M的文件   [代码] [Java]代码   001import java.io.File;  002import java.io.FileInp ...

  2. c++使用http协议上传文件到七牛云服务器

    使用c++ http协议上传文件到七牛服务器时,比较搞的一点就是header的设置: "Content-Type:multipart/form-data;boundary=xxx" ...

  3. http 协议上传文件multipart form-data boundary 说明--转载

    原文地址:http://xixinfei.iteye.com/blog/2002017 含义 ENCTYPE="multipart/form-data" 说明: 通过 http 协 ...

  4. java http工具类和HttpUrlConnection上传文件分析

    利用java中的HttpUrlConnection上传文件,我们其实只要知道Http协议上传文件的标准格式.那么就可以用任何一门语言来模拟浏览器上传文件.下面有几篇文章从http协议入手介绍了java ...

  5. HTTP协议上传boundary确定&下载content-disposition理解

    HTTP协议上传文件-协议 上传文件需要将form标签 的 ENCTYPE 属性设置为 multipart/form-data属性, 与 application/x-www-form-urlencod ...

  6. libcurl上传文件,添加自定义头

    原文  http://www.cnblogs.com/meteoric_cry/p/4285881.html 主题 curl libcurl参数很多,一不小心就容易遇到问题.曾经就遇到过一个很蛋疼的问 ...

  7. libcurl上传文件

    libcurl参数很多,一不小心就容易遇到问题.曾经就遇到过一个很蛋疼的问题:libcurl断点下载>>   这里主要汇总一下,libcurl上传的二种方式: 1.直接上传文件,类似for ...

  8. 上传文件multipart form-data boundary 说明

    含义 ENCTYPE="multipart/form-data" 说明: 通过 http 协议上传文件 rfc1867协议概述,客户端发送内容构造. 概述              ...

  9. FTP与HTTP上传文件的对比

    许多站点,比如facebook或一些博客等都允许用户上传或下载文件,比如论坛或博客系统的图片. 在这种情况下,通常有两种选择上传文件到服务器,那就是FTP协议和HTTP协议. 以下列出了一些两者的不同 ...

随机推荐

  1. SecurityContextHolder.getContext().getAuthentication() return null

    <security:http> <security:intercept-url pattern="/web/**" access="IS_AUTHENT ...

  2. nginx的日常应用

    nginx的配置文件nginx.conf配置详解如下: user nginx nginx ; Nginx用户及组:用户 组.window下不指定 worker_processes ; 工作进程:数目. ...

  3. 踏上Salesforce的学习之路(三)

    一.创建Invoice对象 为了使我们的这个Warehouse app更加接近现实,我们现在为他创建一个Invoice对象. 先点击右上角的Setup,然后在左侧的Quick Find查找框中输入Ob ...

  4. 在C#中调用EXE文件

    1. 如果exe文件的返回值是int类型,标识操作执行的结果是否成功,例如: class Program { static int Main(string[] args) { return args. ...

  5. webpack模块加载css文件及图片地址

    webpack支持css文件加载并打包,只需安装相应加载器并在配置文件中配置 . 加载的css文件内容会与该模块里的js内容混合封装,这样做的好处是一个js文件包含了所有的css与js内容,有效减少了 ...

  6. #iOS问题记录#关于NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9801)

    响应Apple的号召,将APP里的HTTP请求全部升级为HTTPS,一切配置OK,正常的请求也没问题: 但,当使用SDwebImg缓存图片时,遇到了标题写的问题: 根据资料得: 这个问题的出现是因为i ...

  7. 判断是否为mac电脑 、还是windows操作系统

    /** * 是否为mac系统(包含iphone手机) * */ var isMac = function() { return /macintosh|mac os x/i.test(navigator ...

  8. window.onload与$(document).ready()的区别

    对于很多初学者来说,window.onload出现在代码中的频率非常高,这似乎变成了一种习惯,可是并不知道具体为什么要加这句代码,可以做几个试验对比: 实验一: <script> docu ...

  9. 转:隐马尔可夫模型(HMM)攻略

    隐马尔可夫模型 (Hidden Markov Model,HMM) 最初由 L. E. Baum 和其它一些学者发表在一系列的统计学论文中,随后在语言识别,自然语言处理以及生物信息等领域体现了很大的价 ...

  10. Code First开发系列实战之使用EF搭建小型博客平台

    返回<8天掌握EF的Code First开发>总目录 本篇目录 理解应用需求 数据库设计 创建实体数据模型 创建实体类 创建关系和导航属性 实现DbContext类 执行数据访问 理解仓储 ...