構建一個簡單的HTTP服務器的C#程序實例

構建一個簡單的HTTP服務器

一著瓶、程序界面

一個簡單的HTTP服務器

啟動程序,運行后瓣履,在瀏覽器中輸入 http://localhost:8888/ 率翅,可返回結果。

二袖迎、基本原理

1冕臭、HTTP協議

  • HTTP是超文本傳輸協議(HyperText Transfer Protocol)的簡稱,它建立在C/S架構的應用層協議燕锥。
  • TCP/IP協議是協議層的內容辜贵,它定義了計算機間通信的基礎協議。
  • HTTP归形、FTP托慨、Telnet等協議都是建立在TCP/IP協議基礎上的。

2连霉、請求/響應模型

  • 在HTTP協議中榴芳,客戶端負責發(fā)起一個Request,該Request中含有請求方法跺撼、URL窟感、協議版本等信息,
  • 服務端在接受到該Request后會返回一個Response歉井,該Response中含有狀態(tài)碼柿祈、響應內容等信息,
    ??這一模型稱為請求/響應模型。

3躏嚎、HTTP報文

??HTTP協議通信的核心是HTTP報文蜜自,根據報文發(fā)送者的不同,我們將其分為請求報文和響應報文卢佣。其中重荠,由客戶端發(fā)出的HTTP報文稱為請求報文,由服務端發(fā)出的報文稱為響應報文虚茶。

  • 請求報文:請求報文通常由瀏覽器來發(fā)起戈鲁,當我們訪問一個網頁或者請求一個資源的時候都會產生請求報文。請求報文通常由HTTP請求行嘹叫、請求頭婆殿、消息體(可選)三部分組成,服務端在接收到請求報文后根據請求報文請求返回數據給客戶端罩扇,所以我們通常講的服務端開發(fā)實際上是指在服務端接收到信息以后處理的這個階段婆芦。
  • 響應報文:響應報文是指在服務端接收并處理了客戶端的請求信息以后,服務端發(fā)送給客戶端的HTTP報文喂饥,服務端開發(fā)的重要工作就是處理來自客戶端的請求消约,所以這是我們開發(fā)一個HTTP服務器的核心工作。和請求報文類似仰泻,響應報文由HTTP狀態(tài)行荆陆、響應頭、消息體(可選)三部分組成集侯。例如我們通常熟悉的200和404分別表示連接正常和無法訪問資源這兩種響應狀態(tài)被啼。

4、基本請求方法

??HTTP協議的基本請求方法棠枉。常見的方法有GET浓体、POST、HEAD辈讶、DELETE命浴、OPTIONS、TRACE贱除、CONNECT

  • GET:最為常見的一種請示方式生闲。當客戶端從服務器讀取文檔或者通過一個鏈接來訪問頁面的時候,都是采用GET方式來請求的月幌。GET請求的一個顯著標志是其請求參數附加在URL后碍讯,例如”/index.jsp?id=100&option=bind”這種形式即為GET方式請求。GET方式對用戶而言扯躺,傳遞參數過程是透明的捉兴,因為用戶可以通過瀏覽器地址欄直接看到參數蝎困,所以這種方式更適合用來設計API,即在不需要驗證身份或者對安全性要求不高的場合倍啥,需要注意的是GET方式請求對參數長度由一定限制禾乘。
  • POST:POST克服了GET方式對參數長度存在限制的缺點,以鍵-值形式將參數封裝在HTTP請求中虽缕,所以從理論上講它對參數長度沒有限制(實際上會因為瀏覽器和操作系統的限制而大打折扣)始藕,而且對用戶來講參數傳遞過程是不可見的,所以它是一種相對安全的參數傳遞方式彼宠。通常用戶登錄都會采取這種方式鳄虱,我們在編寫爬蟲的時候遇到需要登錄的情況通常都需要使用POST方式進行模擬登錄。

5凭峡、思路

??HTTP是建立在TCP/IP協議上的,所以HTTP的協議應該考慮用TCP/IP協議的實現來實現决记,考慮到Socket是TCP/IP協議的一種實現摧冀,所以我們非常容易地想到應該用Socket來構建一個HTTP服務器。

??c#中的tcp通信實質就是Socket通信系宫。

??所以我們的思路是這樣的索昂,首先我們在服務端創(chuàng)建一個tcp通信來負責監(jiān)聽客戶端連接。每次客戶端發(fā)出請求后扩借,我們根據請問報文來判斷客戶端的請求類型椒惨,然后根據不同的請求類型進行相應的處理,這樣我們就設計了一個基本的HTTP服務器潮罪。

三康谆、程序解析

1、HttpServer類

??程序中我們創(chuàng)建了一個繼承于HttpServer的類嫉到,并實現了handleGETRequest 和handlePOSTRequest 這兩個抽象方法:

2沃暗、監(jiān)聽與接收

??這個WEB服務器由兩個組件構成,一個是負責啟動TcpListener來監(jiān)聽指定端口的HttpServer類何恶,并且用AcceptTcpClient()方法循環(huán)處理TCP連接請求孽锥,這是處理TCP連接的第一步。然后請求到達“已指定“的端口细层,接著就會創(chuàng)建一對新的端口惜辑,用來初始化客戶端到服務器端的TCP連接。這對端口便是TcpClient的session疫赎,這樣就可以保持我們的主端口可以繼續(xù)接收新的連接請求盛撑。從程序的代碼中我們可以看到,每一次監(jiān)聽程序都會創(chuàng)建一個新的TcpClien虚缎,HttpServer類又會創(chuàng)建一個新的HttpProcessor撵彻,然后啟動一個線程來操作钓株。HttpServer類中還包含兩個抽象方法,你必須實現這兩個方法陌僵。

