C/C++ http协议发送字段,文件,单个和多张图片
? ? ? ?關(guān)于c/c++?網(wǎng)絡(luò)編程,無論在linux還是windows,要說到自由性,和安全性,socket無疑是比較好的!對于socket,因為它的傳輸協(xié)議只有兩種tcp和udp,屬于網(wǎng)絡(luò)層,這里我們不去重點討論。
? ? ?關(guān)于應(yīng)用層協(xié)議http,如何用C/C++的socket來實現(xiàn)數(shù)據(jù)傳輸和下載呢?
? ?1. http是超文本協(xié)議,用在html文件中,那么對于html是如何傳輸數(shù)據(jù)呢? ??
?通過post或者get傳輸表單數(shù)據(jù),當(dāng)然http還有其他的方式head,put ,delete,option,trace等方式。head和get差不多,唯一的區(qū)別就是head只返回協(xié)議頭,put和post也很相似,但是可惜html表單數(shù)據(jù)不支持這一特性,put和post的區(qū)別在于,put說出來資源放置于服務(wù)器的位置,而post沒有,post將這項權(quán)利給予服務(wù)器來使用。delete顧名思義,就是指定刪除在服務(wù)器上的資源,option一般用來獲取當(dāng)前URl所支持請求的方法(就是上訴的六種)。
? ? ? ?對于c/c++傳輸單數(shù)據(jù),get方法:
? ? ? ? ?get方法, ?形如:?http://i.cnblogs.com/EditPosts.aspx?opt=1?
?這個表單傳輸?shù)臄?shù)據(jù)就是1,其中鍵值就是opt,這個需要和服務(wù)器上的保持一致
? ? ? 對于一個簡單的html
1 <html> 2 <head><title>右邊</tile></head> 3 <body> 4 <form > 5 <input type="text", name="opt" > 1 </input> 6 </form> 7 </body> 8 </html>? opt就是鍵值
? 那么用socket如何實現(xiàn):?
? ? 首先,windows下,我們
? ?1. 先要啟動異步套接字啟動命令
//初始化套結(jié)字動態(tài)庫2 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) //異步套接字啟動命令3 /版本(次,主) //返回socket實現(xiàn)細節(jié)信息4 {5 system("WSAStartup failed!\n");6 system("pause");7 return -1;8 }2.在想linux下一樣,創(chuàng)建套接字
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);3.綁定端口號,和設(shè)置要訪問的服務(wù)器主機地址
//設(shè)置服務(wù)器地址servAddr.sin_family = AF_INET;servAddr.sin_addr.s_addr = inet_addr("203.195.192.24");servAddr.sin_port = htons((short)80);4.連接服務(wù)器
1 retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
5.然后接收信息字段
char *pHttpGet = "GET %s?%s HTTP/1.1\r\n""Host: %s:%d\r\n\r\n"; char strHttpGet[1024] = { 0 };//ZeroMemory(strHttpGet, BUF_SZIE); //初始化內(nèi)存char msg[]="username=Gjxun&pwd=sssssss";sprintf(strHttpGet, pHttpGet, addr, msg, host, port);int var = send(sHost, strHttpGet, strlen(strHttpGet), 0); recv(sHost,rebuf ,sizeof(rebuf),0);最后關(guān)閉的時候。需要用這個來關(guān)閉異步套接字
1 WSACleanup( );這是http的基本流程,對于get發(fā)送單個或者多個表單數(shù)據(jù)如上面所示
對于post而言,情況 會多些,也會復(fù)雜些
? ?1.如果發(fā)送的是單個或者多個字段信息,那么我們的處理方式大致可以有下面這兩種
? ?第一種: 就像get一樣,只不過單純的將數(shù)據(jù)放置于協(xié)議的后面,需要注意點的是,格式比較重要,特別協(xié)議頭和正文部分之間需要各一個空行:
? ? 下面的msg亦可以和get一樣寫成 msg="username=Gxjun&pwd=ssssss"; 還有content-Length的長度: 是正文和正文數(shù)據(jù)以及尾部長度之后不需要算協(xié)議頭長度,不然會,當(dāng)將連接改為Connection: Keep-Alive?出現(xiàn)服務(wù)器長時間接受現(xiàn)象。---指導(dǎo)服務(wù)器接受到結(jié)尾幀或者數(shù)據(jù)長度達到那個長度為止,才會響應(yīng)剛才的動作!!!!
1 void sendPost1(char* addr, char * host, char *msg, int port) {2 char *pHttpPost = "POST %s HTTP/1.1\r\n"3 "Host: %s:%d\r\n"4 "Content-Type: application/x-www-form-urlencoded\r\n"5 "Content-Length: %d\r\n\r\n"6 "%s";7 13 char strHttpPost[1024] = { 0 }; 14 //ZeroMemory(strHttpGet, BUF_SZIE); //初始化內(nèi)存 15 sprintf(strHttpPost, pHttpPost, addr, host, port, strlen(msg), msg); 16 int var = send(sHost, strHttpPost, strlen(strHttpPost), 0); 17 if (var < 0) { 18 MessageBoxA(NULL, "請求發(fā)送失敗!", 0, 0); 19 return; 20 } 21 }另一種方式:多種數(shù)據(jù)表單的形式:協(xié)議頭部分,將Content-Type: multipart/form-data; 同時還需要加上一個分割標(biāo)識,即boundary = Gxjunnndgx ,
? ? 整體上就是設(shè)置為?Content-Type:?multipart/form-data;?boundary=71b23e4066ed\r\n"; ?
? ?其他部分參開rfc2038部分。
? 所以對于單個或者多個字段表單而言:
比如: 需要像如下的html文件一樣將username和pwd的鍵值數(shù)據(jù)發(fā)送給服務(wù)器數(shù)據(jù)數(shù)據(jù):
<html><head></head> <body><form action="xxx.xxx.xxxx" method="post"><input type="text" name="username">Gxjun</input><input type="password" name="pwd">ssssss</input> <form> </body> </html> 1 void sendPost(char* addr, char * host,string username,string psw, int port){2 3 std::string header("");6 std::string u_content(""); //用戶名7 std::string p_content(""); //密碼8 9 //----------------------post頭開始-------------------------------- 10 header += "POST "; 11 header += addr; 12 header += " HTTP/1.1\r\n"; 13 header += "Host: "; 14 header += host; 15 header += "\r\n"; 16 header += "Connection: Keep-Alive\r\n"; 17 header += "Accept: */*\r\n"; 18 header += "Pragma: no-cache\r\n"; 19 header += "Content-Type: multipart/form-data; boundary=71gxjun\r\n"; 20 21 //用戶名數(shù)據(jù)表單 22 u_content += "--71gxjun\r\n"; 23 u_content += "Content-Disposition: form-data; name=\"u\"\r\n\r\n"; 24 u_content += username+"\r\n"; 25 26 //密碼數(shù)據(jù)表單 27 p_content += "--71gxjun\r\n"; 28 p_content += "Content-Disposition: form-data; name=\"p\"\r\n\r\n"; 29 p_content += psw+"\r\n"; 30 //post尾時間戳 31 std::string strContent("--71gxjun--\r\n\r\n"); 32 char temp[64] = { 0 }; 33 //注意下面這個參數(shù)Content-Length,這個參數(shù)值是:http請求頭長度+請求尾長度+文件總長度 34 // 就分塊傳送 35 sprintf(temp, "Content-Length: %d\r\n\r\n", 36 p_content.length()+u_content.length() + strContent.length()); 37 header += temp; 38 std::string str_http_request; 39 str_http_request.append(header); 40 41 //----------------------post頭結(jié)束----------------------------------- 42 //發(fā)送post頭 43 send(sHost, str_http_request.c_str(), str_http_request.length(), 0); 44 Sleep(0.2); 45 send(sHost, p_content.c_str(), p_content.length(), 0); 46 Sleep(0.2); 47 send(sHost, u_content.c_str(), u_content.length(), 0); 48 Sleep(0.2); 49 ::send(sHost, strContent.c_str(), strContent.length(), 0); 50 Sleep(0.2); 51 } 52?對于boundary=abcdegxjun ?這部分的數(shù)據(jù)可以隨意定義,但不要太簡單,不然可能會和數(shù)據(jù)混淆,上面是兩個字段的發(fā)送,所以需要兩部分的正文加正文數(shù)據(jù),對于尾部的結(jié)束標(biāo)識,前面需要“--”兩個橫短線后面也需要兩個橫短線“--”,對于中間的分割標(biāo)志,只需要前面有“--”就可以了! ?還需要注意的是數(shù)據(jù)發(fā)送完之后,需要換行,然后再接上分割標(biāo)識。
4.然后對于文件和照片的傳輸 ? ?---在linux下,一切接文件,在window下我們也可以將照片看做二進制文件處理
? 其實文件的傳輸,都可以作為二進制文件來傳輸,我們可以將文件
1 char * ReadFile(char *pathpic, int &pic_len){2 //將圖片讀取出來3 FILE *fp = fopen(pathpic, "rb"); //打開文件4 if (!fp){5 MessageBoxA(NULL, "沒有找到文件位置", 0, 0);6 return NULL;7 }8 fseek(fp, 0, SEEK_END); //一直尋找到文件尾部9 pic_len = ftell(fp); //得到圖片的長度 10 rewind(fp); //rewind將文件指針指向開頭 11 char *pic_buf = new char[pic_len + 1]; //開辟一個空間在堆上 12 memset(pic_buf, 0, pic_len + 1); //清空文件指針 13 //讀取文件內(nèi)容 14 fread(pic_buf,sizeof(char),pic_len,fp); 15 //測試將文件再保存于D:中 16 /* 17 MessageBoxA(NULL, "文件開始", 0, 0); 18 FILE *fpw = fopen("C:\\AA.jpg","wb"); 19 fwrite(pic_buf,sizeof(char), pic_len, fpw); 20 fclose(fpw); //關(guān)閉文件流 21 MessageBoxA(NULL, "文件結(jié)束", 0, 0); 22 */ 23 fclose(fp); 24 25 return pic_buf; 26 }對于不同的類型,需要修改不同的Content-Type 比如圖片jpg,jpeg等就是需要這種 ,"Content-Type: image/jpeg,對于其他的的類型,不妨去這兒找找,比較詳細
http://tool.oschina.net/commons
然后下面是一個關(guān)于多個字段和多個照片,運用一個form表單,通過一次post,將數(shù)據(jù)上傳到服務(wù)器上! ?注: 這里是在c\s模式, 客戶端是c++ ,服務(wù)器是php
? 代碼如下:
1 char * ReadFile(char *pathpic, int &pic_len){2 //將圖片讀取出來3 FILE *fp = fopen(pathpic, "rb"); //打開文件4 if (!fp){5 MessageBoxA(NULL, "沒有找到文件位置", 0, 0);6 return NULL;7 }8 fseek(fp, 0, SEEK_END); //一直尋找到文件尾部9 pic_len = ftell(fp); //得到圖片的長度10 rewind(fp); //rewind將文件指針指向開頭11 char *pic_buf = new char[pic_len + 1]; //開辟一個空間在堆上12 memset(pic_buf, 0, pic_len + 1); //清空文件指針13 //讀取文件內(nèi)容14 fread(pic_buf,sizeof(char),pic_len,fp);15 //測試將文件再保存于D:中16 /*17 MessageBoxA(NULL, "文件開始", 0, 0);18 FILE *fpw = fopen("C:\\AA.jpg","wb");19 fwrite(pic_buf,sizeof(char), pic_len, fpw);20 fclose(fpw); //關(guān)閉文件流21 MessageBoxA(NULL, "文件結(jié)束", 0, 0);22 */23 fclose(fp); 24 25 return pic_buf;26 }27 28 void sendPic(char* addr, char * host, char *pathpic, char* picname, int port, string username, string psw) {29 30 //先讀取文件流31 //實名圖片讀取,等級圖片讀取32 int Spic_len, Dpic_len;33 char *Spic_data=NULL, *Dpic_data=NULL;34 35 Spic_data=ReadFile(pathpic, Spic_len);36 Dpic_data = ReadFile(picname, Dpic_len);37 std::string header("");38 std::string content(""); //實名文件39 std::string nex_content(""); //等級文件40 std::string u_content(""); //用戶名41 std::string p_content(""); //密碼42 43 //----------------------post頭開始-------------------------------- 44 header += "POST ";45 header += addr;46 header += " HTTP/1.1\r\n";47 header += "Host: ";48 header += host;49 header += "\r\n";50 header += "Connection: Keep-Alive\r\n";51 header += "Accept: */*\r\n";52 header += "Pragma: no-cache\r\n";53 header += "Content-Type: multipart/form-data;boundary=71b23e4066ed\r\n";54 55 //用戶名數(shù)據(jù)表單56 u_content += "--71b23e4066ed\r\n";57 u_content += "Content-Disposition: form-data; name=\"u\"\r\n\r\n";58 u_content += username+"\r\n";59 60 //密碼數(shù)據(jù)表單61 p_content += "--71b23e4066ed\r\n";62 p_content += "Content-Disposition: form-data; name=\"p\"\r\n\r\n";63 p_content += psw+"\r\n";64 65 //發(fā)送文件數(shù)據(jù)66 content += "--71b23e4066ed\r\n";67 content += "Content-Disposition: form-data; name=\"picurl\"; filename=\"";68 content += pathpic;69 content += "\"\r\n";70 content += "Content-Type: image/jpeg \r\n\r\n";71 72 //發(fā)送文件數(shù)據(jù)73 nex_content += "\r\n--71b23e4066ed\r\n";74 nex_content += "Content-Disposition: form-data; name=\"id_account\"; filename=\"";75 nex_content += picname; //picname;76 nex_content += "\"\r\n";77 nex_content += "Content-Type: image/jpeg\r\n\r\n";78 79 //post尾時間戳 80 std::string strContent("\r\n--71b23e4066ed--\r\n");81 char temp[64] = { 0 };82 //注意下面這個參數(shù)Content-Length,這個參數(shù)值是:http請求頭長度+請求尾長度+文件總長度83 // 就分塊傳送 84 sprintf(temp, "Content-Length: %d\r\n\r\n",85 content.length() + nex_content.length() +p_content.length()+u_content.length() + Spic_len + Dpic_len + strContent.length());86 header += temp;87 std::string str_http_request;88 str_http_request.append(header);89 90 //----------------------post頭結(jié)束-----------------------------------91 //發(fā)送post頭 92 send(sHost, str_http_request.c_str(), str_http_request.length(), 0);93 char fBuff[1024];94 int buffsize = 1024; // 每個數(shù)據(jù)包存放文件的buffer大小 95 int nStart;//記錄post初始位置 96 int nSize;//記錄剩余文件大小 97 Sleep(0.2);98 //發(fā)送用戶名表單99 send(sHost, u_content.c_str(), u_content.length(), 0); 100 Sleep(0.2); 101 //發(fā)送密碼表單 102 send(sHost, p_content.c_str(), p_content.length(), 0); 103 Sleep(0.2); 104 //發(fā)送尾部 105 //發(fā)送格式 106 send(sHost, content.c_str(), content.length(), 0); 107 Sleep(0.2); 108 send(sHost, Spic_data, Spic_len, 0); 109 Sleep(0.2); 110 //發(fā)送等級圖片數(shù)據(jù) 111 send(sHost, nex_content.c_str(), nex_content.length(), 0); 112 Sleep(0.2); 113 send(sHost, Dpic_data, Dpic_len, 0); 114 Sleep(0.2); 115 //如果數(shù)據(jù)是在夠大,需要作調(diào)整,可以使用如下的方式,切割文件發(fā)送數(shù)據(jù) 116 /* 117 for (int i = 0; i < Spic_len; i += bufsize) 118 { 119 nStart = i; 120 if (i + bufsize + 1> Spic_len){ 121 nSize = Spic_len - i; 122 } 123 else{ 124 nSize = bufsize; 125 } 126 127 memcpy(fBuff, Spic_data + nStart, nSize); 128 ::send(sHost, fBuff, nSize, 0); 129 Sleep(0.2); //防止氈包 130 } 131 132 //發(fā)送等級圖片數(shù)據(jù) 133 ::send(sHost, nex_content.c_str(), nex_content.length(), 0); 134 Sleep(0.2); 135 bufsize = 4096; 136 for (int i = 0; i < Dpic_len; i += bufsize) 137 { 138 nStart = i; 139 if (i + bufsize + 1> Dpic_len){ 140 nSize = Dpic_len - i; 141 } 142 else{ 143 nSize = bufsize; 144 } 145 146 memcpy(fBuff, Dpic_data + nStart, nSize); 147 ::send(sHost, fBuff, nSize, 0); 148 Sleep(0.2); //防止氈包 149 } 150 */ 151 /* 152 for (int i = 0; i < Dpic_len; i += nPacketBufferSize) 153 { 154 nStart = i; 155 if (i + nPacketBufferSize + 1> Dpic_len){ 156 nSize = Dpic_len - i; 157 } 158 else{ 159 nSize = nPacketBufferSize; 160 } 161 162 memcpy(fBuff, Dpic_data + nStart, nSize); 163 ::send(sHost, fBuff, nSize, 0); 164 Sleep(0.2); //防止氈包 165 }*/ 166 167 send(sHost, strContent.c_str(), strContent.length(), 0); 168 Sleep(0.2); 169 170 if (Spic_data == NULL) 171 { 172 MessageBox(NULL, L"文件數(shù)據(jù)為空", 0, 0); 173 } 174 //釋放內(nèi)存 175 delete Spic_data; 176 delete Dpic_data; 177 178 }? ? ?當(dāng)這些基本做好了之后,就需要看返回的結(jié)果:
? ? ? ?對于返回http返回結(jié)果協(xié)議頭的簡單解析: 如果需要深入研究去看 rfc2616,這里就簡單的羅列一些100-500的簡單的含義吧
? ? ? 100-199 用于指定客戶端應(yīng)相應(yīng)的某些動作。? 200-299 用于表示請求成功。? ? ? ? 300-399 用于已經(jīng)移動的文件并且常被包含在定位頭信息中指定新的地址信息。? ? ? ? 400-499 用于指出客戶端的錯誤。? ? ? ? 500-599 用于支持服務(wù)器錯誤。??
詳細的文檔,可以看看這個在線文檔,http://tool.oschina.net/commons?type=5
學(xué)習(xí)的過程中參考過幾位博主,此處表達謝意,終于對http在以前認知的基礎(chǔ)上,再次的又重新的知識了一番!! 記錄些這些,希望對以后學(xué)習(xí)的人,能夠提供一點點幫助!!!
總結(jié)
以上是生活随笔為你收集整理的C/C++ http协议发送字段,文件,单个和多张图片的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c++简单实现http协议服务器和客户端
- 下一篇: C++中使用TCP传文件