平时我们说的一个网站的网址,其实就是说的URL。
下面我们来分析一个网站的url。
URL是统一资源定位器,即根据URL就可以定位互联网中唯一的一个资源。
在url中像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.,比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义。
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。
例如下面的URL中,搜索字符串c++中的 ‘+’ 号就被转义为了 “%2B” 。并且在URL中出现的汉字也会采用同样的规则进行转义。
下面是一个http请求的内容
首行: [请求方法] + [url] + [版本]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;
POST http://job.xjtu.edu.cn/companyLogin.do HTTP/1.1Host: job.xjtu.edu.cn //首行
Connection: keep-alive//Header -> begin
Content-Length: 36
Cache-Control: max-age=0
Origin: http:/ljob.xjtu.edu.cnUpgrade-Insecure-Requests: 1
Content-Type: application/ x-www-form-urlencoded
User-Agent: Mozilla/5. (windows NT 6.3; Win64; x64) ApplewebKit/537.36 (KHTML,like Gecko) Chrome/61.0.3163.100 Safari/537.36
Accept: text/html,application/xhtm1l+xml,application/xml;q=0.9, image/webp., image/apng,*//*;q=0.8
Referer: http:/ljob.xjtu.edu. cn/companyLogin.doAccept-Encoding:gzip, deflate
Accept-Language: zh-CN,zh;q=0.8
Cookie: JSESSIONID=D628A75845A74D29D991DB47A461E4FC;
Hm_lvt_783e83ce0ee350e23a9d389df580f658=1504963710,1506661798;
Hm_lpvt_783e83ceee350e23a9d389df580f658=1506661802 //Header -> end
username=hgtz2222&password=222222222 //Body
下面为一个http响应的内容
首行: [版本号] + [状态码] + [状态码解释]
Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束。
**Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中. **
HTTP/1.1 200 OK //首行
Server: YxlinkWAF //Header -> begin
Content-Type: text/ html;charset=UTF-8
Content-Language: zh-CN
Transfer-Encoding: chunked
Date: Fri, 29 Sep 2017 05:10:13 GMT //Header -> end
<! DOCTYPE html> //Body -> begin
<html>
<head>
<title>西安交通大学就业网</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta http-equiv="content-Type" content="text/html; charset=utf-8")/><link rel="shortcut icon" href="/renovation/images/icon.ico">
<link href="/renovation/css/main.css" rel="stylesheet" media="screen"/>
link href=" /renovation/css/art_default.css" rel="stylesheet" media="screen"/><link href= "/renovation/css/font-awesome.css" rel="stylesheet" media="screen"/><script type="text/javascript" src=" /renovation/js/jquery1.7.1.min.js">x</script<script type="text/javascript" src="/renovation/js/main.js"x</script><!--main--<link href="/style/warmTipsstyle.css"rel="stylesheet" type="text/css">
</head> //Body -> end
下面我们来实现一个简单的HTTP服务器。
我们先写好makefile文件。
然后我们使用前面封装好的Sock中的函数来创建套接字,并且进行套接字绑定,监听连接等操作来构建一个基于TCP的服务器。
然后实现HttpServer的Start函数来启动服务器,该服务器的实现我们使用多进程版本,即当有请求时,服务器创建一个子进程,这个子进程调用需要服务器执行的函数,然后父进程继续监听或创建其它子进程处理其它请求。因为我们忽略了SIGCHLD,所有不需要使用wait等待子进程的退出。
然后我们创建HttpServer服务器并且进行启动,然后我们让服务器执行的响应函数为HandlerHttpRequest函数,那么当服务器收到请求后就会执行该函数。
然后我们进行测试,可以看到服务器可以正常响应客户端发送的请求。但是我们的客户端向127.0.0.1发送请求,一直是在进行本地通信。如果我们想使用浏览器来向服务器发送请求,那么现在还是不行的。
这是因为我们使用的云服务器的安全组端口没有开放,其它用户就不能通过公网来向我们写的服务器发送请求,下面我们来登录云服务器的账号将端口开放,让其它用户可以通过公网IP来访问我们的服务器。如果是云服务器就去找安全组然后添加规则,如果是轻量应用服务器就去防火墙中添加规则。
下面我们就添加了一个规则,即允许任意一个IP从8080这个端口来的TCP请求或TCP连接。
然后我们再使用浏览器请求我们服务器的公网IP和端口号,我们看到服务器没有向浏览器返回任何数据,但是服务器中接收到了浏览器发送的请求。
下面我们在服务器中构建一个http的响应。
我们使用telnet工具充当客户端向服务器发送请求,可以看到服务器发送了响应报文。
下面我们再使用浏览器向服务器发送请求,我们看到浏览器接收到了服务器发送的http响应报文,并且将html页面显示了出来。我们看到浏览器发送的请求虽然没有指定要访问的资源的路径和要访问的资源名称,但是自动添加了/favicon.ico,这个是自动访问该网站的图标。
上面的请求中我们都没有加要请求的资源的路径和资源名称。如果我们请求服务器中/a/b/c/中的d.html文件。这个根目录如果不处理就会从服务器运行的linux的根目录中搜索文件,如果指定了web根目录,那么就从web根目录搜索文件。下面我们来修改服务器的web根目录。我们先创建一个wwwroot文件夹来当作web根目录,然后在这个目录下创建一个index.html文件来作为网站的首页。
下面我们需要做的就是将浏览器发送过来的http请求结构进行解析,解析出来请求行中包含的要访问的资源的路径和名称。我们实现一个curString函数来根据分隔符分割字符串,并且将分割好的子串都存入vector容器中进行返回。
然后在HttpServer服务器中我们遍历vector容器中的内容,我们看到http请求结构已经被按行进行分割了。
然后我们拿到http请求行,然后将请求行按照空格符来进行分割,这样就能拿到客户端请求资源的路径和名称了。
下面我们根据浏览器发送的资源路径,让服务器打开这个资源文件,然后按行读取这个资源文件中的数据并且都存到content中,最后将这个文件的内容作为响应正文返回给浏览器。
然后我们再实现一个响应主页。
我们测试看到服务器打印出来了客户端请求的资源的路径。但是网站主页没有返回给浏览器,这是因为我们没有设置web根目录的话,默认会从linux的根目录搜索文件。即会从linux根目录中搜索wwwroot文件夹。
下面我们进行拼接,来将服务器从wwwroot目录下搜索客户端要请求的资源。
此时我们看到当输入了正确的路径后,服务器就会将网页发送给浏览器,但是当我们没有输入要访问的资源的路径时,服务器就没有将网站首页返回给浏览器。这样肯定是不可以的。
下面我们再来进行判断,当客户端请求的路径为默认路径 "/"时,我们就将网站首页返回给客户端。
然后我们再添加一个网页,当浏览器的url中请求的是这个网页时,该网页就被服务器返回给了浏览器。
下面为HTTP协议中的方法,其中最常用的就是GET方法和POST方法。
我们平时所说的上网的行为其实就两种,第一种为从服务器端拿下来资源数据,通常使用GET方法。第二种为把客户端的数据提交到服务器,可以使用GET方法,也可以使用POST方法。当将客户端的数据提交给服务器时,在前端页面会有一个表单,这个表单用来收集用户数据,并把用户数据推送给服务器。当使用GET方法将用户数据推送给服务器时,表单中的数据会被转成http request的一部分。
下面我们在首页实现一个表单,该表单将收集用户的用户名和密码,然后使用GET方法提交给服务器的/a/b/haha.html文件。
我们看到当我们输入信息点击登录后,页面会自动跳转到服务器的/a/b/haha.html中。并且在这次请求的url中还显示出来了用户输入的用户名和密码等信息。所以我们就可以知道了GET方法通过url来向服务端传参。
下面我们再来将表单中的提交方法改为POST方法。然后我们将客户端发送的http请求打印出来。
我们看到这一次请求中url中没有出现表单中用户的信息,在服务器打印客户端这次http请求中,我们在请求正文中看到了用户在表单中填写的信息。所以我们知道了POST方法通过http请求的正文提交参数。通过测试我们知道GET方法和POST方法都可以查看到用户在表单输入的信息,这样是很不安全的,因为用户的信息被明文显示了。只有将这些数据进行加密后再通过GET或POST方法进行传输才是安全的。GET方法和POST方法是最常用的两种方法,通常传递图片、视频等资源时使用POST方法传送,GET方法通常用来传送一些不重要并且小的数据,例如百度搜索信息等。
下面为HTTP协议中最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)
HTTP常见状态码与状态码描述
下面我们主要来演示一下3XX的状态码,即重定向状态码。
301 | Moved Permanently | 永久移动,请求的资源已被永久的移动到新URL,返回信息会包括新的URL,浏览器会自动定向到新URL,今后任何新的请求都应使用新的URL代替 |
---|---|---|
302 | Found | 临时移动,与301类似,但资源只是临时被移动,客户端应继续使用原有URL |
307 | Temporary Redirect | 临时重定向,与302类似,使用GET请求重定向 |
下面为临时重定向的原理。例如当服务器将IP地址从110.123.123.123改为110.123.123.456后,客户端还是会根据原来的IP地址110.123.123.123向服务器发送请求,那么此时服务器就可以在对客户端的响应的状态行中返回302状态码,然后在响应报头中添加一个Location,Location中为服务器的新的URL。即表示该服务器的URL已改变,此时客户端的浏览器就会自动向Loaction中存的新的URL发送请求。这就是临时重定向。
下面我们使用代码来进行演示。当客户端访问的文件不存在时,我们就给客户端返回302状态码,并且添加一个Location响应报头,该报头里面存了新的URL地址,那么客户端的浏览器就会自动向这个新URL地址发送请求,即网页会跳转到这个URL地址。
然后我们使用浏览器请求一个服务器中不存在的资源,那么服务器就会发送302状态码给浏览器,并且服务器还会将新的URL通过Location响应报头发送给浏览器,然后浏览器收到302临时重定向状态码,又收到了Location中的新的URL地址,那么就会自动请求新的URL地址,所以我们看到网页自动跳转到Location中的新的URL地址了。
下面我们修改服务器返回给客户端的Location的URL,当客户端访问服务器没有的资源时,服务器返回302状态码给客户端,然后将服务器的404.html网页的URL地址返回给客户端。那么客户端访问服务器不存在的资源时,就会自动跳转到404.html网页。
永久重定向301状态码就是会比临时重定向多一步,即会更新本地缓存。
我们看到永久重定向也是会在客户端请求服务器时,如果请求到了服务器没有的资源,那么服务器就会向客户端发送301状态码,然后通过Location响应报头将新的URL地址发送给客户端,与临时重定向不同的是,浏览器会在本地中将新的URL地址进行缓存,当以后再次访问服务器旧的URL时,会自动向新的URL发送请求。
Content-Type: 数据类型(text/html等)
Content-Length: Body的长度
Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
User-Agent: 声明用户的操作系统和浏览器版本信息;
referer: 当前页面是从哪个页面跳转过来的;
location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;
下面我们在服务器向客户端发送的响应报文中添加一个Content-Length响应报头,该报头中记录了这一次响应正文中的数据长度。
我们看到服务器发送给客户端的响应报文中就包含了记录响应正文长度的Content-Length报头。
下面我们再来看Content-Type这个响应报头,该报头说明了传送的文件的类型。
Content-Type对照表
如果我们在服务器向客户端返回的响应报文中添加Content-Type报头,客户端的浏览器收到后就会按这个类型来解析服务器发送的响应正文。下面我们在服务器的响应报文中添加Content-Type: text/plain,那么客户端的浏览器就不会再将服务器发送的响应正文当作html文件进行解析了,而是直接当作.txt文件进行显示。
下面我们将Content-Type的内容改为text/html,那么客户端的浏览器就会将服务器的响应正文当作html文件来进行解析并显示。
http的特征:
1.简单快速。
2.无连接。
3.无状态。
我们知道http协议的底层使用的是TCP协议,那么为什么说http协议是无连接的呢?这是因为http协议这一层不需要维护连接,所以是无连接的。需要维护连接的是为http提供服务的TCP层,TCP层需要建立连接。http协议是无状态的,但是实际在我们上网时,例如我们登录哔哩哔哩账号,然后我们关闭浏览器,再打开浏览器进入哔哩哔哩官网,我们看到我们的账号还是登录状态,这其实并不和http协议有关,而是cookie记录下了我们的登录状态。
http协议是超文本传输,其实就是类似于一个文件传输协议,只不过传输的文件类型多一些,能传文本、网页、图片、视频、音频等。所以http只需要保证它的网络功能就好,至于网页怎么渲染等问题不是http协议该考虑的事情。所以http是无状态的。
下面我们来看一下cookie文件。
当客户端第一次输入用户名密码登录账号,服务器认证成功后,客户端会保存用户的用户名密码等信息在cookie文件中。然后下一次用户发送请求时,自动携带用户曾经输入过的用户名和密码,这样用户就可以免登录了。cookie文件有内存级的和文件级的,内存级的cookie文件当浏览器关闭后再打开就还需要重新登录。
我们可以在浏览器中查看cookie文件,如果我们将cookie文件中保存的用户的数据进行删除后,那么下一次用户再次登录这个网站时就需要重新输入用户名和密码了。
下面我们再来讨论这种cookie文件保存用户的账号和密码等私密信息的方案的安全性。
当黑客通过木马病毒拷贝用户的cookie文件,然后放到自己的浏览器中,访问同样的网站,就可以免密登录用户的账号了。而且如果cookie文件中明文保存了用户的账号和密码信息,那么黑客就获取了用户的个人信息。所以直接在cookie文件中保存用户的个人信息这样的方案不安全,现在已经不适用了。
现在的cookie文件中不再保存用户的个人信息了,而是保存服务器生成的一个唯一ID,session id。然后用户再次访问这个网站时,在请求中携带这个session id即可,服务器对session id进行认证,然后为用户呈现登录后的内容。
现在的浏览器使用的就是这种新方案,在cookie文件中保存服务器返回的session id,然后用户再次访问网站时服务器只需要验证用户的session id即可。这样的新方案虽然黑客不能通过cookie文件来获取用户的个人信息了,但是黑客还可以拷贝用户的session id来进行免密登录,并且黑客盗取用户的cookie文件来免密登录的情况不能很好的解决。这也就是为什么现在还有qq号等账号被盗取的原因。当然服务器也可以提供一些策略来预防,即可以做IP地址的验证等策略。如果检测到用户IP地址异常,那么服务器就将session id变为失效。
下面我们在代码中演示服务器在响应报文中添加Set-Cookie报头,该报头中包含了服务器发送给浏览器的cookie信息。
我们看到服务器发送给浏览器的cookie信息被浏览器保存到了cookie文件中,然后浏览器再次访问服务器的网站时,也将cookie文件中的信息添加到请求报头中发送给服务器了。
下面我们再来看Connection选项。短连接一次只保存一个http请求,当客户端请求到网页后就断开连接,然后发现网页中有图片,此时又需要建立新的连接向服务器请求图片。而长连接可以一次保存多个http请求。现在的一个完整的网页是由非常多的资源构成的,所以都使用长连接。
Connection: keep-alive -> 长连接
Connection: close -> 短连接