TOC

HTTP协议发展史

    在此之前,我们接触的大部分都是C/S开发,C/S开发就是利用Socket这个统一的编程接口,进行跨平台开发,有C也有S,C为Client,而S就是所谓的Server,与此同时Python还为我们封装了一个很不错的库,即SocketServer,SocketServer结合Mixin就混合出来了Forking和Threading,来解决并发的问题;
    虽然我们开启了多进程和多线程,但是对于C/S编程当中,我们依旧少不了各种I/O,这个时候对服务器的压力是非常大的,而且大多数情况下,为了保证数据的安全性往往都是使用的TCP协议,很少使用UDP协议,但是TCP的连接代价是非常高的,那么对于客户端来讲,用完了连接就将连接断开了,对于服务器来讲,连接的开辟和回收是需要消耗大量的主机资源的,所以TCP编程中就加入了"池"的概念,达到连接复用的效果;
    那么到底是采用阻塞结合多线程模型,还是采用非阻塞的事件驱动模型结合少量的线程,其实这两种效率没有办法说谁高谁低,只不过线程过多如果真是个问题的话,这个时候我们可以考虑事件驱动或者I/O多路复用模型,这两种方式在操作系统层面基本上都已经支持了,比如poll、epoll基本上各大操作系统都进行支持;
    那么在1991年时,一个名为"蒂姆·伯纳斯-李"的人,为了让更多人在软件开发时,更加的便捷,不再需要过多的关注底层Socket的实现,于是开发出了HTTP协议,也就是说在TCP协议之上开发出一个软件,这个软件仅支持某种特定格式的数据,也就是说在TCP连接建立之后,客户端的一方发送一些特定格式的文本数据到这个软件上,那么这个软件收到这个合法格式的数据时,就知道了客户端来此的目的,由此形成了一个基于TCP协议之上的HTTP协议的数据交互;
    那么当TCP连接建立之后,作为客户端发送一些文本形式的数据到这个软件之上,这个软件就会将这个合法格式的数据进行解析,解析完成之后,将客户端请求的数据进行返回,那么客户端如何能够解析这个返回的数据,这又是一个问题,于是,浏览器就诞生了,那么这个浏览器就是我们网页呈现的载体,同时它也是我们与Server之间通讯的客户端,它可以与服务端进行通讯,同时也能渲染服务端发送回来的数据;

HTTP协议

    那么浏览器与服务端进行交互时,它们交互的数据是有一定格式的,这个格式我们也称为一种特定的协议,这些协议,我们统称为HTTP协议,他们是在TCP连接通道建立之后建立的一种特定的消息格式,那么这种特定格式的消息就是服务端和客户端之间的一种约定,那么在计算机术语中,这种约定就称之为协议,只不过这种协议是在TCP之上建立的,所以HTTP协议也是应用层的一种协议;
    那么由此,我们将这种基于HTTP协议开发的软件编程,我们就不再称为C/S编程了,而是B/S编程,B/S编程一定是需要基于浏览器的;
    所以从本质上来讲,B/S模型就是一种特殊的C/S模型,但是其实我们看透了,说到底它们都是C/S编程,就像之前的群聊软件开发,我们会时不时每隔一段时间发送一次心跳,那这个心跳就是C/S之间约定好的东西,其实也就是这群聊软件之间的通讯协议;

B/S两端开发

    B/S开发分为两端,第一个为客户端开发,或前端开发,前端开发主要分为HTML、CSS和JavaScript等,那么基于服务端开发,那就过多了,比如PHP、JSP、DjanGo、Flask等,现在基本是门语言都可以;
    那么对于Python来将,常见的服务端开发有Django、Flask和Tornado,因为Web开发都是基于HTTP协议的,所以Web开发相对来讲都是一些固定的东西,无非就是一来一回,HTTP协议也是一样的,所以不管是是C/S开发还是B/S开发,客户端发送出去的都称之为请求,服务端返回的都称之为响应;
    在Python中Django和Flask是Web开发的重点,尤其是Django,它以插件的方式支持了各种高级特性,那么不管是Django还是Flask,它门都是基于WSGI协议,WSGI协议也是目前掌握的重点之一;

