關於c/c++ 網絡編程,不管在linux仍是windows,要說到自由性,和安全性,socket無疑是比較好的!對於socket,由於它的傳輸協議只有兩種tcp和udp,屬於網絡層,這裏咱們不去重點討論。php
關於應用層協議http,如何用C/C++的socket來實現數據傳輸和下載呢?html
1. http是超文本協議,用在html文件中,那麼對於html是如何傳輸數據呢? linux
經過post或者get傳輸表單數據,固然http還有其餘的方式head,put ,delete,option,trace等方式。head和get差很少,惟一的區別就是head只返回協議頭,put和post也很類似,可是惋惜html表單數據不支持這一特性,put和post的區別在於,put說出來資源放置於服務器的位置,而post沒有,post將這項權利給予服務器來使用。delete顧名思義,就是指定刪除在服務器上的資源,option通常用來獲取當前URl所支持請求的方法(就是上訴的六種)。c++
對於c/c++傳輸單數據,get方法:編程
get方法, 形如: http://i.cnblogs.com/EditPosts.aspx?opt=1 windows
這個表單傳輸的數據就是1,其中鍵值就是opt,這個須要和服務器上的保持一致安全
對於一個簡單的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如何實現: app
首先,windows下,咱們
1. 先要啓動異步套接字啓動命令
//初始化套結字動態庫 2 if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0) //異步套接字啓動命令 3 /版本(次,主) //返回socket實現細節信息 4 { 5 system("WSAStartup failed!\n"); 6 system("pause"); 7 return -1; 8 }
2.在想linux下同樣,建立套接字
sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
3.綁定端口號,和設置要訪問的服務器主機地址
//設置服務器地址 servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = inet_addr("203.195.192.24"); servAddr.sin_port = htons((short)80);
4.鏈接服務器
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); //初始化內存
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);
最後關閉的時候。須要用這個來關閉異步套接字
1 WSACleanup( );
這是http的基本流程,對於get發送單個或者多個表單數據如上面所示
對於post而言,狀況 會多些,也會複雜些
1.若是發送的是單個或者多個字段信息,那麼咱們的處理方式大體能夠有下面這兩種
第一種: 就像get同樣,只不過單純的將數據放置於協議的後面,須要注意點的是,格式比較重要,特別協議頭和正文部分之間須要各一個空行:
下面的msg亦能夠和get同樣寫成 msg="username=Gxjun&pwd=ssssss"; 還有content-Length的長度: 是正文和正文數據以及尾部長度以後不須要算協議頭長度,否則會,當將鏈接改成Connection: Keep-Alive 出現服務器長時間接受現象。---指導服務器接受到結尾幀或者數據長度達到那個長度爲止,纔會響應剛纔的動做!!!!
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); //初始化內存 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, "請求發送失敗!", 0, 0); 19 return; 20 } 21 }
另外一種方式:多種數據表單的形式:協議頭部分,將Content-Type: multipart/form-data; 同時還須要加上一個分割標識,即boundary = Gxjunnndgx ,
總體上就是設置爲 Content-Type: multipart/form-data; boundary=71b23e4066ed\r\n";
其餘部分參開rfc2038部分。
因此對於單個或者多個字段表單而言:
好比: 須要像以下的html文件同樣將username和pwd的鍵值數據發送給服務器數據數據:
<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 //用戶名數據表單 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 //密碼數據表單 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 //注意下面這個參數Content-Length,這個參數值是: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頭結束----------------------------------- 42 //發送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 這部分的數據能夠隨意定義,但不要太簡單,否則可能會和數據混淆,上面是兩個字段的發送,因此須要兩部分的正文加正文數據,對於尾部的結束標識,前面須要「--」兩個橫短線後面也須要兩個橫短線「--」,對於中間的分割標誌,只須要前面有「--」就能夠了! 還須要注意的是數據發送完以後,須要換行,而後再接上分割標識。
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 //讀取文件內容 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); //關閉文件流 21 MessageBoxA(NULL, "文件結束", 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
而後下面是一個關於多個字段和多個照片,運用一個form表單,經過一次post,將數據上傳到服務器上! 注: 這裏是在c\s模式, 客戶端是c++ ,服務器是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 //讀取文件內容 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); //關閉文件流 21 MessageBoxA(NULL, "文件結束", 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 //用戶名數據表單 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 //密碼數據表單 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 //發送文件數據 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 //發送文件數據 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 //注意下面這個參數Content-Length,這個參數值是: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頭結束----------------------------------- 91 //發送post頭 92 send(sHost, str_http_request.c_str(), str_http_request.length(), 0); 93 char fBuff[1024]; 94 int buffsize = 1024; // 每一個數據包存放文件的buffer大小 95 int nStart;//記錄post初始位置 96 int nSize;//記錄剩餘文件大小 97 Sleep(0.2); 98 //發送用戶名錶單 99 send(sHost, u_content.c_str(), u_content.length(), 0); 100 Sleep(0.2); 101 //發送密碼錶單 102 send(sHost, p_content.c_str(), p_content.length(), 0); 103 Sleep(0.2); 104 //發送尾部 105 //發送格式 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 //發送等級圖片數據 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 //若是數據是在夠大,須要做調整,可使用以下的方式,切割文件發送數據 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 //發送等級圖片數據 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"文件數據爲空", 0, 0); 173 } 174 //釋放內存 175 delete Spic_data; 176 delete Dpic_data; 177 178 }
當這些基本作好了以後,就須要看返回的結果:
對於返回http返回結果協議頭的簡單解析: 若是須要深刻研究去看 rfc2616,這裏就簡單的羅列一些100-500的簡單的含義吧
100-199 用於指定客戶端應相應的某些動做。
200-299 用於表示請求成功。
300-399 用於已經移動的文件而且常被包含在定位頭信息中指定新的地址信息。
400-499 用於指出客戶端的錯誤。
500-599 用於支持服務器錯誤。
詳細的文檔,能夠看看這個在線文檔,http://tool.oschina.net/commons?type=5
學習的過程當中參考過幾位博主,此處表達謝意,終於對http在之前認知的基礎上,再次的又從新的知識了一番!! 記錄些這些,但願對之後學習的人,可以提供一點點幫助!!!