HTTP協議
HTTP協議是一種Web通訊協議,經過特定的規則來實現服務器跟客戶端的通訊。HTTP協議有這樣幾個特色:javascript
(1)面向無鏈接的,一次只能處理一個請求,HTTP1.0服務器解析完客戶端請求並做出應答後,會關閉鏈接;對於HTTP1.1應答後會等待一個很是短的時間,若是這段時間沒有新請求,就會關閉鏈接。css
(2)HTTP協議是無狀態的,即對處理過的事務沒有記憶能力,它認爲每一次請求都是陌生的獨立的,爲了解決這個問題,Web服務程序引進了cookie機制來維持請求狀態。html
(3)HTTP協議容許傳輸任意類型的數據,對於正在傳輸的數據類型用Content-Type標記。java
URL
url須要提供幾種信息,一是採用的協議,二是鏈接的套接字,三是請求文件的路徑,後面還能夠跟上請求參數web
在瀏覽器中輸入www.csdn.net,瀏覽器會自動幫咱們轉化爲http://www.csdn.net/。數組
採用的協議,這裏已HTTP爲例;鏈接套接字包括ip地址跟端口號,端口號通常都是默認的,不須要咱們輸入,ip地址咱們通常輸入域名,而後經過域名服務器解析;請求的路徑,通常web程序會提供一個默認路徑,若是不輸,就是默認的;路徑後面還能夠跟上GET請求的參數,在請求路徑後加上"?"參數之間用「&」鏈接。通常來講簡單的無安全要求的數據,咱們直接經過GET方法傳遞給服務器,而大量的隱私的數據咱們經過Post方法傳遞給服務器。瀏覽器
HTTP請求報文
請求報文分爲三個部分,請求行、請求頭、請求體,格式都是固定的。如圖1.1所示:安全
請求的方法以下:服務器
(1)GET 請求獲取url標識的資源cookie
(2)POST 請求獲取url標識的資源並像瀏覽器傳遞數據
(3)HEAD 請求服務器對url的響應報頭
(4)PUT 請求服務器存儲資源,並以url做爲其標識
(5)DELETE 請求服務器刪除url標識的資源
(6)TRACE 請求服務器返回發送過去的請求,主要用於診斷和測試
HTTP響應報文
響應報文也分爲三個部分,響應行、響應頭、響應體,如圖1.2所示:
響應行包含了協議版本,狀態碼,和狀態碼的描述。
狀態碼是一個百位數,100段表示請求已接受,繼續處理;200段表示請求成功;300段表示請求須要作進一步處理,好比說跳轉;400段表示請求錯誤,而且是客戶端的緣由;500段表示服務器的緣由致使了請求錯誤。
常見的響應碼以下:
200 : OK
302 : Found 重定向
400 : Bad Request 錯誤請求,發出錯誤的不符合Http協議的請求
401 : Unauthorized 請求未經受權,這個狀態代碼必須和WWW-Authenticate報頭域一塊兒使用
403 : Forbidden 禁止
404 : Not Found 未找到
500 : Internal Server Error 服務器內部錯誤
503 : Service Unavailable 通常是訪問人數過多
經常使用的類型名:
Server 提供web服務器版本信息;Content-Type 響應的內容的類型 ;Content-Length 響應體的字節長度 ;Connetion 鏈接的方式信息 ;Accept-Ranges 接收數據的方式,一般是字節數組。
MyWebServer
複習,模擬一個本身的簡單服務器程序,步驟以下:
一、接收瀏覽器發送的請求(socket)
二、分析請求報文(http),請求的路徑
三、生成響應報文,響應體
四、發送響應內容
//用的winform,首先搭建一個窗體,點擊開始後開始監聽。
- namespace MyWebServer
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- Control.CheckForIllegalCrossThreadCalls = false;
- }
- void ShowMsg(string str)
- {
- txtLog.AppendText(str+"\r\n");
- }
- private void btnStart_Click(object sender, EventArgs e)
- {
- //點擊開始運行,建立一個socket對象並進行監聽
- IPAddress ip;int p;
- if (!IPAddress.TryParse(txtIp.Text, out ip))
- {
- MessageBox.Show("請輸入一個正確的ip地址");
- return;
- }
- if(!int.TryParse(txtPort.Text,out p))
- {
- MessageBox.Show("請輸入一個正確的端口號");
- return ;
- }
- IPEndPoint point = new IPEndPoint(ip, p);
- Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- try
- {
- socket.Bind(point);//綁定地址
- socket.Listen(10);//開始監聽
- ShowMsg("開始運行。。。");
- //建立一個後臺線程接受客戶端的鏈接
- Thread th = new Thread(Listen);
- th.IsBackground = true;
- th.Start(socket);
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message);
- }
- }
- private void Listen(object o)
- {
- Socket socket = o as Socket;
- while (true)
- {
- Socket connSocket = socket.Accept();//通訊用的socket
- ShowMsg(connSocket.RemoteEndPoint + "鏈接成功");
- //建立一個DataConnection類專門接收請求和發送響應
- DataConnection dc = new DataConnection(connSocket, ShowMsg);
- }
- }
- }
- }
其中接收和發送請求交給DataConnection
- namespace MyWebServer
- {
- delegate void DelHandler(string str);//用於傳遞方法ShowMsg
- class DataConnection
- {
- Socket connsocket; DelHandler del; string request;
- public DataConnection(Socket socket,DelHandler del)
- {
- connsocket=socket;
- this.del = del;
- request = RecRequest();
- del(request);//經過委託調用窗體的ShowMsg方法,顯示請求報文
- //分析請求報文,放在Request類中進行
- Request req = new Request(request);
- staticPage(req.Url);
- }
- //處理靜態頁面,根據請求的路徑來判斷
- void staticPage(string url)
- {
- string extension = Path.GetExtension(url).TrimStart('.');//獲取後綴名
- switch (extension)
- {
- case "html":
- case "htm":
- case "css":
- case "js":
- case "jpg":
- case "png":
- case "gif":
- //處理靜態頁面,生產響應有Response類負責
- ProcessStaticPage(url);
- break;
- case"she":
- //處理動態頁面
- break;
- default:
- break;
- }
- }
- //處理靜態頁面
- void ProcessStaticPage(string url)
- {
- //求出絕對路徑
- string path = AppDomain.CurrentDomain.BaseDirectory + url;
- //判斷文件是否存在
- if (File.Exists(path))
- {
- using (FileStream fs=new FileStream(path,FileMode.Open))
- {
- //讀取文件
- byte[]buffer=new byte[fs.Length];
- fs.Read(buffer, 0, buffer.Length);
- //生成響應頭,交給Response類
- Response response = new Response(200, buffer.Length, url);
- connsocket.Send(response.GetResponseHeads());
- connsocket.Send(buffer);
- connsocket.Close();
- del("關閉了鏈接");
- }
- }
- else
- {
- //文件不存在,404頁面
- }
- }
- void ProcessDTPage(string url)
- {
- string fileName = Path.GetFileNameWithoutExtension(url);//通常把文件名定義成類名
- //獲取當前方法所在的命名空間
- string nameSpace = System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Namespace;
- //類的全名稱
- string fullName = nameSpace + "." + fileName;
- IHttpHandler handler = System.Reflection.Assembly.GetExecutingAssembly().CreateInstance(fullName, true) as IHttpHandler;
- if (handler != null)
- {
- //響應體
- byte[] buffer = handler.ProcessRequest();
- //響應頭
- Response res = new Response(200, buffer.Length, url);
- //發送
- connsocket.Send(res.GetResponseHeads());
- connsocket.Send(buffer);
- connsocket.Close();
- del("關閉鏈接");
- }
- }
- //接收請求報文
- string RecRequest()
- {
- byte[] buffer = new byte[1024 * 1024];
- int num=connsocket.Receive(buffer);
- return Encoding.UTF8.GetString(buffer,0,num);
- }
- }
- }
分割請求信息獲得路徑和準備響應頭都交給對應的類作了:
- class Request
- {
- string request; public string Url;
- public Request(string request)
- {
- try
- {
- string[] strs = request.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
- string first = strs[0];
- string[] requstHeads = first.Split( ' ');
- Url = requstHeads[1];//請求行中的路徑
- }
- catch (Exception ex)
- {
- Url = "";
- }
- }
- }
- class Response
- {
- Dictionary<int, string> dic = new Dictionary<int, string>();//存狀態碼根描述
- int stateCode = 200;
- int contentLength;
- string contentType;
- public Response(int stateCode,int contentLength,string url)
- {
- Fill();
- this.stateCode = stateCode;
- this.contentLength = contentLength;
- GetContentType(url);
- }
- //生成響應頭
- public byte[] GetResponseHeads()
- {
- StringBuilder sb = new StringBuilder();
- sb.AppendLine("HTTP/1.1 "+stateCode+" "+dic[stateCode]);
- sb.AppendLine("Content-Length: " + contentLength);
- sb.AppendLine("Content-Type: "+ contentType +";charset=utf-8\r\n");
- return Encoding.UTF8.GetBytes(sb.ToString());
- }
- //根據後綴給contenttype賦值
- void GetContentType(string url)
- {
- //.htm
- string ext = Path.GetExtension(url);
- switch (ext)
- {
- case ".htm":
- case ".html":
- contentType = "text/html";
- break;
- case ".css":
- contentType = "text/css";
- break;
- case ".js":
- contentType = "text/javascript";
- break;
- case ".jpg":
- contentType = "image/jpeg";
- break;
- default:
- contentType = "text/html";
- break;
- }
- }
- void Fill()
- {
- dic.Add(200, "OK");
- dic.Add(400, "Not Found");
- }
- }