HTTP协议特点

    HTTP协议有三个特点,第一个特点为无状态,当我们从浏览器端发起的第一个请求,当收到响应之后,又从浏览器端发起了第二个请求,得到响应,那么这两个请求之间是没有关联的,服务端根本就不知道,当前收到的请求和之前哪一次的请求有任何关联关系,这就是无状态的;
    第二个特点是有链接,因为HTTP协议是基于TCP协议开发的,所以它是面向连接的,需要建立三次握手,四次断开才能开启一轮HTTP事物;
    第三个特点是短连接,因为HTTP协议是无状态的,所以每完成一次HTTP事物,连接都会断开,这样对于服务端来讲,它都性能消耗是巨大的,因为它要给众多用户同时提供服务,所以当有一百万个客户端时,可能在某一秒钟内,就会产生大几百万次连接的建立和销毁,所以这对服务端的压力非常大,因此,自HTTP 1.1协议版本开始,就支持了keep-alive技术,当一个客户端和服务端建立了连接,完成数据交互之后,这个连接不会直接断开,它会保持一段时间,这一段时间内会一直维持着这个连接的存活,等待同一个客户端再次请求,这样当同一个客户端需要多次向服务端发送HTTP请求时,就可以使用同一个连接进行数据交互,这样就减轻了服务端的压力,同时也提高了处理连接的效率;

URL组成

    URL( Uniform Resource Locator ),即统一资源定位符,这是HTTP协议中的关键特性,客户端既然要资源,那总得知道怎么去找这个资源,所以设计出了URL,其格式如下;
schema://host[:port#]/path/../[;url-params][?query-string][#anchor]
# schema:即协议,常见的协议有,http、https、ftp、file和mailto等;
# host:即主机,可以是服务器的IP,一般情况下都是域名;
# port:即HTTP服务端口,一般情况下都是80;
# path:资源路径,如/index.html或/python/index.html;
# url-params:URL参数;
# query-string 查询字符串,如?key1=value1&key2=value2;
# anchor 锚点
例如:http://www.magedu.com/pathon/index.html?id=5&name=python

HTTP消息

    对于HTTP协议来讲,消息分为两种,第一种为Request,即请求报文,第二种为Response,即响应报文,Request一般情况下,是浏览器向服务端发送的HTTP请求,Response一般情况下是服务端用来回应客户端的Request的请求;
    请求报文由两部分组成,第一部分为Header,即消息头部,第二部分为Body,即消息主体,响应报文一样,他们总的来说,格式一摸一样,一部分为Header,一部分为Body,但是具体的内容就不一样了,因为请求Header和响应Header是完全不一样的,同时请求Body和响应Body也是完全不一样的,请求报文的Body里面可能有FromData,可能有文件上传,当文件非常大的时候,可能需要多次报文,所以当文件上传时,需要用到mutipart;
请求报文
    在请求报文中的Header内的第一行称之为请求行,请求行包括,请求方法,和请求资源路径,以及HTTP协议版本,第二行为请求主机地址,包括地址和端口,一般使用域名来表示,当没有端口时,默认为80端口,第三行为当前发起请求的浏览器版本号,那么剩下的行基本是在做协议的协商,比如支持什么类型的文档,支持什么类型的压缩等等;

常见请求方法
    常见的请求方法主要有三种,第一种为GET,向服务端请求获取URL对应的资源,第二种为POST,向服务端提交数据,第三种为HEAD请求,和GET请求类似,不过它不返回响应报文,一般用来作为服务端健康状态检查时使用,查看服务端是否正常,它有点特殊,HEAD请求发出去之后,服务端不会将请求的正文发送回来,它只会返回一个Response Header回来,也就是说,只有响应Header,没有响应Body;
    其实还有很多种类的请求方法,如PUT、DELETE等各种方法,但是在使用RESTfull之前,其实我们最常使用的就是上面这三个方法,其他的都很少使用;
