1. 程式人生 > >Http伺服器實現檔案上傳與下載(一)

Http伺服器實現檔案上傳與下載(一)

一、引言

  大家都知道web程式設計的協議就是http協議,稱為超文字傳輸協議。在J2EE中我們可以很快的實現一個Web工程,但在C++中就不是非常的迅速,原因無非就是底層的socket網路編寫需要自己完成,上層的http協議需要我們自己完成,使用者介面需要我們自己完成,如何高效和設計一個框架都是非常困難的一件事情。但這些事情Java已經在底層為我們封裝好了,而我們僅僅只是在做業務層上的事情吧了。

  在本Http伺服器實現中,利用C++庫和socket原套接字程式設計和pthread執行緒編寫。拒絕使用第三方庫。因為主要是讓大家知道基本的實現方式,除去一些安全、高效等特性,但是不管怎麼樣,第三方商業庫的基本原理還是一致的,只是他們對其進行了優化而已。在開始的編寫時,我不會全部的簡介Http的協議的內容,這樣太枯燥了,我僅僅解釋一些下面需要用到的協議欄位。

  在寫本文的時候,之前也有些迷惑,C++到底能幹啥,到網上一搜,無非就是能開發遊戲,嵌入式程式設計,寫伺服器等等。接著如果問如何編寫一個伺服器的話,那麼這些網路水人又會告訴你,你先把基礎學好,看看什麼書,之後你就知道了,我只能呵呵了,在無目的的學習中,儘管看了你也不知道如何寫的,因為儘管你知道一些大概,但是沒有一個人領導你入門,我們還是無法編寫一個我們自己想要的東西,我寫這篇部落格主要是做一個小小的敲門磚吧,儘管網上有許多部落格,關於如何編寫HTTP伺服器的,但是要不是第三方庫acl,要麼就是短短的幾行程式碼,要麼就是加入了微軟的一些C#內容或者MFC,這些在我看來只是一些無關緊要的東西,加入後或許介面上你很舒服,但是大大增加了我們的學習成本,因為這些介面上的程式碼改變了我們所知道的程式流程走向,還有一些介面程式碼和核心程式碼的混合,非常不利於學習。

二、HTTP協議

     在大家在瀏覽器的url輸入欄上輸入http://10.1.18.4/doing時。瀏覽器向10.1.18.4伺服器80埠的程序傳送瞭如下的一個協議頭,它是一個文字字串。每行以\r\n結束。表示回車換行。

複製程式碼
1 GET /doing HTTP/1.1
2 Host: 10.1.18.4
3 User-Agent: Mozilla/5.0 (Windows NT 6.2; rv:40.0) Gecko/20100101 Firefox/40.0
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
5 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
6 Accept-Encoding: gzip, deflate 7 Referer: http://10.1.18.4/ 8 Connection: keep-alive
複製程式碼

  所以知道其實我們傳送了一個URL請求,其實被轉化為了一個如上的一些字串。在這裡我簡單的解釋一下這個協議頭表示什麼,因為在網上你可以找到非常多的資訊來解釋它們。

  1)第一行中 GET /doing HTTP1.1 表示請求的方式是GET,URL是/doing ,HTTP協議的版本是1.1

  2)第二行中 Host 就是伺服器的IP

  3)第三行中 User-Agent代表著你使用的是什麼瀏覽器在什麼系統上執行的。從上本可以這條資訊顯示是window上火狐瀏覽器發出的請求頭

  4)第四行中Accept代表著該瀏覽器可以接受的資訊格式,可以是文字,html,或者應用檔案(二進位制檔案)。其中q代表權重,表示更願意接受前面的資訊。還有一些其他的內容,讀者可以自己百度。

  5)以下的一些資訊中,沒有什麼用到,我就不解釋,看文字意義也大概知道一些資訊。詳細的請搜尋網路。

在最重要的是一本請求頭什麼時候表示結束呢,那就是一個空行表示結束。其實就是"\r\n"結束。

  說了這麼多可能大家還是有點迷糊,知道這些那麼在程式中又是怎麼實現的呢。當初我也迷惑,現在我提出一個最簡單的一種實現,就是直接連線一個字串即可。在實際實現中我對其進行了分解,但是現在,我解釋為如下編寫程式:

複製程式碼
1 char *str= "GET /doing HTTP/1.1\r\n\
2 Host: 10.1.18.4\r\n\
3 User-Agent: Mozilla/5.0 (Windows NT 6.2; rv:40.0) Gecko/20100101 Firefox/40.0\r\n\
4 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*//*;q=0.8\r\n\
5 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3\r\n\
6 Accept-Encoding: gzip, deflate\r\n\
7 Referer: http://10.1.18.4/\r\n\
8 Connection: keep-alive\r\n\
9 Range: bytes=14584264-\r\n\r\n" ;
複製程式碼

      可能上面的協議內容跟之前的有點不一樣,沒關係,我只是截取了一些內容進行輸入。很簡單就是C語言的char*字串。在沒一行的的結尾都都有一個'\',表示表示換行輸入,去掉也行,需要把器內容寫到一行上,是C語言語法,不懂的讀者可以自己查閱C語言的字串。我想說的是在每行的結尾都有一個\r\n。這兩個轉義字元就是代表回車換行。並且在第9行有2個\r\n,最後一個代表著空行,意思是說告訴伺服器我的協議頭到此位置。

  為什麼需要一個空行呢,這裡就有一個網路程式設計的小小資訊。在socket TCP流程式設計中,比如你呼叫了write或read函式,內部不是一次性接受或者傳送所有的資訊。所以當我們傳送上述的str的時候,不一定一次全部的傳送,那麼服務端就不知道什麼時候結尾了。所以我們需要HTTP規定以空行作為結尾代表著協議頭的結束。

  接下面了來就是我們編寫的伺服器接受到這個字串。並且以空行表示接受到整個協議頭,然後對其進行解析。下面就是解析這段字串的程式碼,在工程中我對其封裝,但是現在我們只要知道實現解析功能即可。

