前一段时间,做过一个项目,我这边是客户端,需要把数据实时的传到服务器上去,使用的协议是http;之前一直认为,通过socket向服务器发送数据
是一件很简单的事情,只要两边把协议定好,一般不会出什么问题;但这个项目,彻底颠覆了我之前的这个认识;
这个项目中,和服务通信之前,需要先登录一下服务器,这里所谓的登录,其实就是按照固定的格式发送一些数据;这个很简单,服务器登录成功了,
但是后面向服务器发送数据后,却收不到服务器的响应消息;read函数返回的字节数为0;这不是很奇怪吗?检查代码,也没有发现问题,很困惑;第二天和
服务器那边沟通,他们说,他们用postman测试是好的,登录ok,发送数据也ok;这下提示了我,原来,客户端和服务器之间采用的是短连接;登录成功后,
需要断开连接;有数据发送时,在建立连接,发送数据;发送完毕,再断开连接;实际工程中,采用短连接还是长连接,需要双方协定,这次也没有和服务器
确认,就默认了长连接,才导致了这个问题;
现在数据可以发送了,服务器有回复了,但是,返回错误,服务器端说是不能解析上传的数据;这个项目,客户端向服务器上传数据时,使用http协议,
采用http的multipart/form-data格式来发送数据;就是可以传输多个部分的数据,每个部分的数据格式可以不同;http规定各个部分的数据通过分割符boundary来隔开;
--Boundary\r\n
Content-Disposition\r\n
Content-type\r\n
Data\r\n
--Boundary\r\n
Content-Disposition\r\n
Content-type\r\n
Data1\r\n
--Boundary--\n
最后是以Boundary结束
下面是我发送的数据:
检查了格式,没有发现什么问题,网上百度,也都是这么写的,但到我这,就是有问题,服务器不认!郁闷、困惑。。。
第二天,想了一个办法,那就是用postman发送数据,通过wareshark抓包,看看它的数据格式怎么写的;
等抓完包,发现,除了个别字段外,也没有什么不同呀,postman发出的包里面的user-agent字段和我的不同,其他的也一样啊。
难道server还管数据是通过什么工具发出来的?不会呀。
实在没办法了,只能才起hex数据对比了!我采用了和postman相同的boundary,抓取了log,从content-Type字段开始,
一个字节一个字节的和postman比,到boundary这个地方,发现对不上了,刚在拷贝时,还在计算字符个数呢,怎么不对了呢?
再次对比,发现了问题,数据里面有多个boundary,把他们放到一块一比,一切都明白了!
boundary出现在三个地方,第一个地方是在header部分:
Content-Type:multipart/form-data; boundary=myboundary\r\n
第二个地方是各部分数据的边界地方;
--myboundary\r\n
第三个地方是,所有数据结束的地方:
--myboundary--\r\n
可以看到,这个三个地方boundary的写法是有所不同的,第二部分比第一部分前面多了两个“-”;第三个地方的末尾也多了两个“-”;类似这个
boundary=myboundary
的情况,好像也没有什么,一目了然,但对于类似
boundary= --------------------------693106696888796587382791
的情况,那么这三个地方的区别就不那么明显了!
原来是这样!立马改代码,再次测试,ok!
下面附上这一段代码,也许有人会用得着;
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "clt_tcp.h"
#define my_log printf
#define my_printf printf
#define u8 unsigned char
void print_hex(u8* dat_buf, int dat_buf_len)
{
int i = 0;
my_printf("\ndat_len:%d \n", dat_buf_len);
for(i = 0; i<dat_buf_len; i++)
{
if(0 == i%16)
{
my_printf(" \n");
}
my_printf(" %02x", dat_buf[i]);
}
my_printf(" \n");
}
int main(int argc, char *argv[])
{
#define REQ_HEAD_POST "POST /upload.ashx HTTP/1.1\r\n"
#define REQ_HEAD_HOST "Host: healthgw.test.com.cn\r\n"
#define REQ_HEAD_CLOSE "Connection: close\r\n"
#define REQ_HEAD_AGENT "User-Agent: FL_V1\r\n"
#define REQ_HEAD_CONT_TYPE "Content-Type: multipart/form-data; boundary=--------------------------693106696888796587382791\r\n"
#define REQ_DAT_CONT_TYPE "Content-Type: text/plain:charset=UIF-8\r\n"
#define REQ_HEAD_BOUNDARY "----------------------------693106696888796587382791\r\n"
#define REQ_HEAD_CONT_DISPOSIT_FILE "Content-Disposition: form-data; name=\"file\"; filename=\"\"\r\n\r\n\r\n"
#define REQ_HEAD_CONT_DISPOSIT_TEXT "Content-Disposition: form-data; name=\"key\"\r\n\r\n"
#define REQ_HEAD_BOUNDARY_LAST "----------------------------693106696888796587382791--\r\n"
char *send_dat = "opt=02&sn=4428A30032A0&token=AEFE1085F4A07E5C186185D08D6EDD0E2DC1FE9E2B0A3E0CCD8FB9A4383842E7&data=F2FEBBFC870E5AD3203C75A1996A1D1142FF8D838078B7BCF0F44D943A317E3C22642CCAF3185DC5&sign=cb3bc8defda5ee1242926f67f0929f75\r\n";
char head_line_content_len[256] = {0};
char send_buf[2048] = {0};
char content_buf[2048] = {0};
my_log("%s %d start.\n", __FUNCTION__, __LINE__);
/*content buf*/
strcat(content_buf, REQ_HEAD_BOUNDARY);
strcat(content_buf, REQ_HEAD_CONT_DISPOSIT_FILE);
strcat(content_buf, REQ_HEAD_BOUNDARY);
strcat(content_buf, REQ_HEAD_CONT_DISPOSIT_TEXT);
strcat(content_buf, REQ_DAT_CONT_TYPE);
strcat(content_buf, send_dat);
strcat(content_buf, REQ_HEAD_BOUNDARY_LAST);
my_log("%s %d content:[%d]%s.\n", __FUNCTION__, __LINE__, (int)strlen(content_buf), content_buf);
snprintf(head_line_content_len, sizeof(head_line_content_len), "Content-Length: %d\r\n\r\n", (int)strlen(content_buf));
/*build send buf*/
strcat(send_buf, REQ_HEAD_POST);
strcat(send_buf, REQ_HEAD_HOST);
strcat(send_buf, REQ_HEAD_CLOSE);
strcat(send_buf, REQ_HEAD_AGENT);
strcat(send_buf, REQ_HEAD_CONT_TYPE);
strcat(send_buf, head_line_content_len);
strcat(send_buf, content_buf);
my_log("%s %d send_buf:[%d]\n%s.\n", __FUNCTION__, __LINE__, (int)strlen(send_buf), send_buf);
print_hex((u8*)send_buf, strlen(send_buf));
clt_tcp_send(send_buf);
my_log("%s %d end.\n", __FUNCTION__, __LINE__);
return 0;
}