常见传递信息方式
    当目前为止,通过请求来与HTTP服务端传递消息的方式,主要有三种,第一种为Query String,第二种为POST数据提交,第三种为URL本身传参;
    第一种Query String,即查询字符串,直接将需要传递的消息放在URL上面,我们称之为参数,将参数和URL结合,如http://www.baidu.com/index.html?id=1&name=cce这种形式,当以这种方式去向HTTP服务端请求数据时,服务端拿到这个URL就可以进行解析,将?号后面的数据统一称之为参数,参数以&进行分割,然后将参数一一取出,这样我们就将数据传递到服务端了;
    第二种就是POST,它是以FormData表单的形式向服务端发送消息,它将数据以FormData要求的格式组装起来,然后打包一起发送给服务端,另外,除了FormData我们也可以使用其他的方式,比如我们可以传递一个JSON对象过去,等等;
    第三种,就是在URL中直接包含了,需要传递的信息,比如,http://www.magedu.com/python/student/001,可以很明显的知道是上面意思,查询马哥教育下面python班级学号为001学生的信息,当服务端收到这个URL,可以直接进行解析,拿到客户端想要传递的具体信息,这种方式也是非常常见的;
响应报文
    对于响应报文中的Header内的第一行称之为状态行,分别有HTTP协议版本(目前来讲大多数都是HTTP 1.1协议版本)、状态码、状态码说明,第二行为响应时间,第三行为返回的文档类型和文档编码,之后的行也是说明这个文档的一些信息,比如返回文档是否经过压缩,是否支持短连接,过期时间等各种信息,如下;

状态码
    状态码非常重要,它代表服务端的健康状态、发起请求的数据是否成功返回等等,都会在状态码中反应出来,那么常用的状态码有很多,如下;
1xx 表示临时响应并需要请求者继续执行操作的状态代码;
    100:(继续)请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等待其余部分;
    101:(切换协议)请求者已要求服务器切换协议,服务器已确认并准备切换;
2xx 表示成功处理了请求的状态代码;
    200:(成功)服务器已成功处理了请求。通常,这表示服务器提供了请求的网页;
    201:(已创建)请求成功并且服务器创建了新的资源;
    202:(已接受)服务器已接受请求,但尚未处理;
    203:(非授权信息)服务器已成功处理了请求,但返回的信息可能来自另一来源;
    204:(无内容)服务器成功处理了请求,但没有返回任何内容;
    205:(重置内容)服务器成功处理了请求,但没有返回任何内容;
    206:(部分内容)服务器成功处理了部分GET请求;
3xx 表示要完成请求,需要进一步操作。通常,这些状态代码用来表示网页重定向;
    300:(多种选择)针对请求,服务器可执行多种操作。服务器可根据请求者(user-agent)选择一项操作,或提供操作列表供请求者选择;
    301:(永久移动)请求的网页已永久移动到新位置。服务器返回此响应(对GET或HEAD请求的响应)时,会自动将请求者转到新位置;
    302:(临时移动)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求;
    303:(查看其他位置)请求者应当对不同的位置使用单独的 GET 请求来检索响应时,服务器返回此代码;
    304:(未修改)自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容;
    305:(使用代理)请求者只能使用代理访问请求的网页。如果服务器返回此响应,还表示请求者应使用代理;
    307:(临时重定向)服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求;
4xx:表示请求可能出错,妨碍了服务器的处理;
    400:(错误请求)表示客户端请求的语法错误,服务器无法理解,例如url含有非法字符、json格式有问题;
    401:(未授权)请求要求身份验证。对于需要登录的网页,服务器可能返回此响应;
    402:表示保留,将来使用;
    403:(禁止)表示服务器理解请求客户端的请求,但是拒绝请求;
    404:(未找到)服务器无法根据客户端的请求找到资源(网页);
    405:(方法禁用)禁用请求中指定的方法;
    406:(不接受)无法使用请求的内容特性响应请求的网页;
    407:(需要代理授权)此状态代码与401(未授权)类似,但指定请求者应当授权使用代理;
    408:(请求超时)服务器等候请求时发生超时;
    409:(冲突)服务器在完成请求时发生冲突。服务器必须在响应中包含有关冲突的信息;
    410:(已删除)如果请求的资源已永久删除,服务器就会返回此响应;
    411:(需要有效长度)服务器不接受不含有效内容长度标头字段的请求;
    412:(未满足前提条件)服务器未满足请求者在请求中设置的其中一个前提条件;
    413:(请求实体过大)表示响应实在太大。服务器拒绝处理当前请求,请求超过服务器所能处理和允许的最大值;
    414:(请求的 URI 过长)请求的URI(通常为网址)过长,服务器无法处理;
    415:(不支持的媒体类型)请求的格式不受请求页面的支持;
    416:(请求范围不符合要求)如果页面无法提供请求的范围,则服务器会返回此状态代码;
    417:(未满足期望值)在请求头Expect指定的预期内容无法被服务器满足(力不从心);
    418:表示我是一个茶壶。超文本咖啡馆控制协议,但是并没有被实际的HTTP服务器实现;
    420:表示方法失效;
    422:表示不可处理的实体。请求格式正确,但是由于含有语义错误,无法响应;