複製程式碼
 1 #include <iostream>
 2 #include <cstring>
 3 #include <vector>
 4 #include <map>
 5 #include <algorithm>
 6 using namespace std;
 7 
 8 char *str= "GET /download/JBPM4S.tt HTTP/1.1\r\n\
 9 Host: 10.1.18.4\r\n\
10 User-Agent: Mozilla/5.0 (Windows NT 6.2; rv:40.0) Gecko/20100101 Firefox/40.0\r\n\
11 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*//*;q=0.8\r\n\
12 Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3\r\n\
13 Accept-Encoding: gzip, deflate\r\n\
14 Referer: http:http://10.1.18.4/\r\n\
15 Connection: keep-alive\r\n\
16 Range: bytes=14584264-\r\n\r\n" ;
17 
18 string& ltrim(string &str) {  
19     string::iterator p = find_if(str.begin(), str.end(), not1(ptr_fun<int, int>(isspace)));  
20     str.erase(str.begin(), p);  
21     return str;  
22 }  
23   
24 string& rtrim(string &str) {  
25     string::reverse_iterator p = find_if(str.rbegin(), str.rend(), not1(ptr_fun<int , int>(isspace)));  
26     str.erase(p.base(), str.end());  
27     return str;  
28 }  
29   
30 string& trim(string &str) {  
31     ltrim(rtrim(str));  
32     return str;  
33 }  
34 string getContent(string& str,int start,char c,int &pos){
35     int i=start;
36     int len=str.size();
37     while(i<len&&str[i]!=c){
38         i++;
39     }
40     pos=i;
41     return str.substr(start,i-start);
42 }
43 map<string,string> parseHeader(char* str){
44     int len=strlen(str);
45     vector<string> vs;
46     int i=0;
47     while(i<len){
48         if(str[i]!='\r'){
49             int j=i;
50             while(i<len&& str[i]!='\r')
51                 i++;
52             vs.push_back(string(str+j,str+i));
53         }else{
54             i+=2;
55         }
56     }
57     int pos;
58     string method=getContent(vs[0],0,' ',pos);
59     string url=getContent(vs[0],method.size()+1,' ',pos);
60     map<string,string> mp;
61     mp["Method"]=method;
62     mp["Url"]=url;
63     for(int i=1;i<vs.size();i++){
64         string key=getContent(vs[i],0,':',pos);
65         string value=vs[i].substr(pos+1);
66         mp[key]=trim(value);
67     }
68     return mp;
69 }
70 
71 int main(int argc, char **argv)
72 {
73     map<string,string> mp =parseHeader(str);
74     for(map<string,string>::const_iterator it=mp.begin();it!=mp.end();++it){
75         cout<<it->first <<"   "<<it->second<<endl;
76     }
77     return 0;
78 }
複製程式碼

      把一些資訊解析都放到了一個map裡面。這裡的解析是先處理每一行,然後再對每一行進行解析。可能這樣的處理方式有點慢,但是沒什麼關係,原因是字串反正比較短,在大併發下效率不會影響太大,如果大家有什麼更好的解析方式,可以回覆我。

      在服務端解析頭資訊後,我們可以得到/doing這個url,這樣我們服務請就可以把客戶端需要的內容返回給客戶端了,這裡就有瀏覽器請求的內容是否合法是否存在這些資訊。就要在相應的響應頭中說明,在《Http伺服器實現檔案上傳與下載(二)》中會進行說明。

     歡迎大家一起探討這些問題。有什麼想法的人給我回復,我們一起學習,一起進步哦。

相關推薦

Http伺服器實現檔案下載()

一、引言   大家都知道web程式設計的協議就是http協議,稱為超文字傳輸協議。在J2EE中我們可以很快的實現一個Web工程,但在C++中就不是非常的迅速,原因無非就是底層的socket網路編寫需要自己完成,上層的http協議需要我們自己完成,使用者介面需要我們自己完

Http伺服器實現檔案下載(二)

一、引言 歡迎大家接著看我的部落格,如何大家有什麼想法的話回覆我哦,閒話不多聊了,接著上一講的內容來說吧,在上一節中已經講到了請求頭字串的解析,並且在解析中我我們已經獲取了url。就是上節中提到的/doing。當瀏覽器傳送了/doing請求後,這是的與伺服器的連線並沒有

