因为工作中做个客户端,需要和web后端打交道,用的都是http的接口。对http接口也不是很熟,就自己搭个环境测试了解这个过程。
后端就随便选用一个可以用的框架就好了。框架反正都是调用api基本都是一个套路,平台搭建好了调调接口解析一下就ok了。而这里主要着重于http协议进行分析,从而对http有直观的认识。
文章目录
1.整体框架说明
整个网络通信采用http通信。后面会按几个功能进行单独分析。
客户端这边用c++,使用libcurl的库,运行在某类U系统上。编译这边的环节省略,一般系统会自带有这个库。关于这个库的使用,建议参考官方文档和test。
https://curl.haxx.se/libcurl/c/
服务器这端采用apache+php的框架,跑在ubuntu系统上。
然后整个过程保持网络畅通。。。。
2.工具使用说明
2.1.如何查看http报文
方法1
libcurl库提供一个接口选项:
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
在执行前打开此接口即可查看通信过程的信息,也可以看到报文内容
方法2
服务器端查看报文。可以在请求脚本里将接收到的报文定向输出到文件。
方法3
借助网络抓包工具。抓包工具有很多,这里可以用wireshark。wirshark的功能比较多,这里我用过滤器去过滤出我需要的监听信息就好了。
因为我知道服务器的ip,所以可以在过滤器里填写:
ip.dst == xx.xx.xx.xx or ip.src == xx.xx.xx.xx
然后监听,这个过程中请求的数据就会被捕捉下来了
其实这个工具用来了解整个网络协议是挺好的,然后我们只需要http/txt,选中这个工具。
这里可以查看流数据信息以及通信流程。具体流数据的解析详见后面。
2.2.命令行发送http请求
发送http请求可以是用curl工具,本身他就是libcurl库的一个产物工具。它相当于把基本功能都集成在一个命令里了。
如果使用curl详见man参考文档或者其他资料。如果要查看通信的详细过程可以加上-v参数。
3.POST上传文件
这里不再用简单的get或post请求来做示范,而是来解释发送文件。为此特地找了个大小较大的XML文件作为发送数据,服务器仅接受数据,应答随意。这里用表单的方式进行数据填充,之前还是过直接填入到报文主体,也是ok的。其实说到底还是一个数据解析的问题。
1.客户端
curl命令
curl命令中又一个-F 的参数将会以multipart/form-data发送POST数据;如果是发送文件的话,需要在参数前面加@。
客户端代码
bool send_postReq(const char *url, const char *filename)
{
bool ret = true;
char head1[] = "Content-Type:multipart/form-data"; //采用表单的方式
char head2[] = "Accept: */*";
CURL *curl;
CURLcode rcode;
struct curl_httppost *formpost = NULL;
struct curl_httppost *lsptr = NULL;
struct curl_slist *headers = NULL;
curl_global_init(CURL_GLOBAL_DEFAULT);
headers = curl_slist_append(headers, head1);
headers = curl_slist_append(headers, head2);
curl = curl_easy_init();
if (curl == NULL)
{
goto lb1;
ret = false;
}
curl_global_cleanup();
curl_formadd(&formpost, &lsptr, //填写表单
CURLFORM_COPYNAME, "file",
CURLFORM_FILE, filename,
CURLFORM_CONTENTTYPE, "text/xml",
CURLFORM_END);
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &out);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_respData);
#ifdef CURL_DEBUG
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
#endif
//ssl
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L);
if (curl_easy_perform(curl) != CURLE_OK)
{
printf("%s\n", curl_easy_strerror(rcode));
ret = false;
}
curl_formfree(formpost);
curl_slist_free_all(headers);
lb1:
curl_easy_cleanup(curl);
return ret;
}
这里有几个比较坑的点:
1.这种easy模式采用的是阻塞的,也就是说当网络产生卡顿的时候,有一定几率会被阻塞到curl_easy_perform这个函数里。最坑的是即使网络恢复,也不能出来。所以这里解决这个问题有几个方式。一个是curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L)来设置超时时间。而是开多线程来管理每个curl_easy_perform,同时万一被阻塞了还要想办法终止它。还有一种是可以参考multi模式。
2.关于ssl的问题
之前设置CURLOPT_SSL_VERIFYPEER和CURLOPT_SSL_VERIFYHOST两个选项的值一直没用,其实参考官方test会发现,是对curl_global_init(CURL_GLOBAL_DEFAULT)的设置值有要求。这种配置情况下,http和https是通用的。
3.curl_formadd官方好像并不推荐用这个???
2.分析
连接和断开
http是建立与tcp上的,因此一下子就能看到3次握手4次挥手的协议。
三次握手:
四次挥手:
整个过程:
http的数据
从wireshark上看很明显发现是被是数据被分割发送了。这个数字应该和最大发送单元(MTU)有关。我们看到的数据单位是1500,可以从seq来得知当前收到数据的位置。当收到最后一个包的时候,就被标示成了http/xml协议。
这里的分包其实就是TCP的拆包和滑动窗口来接受数据。
报文主体格式
关于文件大小:
通过http显示报文得知总共数据大小:285819.而实际发送的文件大小是:285599.这里的大小是指报文主体内容的大小。可以查看数据信息,从First boundary这里到xml开始前的值是正好是两个的差值。
![在这里插入图片描述](https://img-blog.csdnimg.cn/2019111810501636.png
然后最后看一下用表单发送文件的格式:
到这里其实已经非常清楚这个表单发送文件,是如何发送的了。文件的数据被填充到了前面关于描述content的信息之后,直接插入到后面的文本里。