5xx:表示服务器在尝试处理请求时发生内部错误。这些错误可能是服务器本身的错误,而不是请求出错;
    500:(服务器内部错误)服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理;
    501:(尚未实施)服务器不具备完成请求的功能。例如,服务器无法识别请求方法时可能会返回此代码;
    502:(错误网关)服务器作为网关或代理,从上游服务器收到无效响应;
    503:(服务不可用)服务器目前无法使用(由于超载或停机维护)。通常,这只是暂时状态;
    504:(网关超时)服务器作为网关或代理,但是没有及时从上游服务器收到请求;
    505:(HTTP版本不受支持)服务器不支持请求中所用的HTTP版本;
常见的 http 响应码:
    200:服务器成功返回网页;
    404:请求的网页不存在;
    503:服务不可用;

Cookie技术

    在Web技术的发展史上,Cookie技术的出现是一次重大的变革。但是,Cookie技术又是一项非常有争议的技术,从它诞生之日起就成了广大网络用户和Web开发人员的一个争论焦点,原因不是Cookie的功能太弱,而是认为Cookie的使用会对网络用户的隐私信息构成危害;
    Cookie技术最先是被Netscape公司引入到Navigator浏览器中。之后,WoridWideWeb协会支持并采纳了Cookie标准,微软也在InternetExpiorer浏览器中使用了Cookie。现在,绝大多数浏览器都支持Cookie,或者至少兼容Cookie技术的使用。目前,几乎所有的网站设计者都使用了Cookie技术。Cookie的广泛使用导致了人们对个人信息安全的担忧。有的网站和机构滥用Cookie,未经访问者的许可就搜集他人的个人资料,达到构建用户数据库、发送广告等营利目的,造成用户隐私信息的泄露;
    按照Netscape官方文档中的定义,Cookie是指在HTTP协议下,服务器或脚本可以维护客户端计算机上信息的一种方式。通俗地说,Cookie是一种能够让网站Web服务器把少量数据储存到客户端的硬盘或内存里,或是从客户端的硬盘里读取数据的一种技术。Cookie文件则是指在浏览某个网站时,由Web服务器的CGI脚本创建的存储在浏览器客户端计算机上的一个小文本文件,其格式为:用户名@网站地址[数字].txt;
WinXP操作系统:C:\Document\sand\Set-tings\用户名\Cookies
Win7以上操作系统:C:\Users\用户名\AppData\Roaming\Microsoft\Windows\Cookies
MACOS:/User/用户名/Library/Cookies
    Cookie的主要功能是实现用户个人信息的记录,它最根本的用途是帮助Web站点保存有关访问者的信息。更概括地说,Cookie是一种保持Web应用程序连续性(即执行状态管理)的方法,因为HTTP协议是一种无状态、无连接的协议,不能在服务器上保持一次会话的连续状态信息。随着www的不断发展,HTTP的无状态性不能满足某些应用的需求,给Web服务器和客户端的操作带来种种不便。在此背景下,提出HTTP的状态管理机制———Cookie机制,它是对HTTP协议的一种补充,以保持服务器和客户端的连续状态;