3轴合、解析HTTP

??這樣,一個新的tcp連接就在自己的線程中被HttpProcessor處理了碗短,HttpProcessor的工作就是正確解析HTTP頭受葛,并且控制正確實現的抽象方法。

??HTTP請求由3部分組成偎谁,所以我們只需要用string.Split()方法將它們分割成3部分即可总滩,接下來就是接收和解析來自客戶端的HTTP頭信息,頭信息中的每一行數據是以Key-Value(鍵-值)形式保存巡雨,空行表示HTTP頭信息結束標志闰渔,我們代碼中用readHeaders方法來讀取HTTP頭信息。

4铐望、數據流的處理

到這里冈涧,我們已經了解了如何處理簡單的GET和POST請求,它們分別被分配給正確的handler處理程序正蛙。在本例中督弓,發(fā)送數據的時候有一個棘手的問題需要處理,那就是請求頭信息中包含發(fā)送數據的長度信息content-length乒验,當我們希望子類HttpServer中的handlePOSTRequest方法能夠正確處理數據時愚隧,我們需要將數據長度content-length信息一起放入數據流中,否則發(fā)送端會因為等待永遠不可能到達的數據和阻塞等待锻全。我們用了一種看起來不那么優(yōu)雅但非常有效的方法來處理這種情況狂塘,即將數據發(fā)送給POST處理方法前先把數據讀入到MemoryStream中。這種做法不太理想虱痕,原因如下:如果發(fā)送的數據很大睹耐,甚至是上傳一個文件,那么我們將這些數據緩存在內存就不那么合適甚至是不可能的部翘。理想的方法是限制post的長度硝训,比如我們可以將數據長度限制為10MB。

5新思、返回值與擴展

這個簡易版HTTP服務器另一個簡化的地方就是content-type的返回值窖梁,在HTTP協議中,服務器總是會將數據的MIME-Type發(fā)送給客戶端夹囚,告訴客戶端自己需要接收什么類型的數據纵刘。在writeSuccess()方法中,我們看到荸哟,服務器總是發(fā)送text/html類型假哎,如果你需要加入其他的類型瞬捕,你可以擴展這個方法。

四舵抹、C#中如何在一個類里訪問主窗體中的控件

1肪虎、增加一個靜態(tài)變量

public static Form1 frm1;

2、聲明一個form變量方便后面的調用

public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
frm1 = this;
}

3惧蛹、在類中直接用Form類里的frm1變量去調用就可以了

Form1.frm1.WriteLog("get post data end", 1, Color.Green);

4扇救、修改控件的Modifiers屬性為public

  • 最后如果你發(fā)現不成功的話,請記得將richtext的Modifiers屬性設置為public

五香嗓、參考資料

1迅腔、 PayneQin的博客https://blog.csdn.net/qinyuanpei/article/details/51757148
2、 烏班圖ysm的博客 https://blog.csdn.net/u012278016/article/details/104756543
3靠娱、 小y的博客 https://www.cnblogs.com/tuyile006/p/11857590.html
4沧烈、 百度經驗:https://jingyan.baidu.com/article/425e69e601c781be14fc1640.html
5、 秦元培的github https://github.com/qinyuanpei/HttpServer

六像云、程序下載

1掺出、dalong10的下載https://download.csdn.net/download/dalong10/12850855

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市苫费,隨后出現的幾起案子,更是在濱河造成了極大的恐慌双抽,老刑警劉巖百框,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異牍汹,居然都是意外死亡铐维,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進店門慎菲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來嫁蛇,“玉大人,你說我怎么就攤上這事露该〔桥铮” “怎么了?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵解幼,是天一觀的道長展运。 經常有香客問我蛤售,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任所灸,我火速辦了婚禮,結果婚禮上祭衩,老公的妹妹穿的比我還像新娘。我一直安慰自己壹瘟,他們只是感情好,可當我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布鳄逾。 她就那樣靜靜地躺著稻轨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪严衬。 梳的紋絲不亂的頭發(fā)上澄者,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天,我揣著相機與錄音请琳,去河邊找鬼粱挡。 笑死,一個胖子當著我的面吹牛俄精,可吹牛的內容都是我干的询筏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼竖慧,長吁一口氣:“原來是場噩夢啊……” “哼嫌套!你這毒婦竟也來了?” 一聲冷哼從身側響起圾旨,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤踱讨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后砍的,有當地人在樹林里發(fā)現了一具尸體痹筛,經...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年廓鞠,在試婚紗的時候發(fā)現自己被綠了帚稠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡床佳,死狀恐怖滋早,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情砌们,我是刑警寧澤杆麸,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站怨绣,受9級特大地震影響角溃,放射性物質發(fā)生泄漏。R本人自食惡果不足惜篮撑,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一减细、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧赢笨,春花似錦未蝌、人聲如沸驮吱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽左冬。三九已至,卻和暖如春纸型,著一層夾襖步出監(jiān)牢的瞬間拇砰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工狰腌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留除破,地道東北人。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓琼腔,卻偏偏與公主長得像瑰枫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子丹莲,可洞房花燭夜當晚...
    茶點故事閱讀 44,901評論 2 355