Http伺服器實現檔案下載(五)

一、引言      歡迎大家和我一起編寫Http伺服器實現檔案的上傳和下載,現在我回顧一下在上一章節中提到的一些內容,之前我已經提到過檔案的下載,在檔案的下載中也提到了檔案的續下載只需要在響應頭中填寫Content-Range這一欄位,並且伺服器的檔案指標指向讀取的指定

Http伺服器實現檔案下載(三)

一、引言   在前2章的內容基本上已經講解了整個的大致流程。在設計Http伺服器時,我設計為四層的結構,最底層是網路傳輸層,就是socket程式設計。接著一層是請求和響應層,叫做Request和Response。在上一層是URL解析流程走向層。最頂層我設計為索引層。這一層主要多檔案時對檔案進行記憶體上的索引

Http伺服器實現檔案下載(四)

一、引言   歡迎大家來到和我一起編寫Http伺服器實現檔案的上傳和下載,現在我稍微回顧一下之前我說的,第一、二章說明說明了整體的HTTP走向,第三章實現底層的網路程式設計。接著這一章我想給大家講的是請求獲取,和響應傳送的內容。這裡主要講解的響應內容,為什麼?因為我們編寫的是一個與瀏覽器互動的HTTP伺服器

struts2實現檔案下載功能

一、Demo介紹 基於struts2框架,實現多檔案的上傳和下載功能。 實現原理圖: 部分介面圖: 上傳成功及下載頁面: 二、主要程式碼 uploadFile.jsp:在form表單中包含一個文字框(上傳使用者的姓名)和兩個檔案上傳選項. <%@

JAVA通訊(1)-- 使用Socket實現檔案下載

客戶端 /** * 檔案上傳客戶端 * * @author chen.lin * */ public class UploadClient extends JFrame { /** * */ priva

使用jsp/servlet簡單實現檔案下載

public class UploadServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,

Spring 實現檔案下載

檔案上傳 upload.jsp: <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <%@ include file="

struts2實現檔案下載

一、單檔案上傳 1、檔案上傳條件: (1)請求方法必須是post (2)enctype的屬性值必須為multipart/form-data (3)提供一個檔案選擇域 2、檔案上傳jsp程式碼 <%@ page language="java" c

C# 之 FTP伺服器檔案下載(二)

        通過上一篇部落格《C# 之 FTP伺服器中檔案上傳與下載(一)》,我們已經建立好了一個FTP伺服器,並且該伺服器需要使用者名稱和密碼的驗證。今天我們來實現檔案的上傳。 首先,我們前臺需要一個FileUpload控制元件和一個Button控制元件 <

java實現檔案下載

           東風化宇,檔案上傳 一、對於檔案上傳,瀏覽器在上傳的過程中是將檔案以流的形式提交到伺服器端的,Servlet獲取上傳檔案的輸入流然後再解析裡面的請求引數是比較麻煩。 JSP程式碼,POST請求,表單必須設定為enctype="multipar

Http服務器實現文件下載()

80端口 window har name content cti 封裝 商業 利用 一、引言   大家都知道web編程的協議就是http協議,稱為超文本傳輸協議。在J2EE中我們可以很快的實現一個Web工程,但在C++中就不是非常的迅速,原因無非就是底層的socket網絡編

基於TCP伺服器檔案下載

** ## service.c ** #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include &l

SpringBoot下檔案下載實現

本文歡迎轉載,轉載請註明出處,謝謝~(作者:喝酒不騎馬 Colton_Null) from CSDN SpringBoot後臺如何實現檔案上傳下載? 最近做的一個專案涉及到檔案上傳與下載。前端上傳採用百度webUploader外掛。有關該外掛的

檔案下載的功能實現

檔案上傳 檔案上傳原理分析 1、檔案上傳的必要前提: 一、進行檔案上傳時只能使用post方式提交表單 二、表單必須新增一個屬性:enctype=”multipart/form-data” 三、用於上傳檔案的元素必須時 2、enctype屬性 作

java使用Jsch實現遠端操作linux伺服器進行檔案下載,刪除和顯示目錄資訊

1 package com.fline.aic.utils; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileInputStream; 6 import jav

基於tobato的fastdfsspring boot整合實現檔案下載

專案結構: pom.xml檔案新增配置: <!-- fastdfs --> <dependency> <groupId>com.github.tobato</groupId> <artifactId>fastd

基於js-ipfs-api實現ipfs的檔案下載

配置本地的ipfs節點 # 初始化ipfs節點 ipfs init # 執行ipfs節點 ipfs daemon 使用js-ipfs-api呼叫ipfs服務 連線本地ipfs節點 const ipfsAPI = require('ip

Struts2實現檔案,多檔案下載(十)

    “兩個蝴蝶飛”特別喜歡"java1234知識分享網"小峰的實用主義,所以本文及其系列文章均是採用實用主義,從專案和程式碼的角度去分析。由於本人經驗有限,嘴皮子不溜,所以學術性,概念性,底層性的知識點暫時不做介紹。文章中有錯誤之處,歡迎拍磚和指點。特別感謝"java12