Cookie的实现
    一个Cookie里面一般情况下会有多个键值对,当HTTP服务端收到浏览器的请求时,HTTP服务端如果检测到这个浏览器是第一次访问自己,会在Response报文的Header部分会新增一个set-cookie字段,它的值是key/value类型的数据,那么当这个请求返回给浏览器端之后,浏览器端会将这个cookie的值持久的存储下来;
    那么对于浏览器的存储也有一定讲究,浏览器会对每个网站在操作系统本地生成一个cookie目录,这个目录是以域名+端口或者IP+端口的形式来命名的,浏览器收到HTTP服务端发回来的Cookie,会将这个Cookie存储到这个目录下面,然后下一次使用这个浏览器再次访问该域(网站)时,会将这个key/value类型的Cookie重新组织,放在Request请求报文的Header里面,一起发往服务端,当服务端收到这个HTTP请求后,就会发现请求头里携带了一个cookie值,从而服务端也就知道了这个客户端就知道了这个浏览器之前访问过自己;

Cookie的安全问题
    我们知道的是,Cookie是由服务端生成的,然后存放在浏览器端,但是HTTP协议是明文传输的,并且Cookie的存储也是明文形式的,虽然Cookie是由服务端生成和发送的,但是这个值是存储在浏览器的,所以它存在很大的安全问题,任何人只要掌控计算机,可以直接获取这个cookie的值,甚至可以在另外一台机器上直接将cookie写入浏览器,然后利用这个盗来的cookie去访问这个HTTP服务端进行客户端伪装;
    此时,服务端依旧会认为这个客户端还是之前那个客户端,因为Cookie一般都是用来识别用户的,为了解决用户重复登陆问题,所以这个时候可能就很危险了,拿到了Cookie就可以直接使用别人的用户来操作这个网站,Cookie的安全隐患非常之大;
    所以对于Cookie来讲,有一条不成文的规定,就是不存放敏感的重要信息,因为Cookie太不安全了;
Cookie内容
    对于服务端设置的Set-Cookie大致有以下几种内容,对于key/value来讲,它是可以有多个的,如下;
语法:Set-Cookie:key=value[...];Expires=Date;Path=PATH;Domain=DOMAIN_NAME;SECURE
    key:服务端自定义k/v值;
    Expires:Cookie过期时间,Cookie可以设置过期时间,过期后将被浏览器自动删除,如果未设置过期时间,Cookie不会持久化,关闭浏览器就会立即删除,这种Cookie称为Session(会话级)级Cookie;
    Path:Cookie允许的路径,在访问什么路径或者子路径下可以携带此Cookie,如果为/,则代表/下面所有的子路径;
    Domain:当前Cookie允许的域,默认情况为本域(当前访问的域,如www.baidu.com),其实我们还可以设置其他域,这种操作主要用于跨域共享Cookie;
    SECURE:安全相关,表示Cookie值必须随着HTTPS加密发送过去,也就是说,网站一旦启用HTTP加密,那么Cookie也会进行加密,很少使用,即使用了,也不能保证Cookie本身是安全的;
    HttpOnly:Cookie一旦设置此标记,就不能被JavaScript访问,只能发送给服务端;

例:set-cookie: 67cf521658861580a; Max-Age=20; Expires=Tue, 26-Jul-2022 18:53:20 GMT; Domain=aliyun.com; Path=/; HttpOnly
Cookie作用域
    Cookie中的domain值,一般情况是和本域相关,比如www.baidu.com,但是我们在domain后面写上一个baidu.com,那么这个cookie在java.baidu.com、python.baidu.com...都可以使用,但是真正能够使用上,还需要看Path值,一般情况Path默认都是/,那么java.baidu.com、python.baidu.com...下面的所有的子路径都可以使用,如果Path的值是/test,那么java.baidu.com、python.baidu.com...所有域下面的/test路径才可以使用;
