HTTP协议--MyWebServer
HTTP协议
HTTP协议是一种Web通信协议,通过特定的规则来实现服务器跟客户端的通信。HTTP协议有这样几个特点:
(1)面向无连接的,一次只能处理一个请求,HTTP1.0服务器解析完客户端请求并作出应答后,会关闭连接;对于HTTP1.1应答后会等待一个非常短的时间,如果这段时间没有新请求,就会关闭连接。
(2)HTTP协议是无状态的,即对处理过的事务没有记忆能力,它认为每一次请求都是陌生的独立的,为了解决这个问题,Web服务程序引进了cookie机制来维持请求状态。
(3)HTTP协议允许传输任意类型的数据,对于正在传输的数据类型用Content-Type标记。
URL
url需要提供几种信息,一是采用的协议,二是连接的套接字,三是请求文件的路径,后面还可以跟上请求参数
在浏览器中输入www.csdn.net,浏览器会自动帮我们转化为http://www.csdn.net/。
采用的协议,这里已HTTP为例;连接套接字包括ip地址跟端口号,端口号一般都是默认的,不需要我们输入,ip地址我们一般输入域名,然后通过域名服务器解析;请求的路径,一般web程序会提供一个默认路径,如果不输,就是默认的;路径后面还可以跟上GET请求的参数,在请求路径后加上"?"参数之间用“&”连接。一般来说简单的无安全要求的数据,我们直接通过GET方法传递给服务器,而大量的隐私的数据我们通过Post方法传递给服务器。
HTTP请求报文
请求报文分为三个部分,请求行、请求头、请求体,格式都是固定的。如图1.1所示:
请求的方法如下:
(1)GET 请求获取url标识的资源
(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
复习,模拟一个自己的简单服务器程序,步骤如下:
1、接收浏览器发送的请求(socket)
2、分析请求报文(http),请求的路径
3、生成响应报文,响应体
4、发送响应内容
//用的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");
- }
- }