因爲項目的須要,要作一個C++的http請求客戶端實現上傳數據到服務端的功能,服務端用的是Spring MVC實現的Restful Web Service,起初設計時在服務端以byte[]的形式接受數據,畢竟服務端用java寫的,把接收到的數據流解析成相應的文件這是徹底可行的。html
若是寫過Java或者其餘語言的Http請求的人都知道,請求方式能夠分GET和POST兩種,不過GET傳遞的數據充其量只是簡單類型的參數而已,在請求時的數據格式如代碼,這是請求http://www.ip138.com:8080/search.asp?mobile=1565888&action=mobile站點的請求參數:java
GET /search.asp?mobile=1565888&action=mobile HTTP/1.1 Host: www.ip138.com:8080 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive
以上是用FireFox的插件Live Http Headers獲取的數據,並且這個插件能夠自行編輯參數定製HTTP請求,方便編程的測試工做,具體使用能夠查閱資料。這裏面好多參數並非必須的,可是爲了模擬瀏覽器的效果最好設置一下。另外對於GET請求不須要設置Content-Type和Content-Length參數,一般這兩個也是成對出現的。User-Agent表示請求客戶端的瀏覽器和OS信息,這裏模擬瀏覽器訪問能夠本身定製。linux
POST請求的數據是放在請求體裏的,具體信息可參考http://blog.csdn.net/yc0188/article/details/4741871,嚴格按照要求組織請求數據便可。注意請求頭和請求數據之間的空行。這是一個Web系統的POST請求數據:ios
POST /myproject/infoUpdate HTTP/1.1 Host: 192.168.52.250:8088 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive Referer: http://192.168.52.250:8088/myproject/infoUpdate.action Content-Type: application/x-www-form-urlencoded Content-Length: 24 infoId=3¤tPage=1
另外在具體組織請求信息時,Windows下是用\r\n做爲換行符的,可是在linux下這樣用也不錯,雖然linux的換行符是\n。編程
strcat(requestStr, "POST "); strcat(requestStr, api); strcat(requestStr, " HTTP/1.1\r\n"); strcat(requestStr, "Accept: application/x-ms-application, image/gif, " "application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, " "application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*\r\n"); strcat(requestStr, "User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:17.0) Gecko/17.0 Firefox/17.0\r\n"); strcat(requestStr, "Accept-Language: zh-CN,en-US;q=0.5\r\n"); strcat(requestStr, "Accept-Encoding: gzip, deflate\r\n"); strcat(requestStr, "Host: "); strcat(requestStr, hostname); strcat(requestStr, "\r\n");
POST簡單的數據以上就能夠作到,可是對於上傳文件呢。文件一般是二進制形式的,也就是要傳輸流。不過咱們對struts2或者Spring MVC的上傳文件表單不陌生,因而我監測了一個上傳文件操做的請求數據:windows
POST /myproject/infoUpdate HTTP/1.1 Host: 192.168.52.250:8088 User-Agent: Mozilla/5.0 (Windows NT 6.1; rv:14.0) Gecko/20100101 Firefox/14.0.1 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Connection: keep-alive Content-Type: multipart/form-data; boundary=---------------------------41184676334 Content-Length: 95254 -----------------------------41184676334\r\n Content-Disposition: form-data; name="id"\r\n \r\n 6\r\n -----------------------------41184676334\r\n Content-Disposition: form-data; name="doc"; filename="faq_info.jpg"\r\n Content-Type: image/jpeg\r\n \r\n ……請求的數據流…… \r\n-----------------------------41184676334--\r\n
這裏用到了boundary(分隔符),用來間隔不一樣的請求數據,這是Content-Type類型爲multipart/form-data(Spring MVC Restful Web Service也支持multipart/mixed的類型用於上傳文件)時所要求的數據格式,和Content-Type爲application/x-www-form-urlencoded時用&間隔參數是同樣的。可是要注意使用的規則,可參見http://blog.csdn.net/mspinyin/article/details/6141638。Java版本的HttpURLConnection請求可參考:http://lapulande.iteye.com/blog/719581。C++版的能夠用Windows下的winsock,代碼可參考:http://www.cnblogs.com/evlon/archive/2007/08/13/853145.html和http://www.oschina.net/code/snippet_176076_5908分別是windows下的和linux的範例,上述連接代碼只是使用socket完成了請求操做,並無作上傳數據的部分。我建議用到的朋友能夠先用Java版的客戶端進行上傳文件測試,若是測試經過了,能夠比照着寫C++的代碼,在請求頭部分C++老老實實地組織對應的參數就能夠,請求數據部分要嚴格注意換行的地方,以及上傳文件的長度加入Content-Length(包括上傳數據長度和分隔符等其餘字符的長度)。api
文件流的長度能夠這樣得到:瀏覽器
ifstream fin(filePath ,ios::in|ios::binary); if(!fin){ cout<<"File open error!\n"; return -1; } //---get the length of the file int temp = fin.tellg(); fin.seekg(0,ios_base::end); int len = fin.tellg(); fin.seekg(temp);
得到長度後從新定位到文件開始處。app
上傳文件流時要讀取多少send多少,不然服務端獲得的是錯誤的流,且在send時不要用strlen(c)來指定發送的長度,由於char* c中可能包含‘\0’這樣的空字符,從而使用了錯誤的長度。socket
char c[1024]; memset(c, 0,1024); int tmpLen=0; int i=0; while(!fin.eof()) { if(tmpLen<(len/1024)*1024) //首先按照1024的單位傳 { fin.read(c, 1024); send(sock,c,1024,0); Sleep(1); //休息,爲了下降CPU使用率以及照顧帶寬 tmpLen+=1024; }else //若是剩餘的小於等於1024了,單獨send剩餘的字節 { fin.read(c,len-(len/1024)*1024); send(sock,c,len-(len/1024)*1024,0); break; } } fin.close();
在C++裏用socket創建的請求,沒有使用HTTP協議,能夠選擇TCP或者UDP協議,默認用TCP。我在測試中使用WireShark監測傳輸的數據時,在HTTP相關協議中沒有看到數據,緣由就在於此,Java版請求的就能夠檢測到。