Cookie的缺陷
    Cookie的主要缺陷有三个,第一个Cookie是明文传输、明文保存,所以我们尽量不要在Cookie保存私有信息,第二个是Cookie有一个长度限制,最大长度不能超过4kB大小限制,只能存储少量的数据,第三个,每次请求都会携带Cookie,如果Cookie值过大,会增加流量的消耗;
    对于HTTP协议的无状态问题,是早期众多开发者头疼的问题,但是后来出现了Cookie就解决了这么一个问题,完美了解决了HTTP无状态的问题,使服务端能够通过Cookie知道当前这次请求和上一次请求是不是同一个浏览器,但是由于Cookie各种安全问题,饱受开发中诟病;
     因为Cookie是由服务端主动颁发给客户端的,只要Cookie没有过期,那么客户端下次请求就会携带这个Cookie,那么这就是关键了,如果此时,有一个心怀不轨的人,看到这个Cookie的内容,利用一个支持HTTP协议的客户端,将这个Cookie内容封装成Request报文,然后向这个HTTP服务端发起请求,那么服务端它依旧会认为发送请求的这个客户端,还是当初颁发Cookie的那个客户端,只要伪装者伪装得像,服务端根本无法判断它是否是一个合法的客户端;
    虽然,发送的IP地址变了,但是对于服务端来讲,它不会将IP地址作为判断根据,因为家用的网络大部分都是拨号上网的,IP地址是随时都有可能会变动的,所以服务端也没办法将IP地址作为唯一的判断依据,这就是Cookie存在的最大的安全问题;

Session技术

    Session是一种保存上下文信息的机制,保存的是对象,它的值是存放在HTTP服务端,它通过sessionId来区分不同的客户端,而SessionID是保存在客户端的,做为客户端与服务器的验证标识,它是一个24位的随机字符串,用户每次提交页面时,浏览器都会把这个SessionID包含在HTTP头中提交给WEB服务器;
    Session技术是需要借助于Cookie来实现的,因为在Session的实现原理中,会给客户端响应一个SessionID,而这个SessionID是以Cookie的形式响应到客户端的,所以可以认为Session的实现是借助了Cookie技术,而Cookie也是一种保存会话状态的机制,只不过它存储在客户端。可以存储在浏览器的内存中,也可以存储在硬盘文件中;
    拿用户登陆为例,当一个用户登陆了www.baidu.com时,一般情况下,www.baidu.com网站的后端服务器会在响应报文里面创建一个会话级别的Cookie(浏览器关了Cookie就销毁了),然后在这个Cookie里面封装一个SessionID,这个SessionID就是一个随机Hash值,然后将这个响应报文回应给客户端;
    那么当客户端收到这个响应报文时,就会将这个会话级别的Cookie存储在内存当中,只要浏览器不关闭,那么这个客户端浏览器再次向服务端发起请求时,就会将Cookie值提取出来,封装到Request报文的Header里面,发送给服务端,当服务端收到这个请求时,就可以从Cookie里面提取这个SessionID的值;
    因为服务端为了解决HTTP无状态的问题,所以服务端会将每个自己颁发的SessionID保存起来,一般是存放于一个内存型数据库中,方便下次校验,只不过这个工作,一般由开发人员自行处理;
    所以,当服务端拿到这个SessionID的值时,会将这个SessionID的值和数据库里面的SessionID进行对比,会发现,这个SessionID是属于某一个用户的SessionID,然后将这个用户的一些信息返回,比如,使其无序再次登陆,这样就将Session和用户进行了绑定;
    那么这样,我们就进步一提升了,安全性,因为该Cookie是一个会话级的Cookie,所以浏览器关闭了Cookie值就失效了,但是依旧存在安全问题,虽然客户端关闭浏览器就会销毁Cookie,但是服务端会一直存储,所以,如果在浏览器关闭之前,如果有恶意者将这个Cookie值记录下来,即使浏览器关闭了,依然可以使用其他的客户端将这个Cookie值封装成一个Request报文,向服务端发起请求,服务端依旧会从数据库查询出这个SessionID对应的用户信息返回给客户端;
    同时,因为SessionID是借助会话级的Cookie来实现的,所以只要用户关闭浏览器重新访问又会创建一个新的SessionID,所以这样会造成大量的无用SessionID,所以,为了解决这种服务端的性能问题,和客户端的安全问题,服务端应该为每个自己颁发的SessionID设置一个过期时间,一般情况下,SessionID都有一个过期时间,这个时间主要由业务的特性来设置,一般情况下是15分钟,或者30分钟;

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注