黑马程序员:HTTP协议--MyWebServer

本文介绍了HTTP协议的基本特点,如无连接、无状态,并详细讲解了URL、HTTP请求报文和响应报文的组成部分。接着,讨论了常见的HTTP方法和状态码。最后,提出了一个简单的MyWebServer实现的步骤,包括接收请求、分析报文和发送响应。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------


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");
        }
    }


---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

MyWebServer是一款专业的轻量级WEB服务器软件。软件具有高性能、易用、小巧、绿色等特点,是用户快速建站及个人HTTP文件服务器的难得工具。软件功能强大,可以实现包HTTP/1.1、断点续传、大文件下载、正则表达式URL重写、虚拟目录、HTTP反向代理等,可通过ISAPI接口、FastCGI接口实现执行服务器脚本等诸多功能。软件界面美观简洁、简单全面、实用方便,无需培训,即可快速上手,轻轻松松完成日常用户快速建站及个人HTTP文件服务器功能,真正做到简单全面实用。是用户实现用户快速建站及个人HTTP文件服务器功能的好帮手。 MyWebServer使用说明 使用FastCGI时,在映射设置中将映射模块设置为启动FastCGI的命令,且命令行中必须包含IP:port格式(如:127.0.0.1:8988)的服务器信息,当不需要WEB服务器启动FastCGI时,命令行中填入IP:port格式的FastCGI服务器信息即可。 如果使用ISAPI接口,指定ISAPI的DLL文件即可。 注(本服务器不集成任何动态脚本支持,要使用请自行安装):asp支持可安装IASP(该软件要求安装java运行环境)通过isapi接口实现;PHP通过isapi和FastCGI接口均可(isapi方式建议使用PHP 5.2,因为5.3以上版不再提供ISAPI支持);asp.net支持可安装mono然后通过FasctCGI接口实现。上述脚本已测试过可以运行。 URL重写命令(使用基于VBScript的正则表达式): ifsve 如果匹配指定的服务器变量则往下执行,否则执行下条规则之后的规则。(支持HTTP_HOST REMOTE_ADDR HTTP_REFERER URL四个服务器变量) rewrite 如果匹配URL 则执行重写后面URL操作,可选命令参数: P 执行反向代理; R 执行重定向; L 最后一条规则; D 禁止URL并返回HTTP状态码 例如:rewrite ^/test.rar /web/test.rar L sethd 修改反向代理时发送的HTTP头值 格式为 sethd=头名称:头值,头名称区分大小写。 小技巧:通过URL重写可让WEB服务器变成一台HTTP代理服务器(代理上网),服务端口就是代理端口,重写规则如下: rewrite ^(http:.*) $1 P MyWebServer v3.6.22  更新内容 修正HTTP代理和HTTPS等若干问题 MyWebServer 截图
MyWebServer v3.1.29更新日志: 增加实时流量查看,完善NT服务功能,增加静默启动(加/s命令行启动时不显示主窗口),优化一些网络参数 MyWebServer是一个高性能、易用、小巧、绿色的轻量级WEB服务器软件,是你快速建站及个人HTTP文件服务器的难得工具。支持HTTP/1.1、断点续传、大文件下载、正则表达式URL重写、虚拟目录、HTTP反向代理等,可通过ISAPI接口、FastCGI接口实现执行服务器脚本(如PHP,asp,asp.net等),性能完全超越IIS等很多主流WEB服务器软件。   MyWebServer使用说明: 使用FastCGI时,在映射设置中将映射模块设置为启动FastCGI的命令,且命令行中必须包含IP:port格式(如:127.0.0.1:8988)的服务器信息,当不需要WEB服务器启动FastCGI时,命令行中填入IP:port格式的FastCGI服务器信息即可。 如果使用ISAPI接口,指定ISAPI的DLL文件即可。   注(本服务器不集成任何动态脚本支持,要使用请自行安装):asp支持可安装IASP(该软件要求安装java运行环境)通过isapi接口实现;PHP通过isapi和FastCGI接口均可(isapi方式建议使用PHP 5.2,因为5.3以上版不再提供ISAPI支持);asp.net支持可安装mono然后通过FasctCGI接口实现。上述脚本已测试过可以运行。   URL重写命令(使用基于VBScript的正则表达式): ifsve  如果匹配指定的服务器变量则往下执行,否则执行下条exitr之后的规则。(目前仅支持HTTP_HOST REMOTE_ADDR HTTP_REFERER三个服务器变量) ifurl  如果匹配请求的URL则执行wrurl重写命令,否则执行下条exitr之后的规则。 wrurl  执行URL重写 exitr  结束url重写,不再往下执行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值