)
1.HTTP协议下面进入HTTP协议部分先来谈谈简单的预备知识浏览器中输入的东西我们一般把它称为域名。根据我们目前学到的知识客户端想访问服务端在技术上只需要知道IP和端口号就可以访问服务。实际上日常生活中我们并不使用IP地址而是直接使用一个叫域名的东西但本质访问时还是需要IP地址只不过会把域名通过域名解析服务转为IP地址。怎么证明可以ping一下ping是用来检测网络连通性的可以看到域名有对应的IP地址。直接用IP也是可以在搜索框访问到的但好像没看到使用端口号。我们把输的IP贴过来看到默认带了HTTP协议有时是HTTPS。一般像HTTP或HTTPS这种知名的服务端口号在服务端一般要固定下来HTTP所绑定端口默认是80HTTPS绑定的端口是443。我们之前写网络套接字时说过服务器必须得绑定一个端口号客户端也要绑定但客户端不需要由程序员自己指明端口由系统随机绑定。所以客户端端口是多少不重要能在我这个系统里区分客户端的唯一性就行。服务器都要把客户端口号指明出来这个端口号一旦有了一般不能轻易修改一旦改了所有客户端就找不到了。一般服务器上的端口号是众所周知的端口号像这种常见协议浏览器自己就会知道如同119每个人知道是火警这样HTTP浏览器知道是80。因此只输入IP浏览器会默认添加端口也可以IP:端口试一下照样可访问。我们平时可能网上看了个文章特别好然后会复制文章链接分享给同学一般把分享的这个字符叫url统一资源定位符也就是说互联网中拿着这个字符串在浏览器中访问就可以找到分享的文章。我们用域名确定互联网中唯一一台主机协议默认端口号可表示该主机上唯一的一个服务后面的资源路径表示我可以找到相应的资源。所以在公网服务器上每个资源都有唯一的字符串路径这个资源路径是唯一的。因此所有网落上的资源都可以用唯一的一个字符串标识并且可以获取到。图片上面是一个完整URL的样子登录信息一般已经很少见了端口号因协议已指明所以可省略第一个/一般是web根目录。平时我们网络行为一般是两种1.把别人的东西拿下来。2.把自己的东西传上去。如果我们要把自己的东西传上去URL中就可以把自己的信息携带上存在如?后面有个uid1。uid1是个KV代表这个请求的一些动态数据。最后总结一下1.我们网络中访问某些资源的时候通过URL来定位某一个资源。2.网络中所有通过浏览器访问到的东西都称为资源。我们通信时URL里有很多特殊字符如/等这些都是作为功能性的符号来用的。那我要提交一个特殊符号是否会混淆有问题呢发现aaaaa和bbbb之间剩下的符号变了一个样子所以少量的情况提交或者获取的数据本身可能包含和URL中特殊字符冲突的字符要求BS(浏和服)双方进行编码(encode)和解码(decode)这样就没影响了(转移有相应的工具)。我在浏览器上输域名回车拿到了网页这个工作相当于向目标服务器发了一次HTTP请求。下面把HTTP请求和响应格式化出来再用两个工具见一见http请求/响应的样子。下面谈一下HTTP的 request格式它是以行为方式来陈列请求或响应格式。HTTP请求是由多行内容构成第一行叫请求行第二部分叫请求报头实际上由多行构成第三部分叫请求正文它不一定以行为单位这个是用户要上传的内容可以没有。这上面一行分割符都是行分割符可以是\n或\r\n每一行内容都是以\r\n结尾的。说个题外话\r\n也是字符画图时一行行画的实际上TCP的接收/发送缓冲区里是一个大的字符串只不过中间有特殊字符这就是存的时候还是按字节一个个存的打的时候是多行。下面说说请求行请求行用行分割符和请求报头做区分所以请求行里面不能再出现回车换行符。请求行所有字段一共分三部分第一部分是请求方法请求方法很多但HTTP场景里95%请求方法只有两种1.GET方法 2.POST方法。一个是获取方法一个是上传方法以空格做分割符。第二部分是URL以空格做分割符。第三部分是请求的协议版本最常见1.0、1.1、2.0第二大部分请求报头是由多行内容构成的每一行都叫做HTTP请求的请求属性大部分是key:Val键值对。key表示是什么属性Value表示属性的值是什么如果包含了请求正文会存在一个重大的问题怎么去区分整体大报头部分(包括请求行和请求报头)和正文部分所以http协议做一个规定在报头和正文之间加个空行这是HTTP请求的第四部分我们一般进行HTTP请求或报头读取时我们都一直按行读取读到空行后前半部分是HTTP整体报头后半部分是内容第一行是请求行。曾经我们写代码在读取网络请求的时候怎么保证自己读到的是一个完整的网络请求呢我们通过calbackdecode不断分析我们自定义协议可以区分清楚。那HTTP协议怎么保证自己读到的是一个完整的报文呢保证不了但可以保证读到一个完整的HTTP的报头因为读到空行就行。那怎么保证把正文读完呢报头里有个属性是Cont-Length:xxx表明正文部分的长度这样根据属性可读完正文这样上述一配合就可以保证读到一个完整的HTTP请求。下面来谈谈HTTP的response它和request的格式几乎一模一样。第一行称为状态行结尾字符\r\n再下一部分内容也是大量KV式内容第三部分是响应正文一般里面是html/CSS/JS/图片等各种资源同样的当我作为一个客户端收到了response怎么保证把整个response全部读完呢保证不了但响应这也包含空行这样空行前都可以读完正文依照读取来读。可以保证读取下一步就是序列化反序列化然后一来一回完成一次HTTP请求和响应。下面先看看响应先来介绍一个小工具抓一个responsetelnet我们用80登陆说明连接成功连的ip也显示了。ctrl]出现talnet提示符要抓到百度首页需要构建一个最简单的请求请求报头和正文都可以没有空行和请求行必须得有。我们弄个简单的用GET方法/根目录一般是获取首页资源HTTP/1.1把版本也要发过去。第一个回车没反应这只是给请求行加了\r\n没报头就在空行此时相当于拿到了请求然后我们得到了响应。这是response报头第一行是响应的状态行空行前剩下的内容是HTTP的响应报头都是KV的。我们看到的状态行也是分为三部分用空格做为分隔符第一个字段是HTTP服务端的版本第二个字段是状态码第三个字段是状态码描述服务端的http version常见的也是1.01.12.0那客户端的和服务端要通告一下版本是什么意思呢这样双方能够协商功能。状态码常见404200类似于当时我们定义网络版本计算机时定义个code表明计算状态。码描述是对状态码字符中版本的描述方便让用户知道什么原因失败。所以HTTP里面哪怕请求的资源是失败的也要有响应所以请求必有响应。下面看看请求我们要基于http做一次抓包这里介绍个工具叫fiddler它是一个可帮我们抓包的软件下载启动以后可通过它发起请求抓到对应的报文。比如访问百度上面和下面对应请求和响应。Raw可看到最原始的(空格下加密了HTTPS协议我们不管)以前是我电脑上的浏览器直接去访问服务器现在启动了一个fiddler浏览器上发的所有请求是先交给fiddler然后fiddler把请求包装一下发给服务器。服务器把响应返回给fiddler再返回给我们 fiddler相当于一种代理所以可以抓到报文。还有个工具是模拟浏览器行为构建请求的postman比如发个请求send后可抓到响应2.编写下面写一个最简单的httpserver用浏览器直接测试(前提要有网络功能)创建好后启动。再然后至此得到了链接接下来想办法处理。我们用多线程处理man 2 recv它可用来tcp读取前三个和read一样最后一个参数是读取方式我们设为0作用和read一样相当于我们收到它后把它读一次打到显示器上关了就行至此完成了简单的服务器。下面测试一下改一下可用浏览器IP:端口访问一下可看到收到的请求。再改一下读成功后返回一个响应(目前不能保证读到完整请求)构建一个简单的响应然后把手写的响应返回去但不一定全能发过去这忽略网络细节。这用man send返回值及前三个参数含义和write一样选项设为0现在就把消息返回去了(可以用手机访问尝试)。测试一下telnet请求的时候服务器给的响应是这样子浏览器上显示的是正文部分上面的内容由浏览器做解释了。HTTP响应正文里通常是图片、网页等我们这写了个字符串我们其实可在正文部分写个简单的网页。网页不会怎么办搜索W3Cschool html它里面有html一些相关的教程简单说说一般一个网页包含!DOCTYPE html表明网页是一个新的标准h5。网页最开始有自己的标签html及最后的/html说明这是个完整的DOCTYPE文档。head和/head表明是网页的头部信息body和/body携带网页的主体。还有一些具有特性的标签如h2/h2作为大标题、一级、二级标签p/p表明内容是一些文档。下面试一下现在发了这样的信息服务器在收到请求时浏览器访问时想找到特定路径下的一些文件所以url可以是图中样子的。那这里的\我们说过是web根目录不一定是 Linux 根目录这个该怎么理解还有网页难道每次要静态编码写到服务器里吗应该是独立的文件需要访问哪个文件通过HTTP访问就行了。网页文件可不止一个各种各样文件存在的话一定会有一个目录结构这个目录结构可以整体被外部去访问。综上下面开始做第一个事情一个http协议一定要有自己根目录可以是Linux根目录也可以由自己去指定定一个目录这个目录就是当前工作目录。在当前目录下新建个文件夹叫wwwroot将来把所有资源都放在这个目录里面从这个目录开始让别人去访问我们把这个目录叫做web根目录。当前有了一个网页下面把网页动态让服务器响应给浏览器服务启动后又往网页加一些东西此时一刷新网页就更新了不用关服务器。现在随便输个路径返回的是同一个网页文件但将来我们可能要不同的文件。现在要客户端说明去哪里所以我们要分析一下请求先把整个请求的第一行读到按空格分隔符把URL提出来拿到对应的目录拼上web根目录去读取若不存在返回404假设我们读到一个完整的HTTP请求接下来把对应字符串打散按行放到req_header把正文放test里这样更容易获取里面的字段。接下来进行完善这样反序列化工作完成。为了方便再做一下Debug(为了方便暂时注释响应)下面测试一下看到了提取内容。我们要的是urlHttpRequest里再填一些变量下面介绍个分割字符串的另一个方法stringstream这样就分开了。读到后反序列化然后提取下面继续测试看到成功打散。我们只拿IP和端口访问时默认URL是根目录。如果只请求根目录总不能把根目录下的所有内容返回一般只返回整个网站的首页这个首页就叫index.html。因此改一下代码解析完判断一下接下来把对应网页文件打开然后把要访问资源读取然后构建响应后send回来下面测试一下现在就可以访问很多资源了通过指定路径可访问wwwroot的任意合法资源了。可我们访问网页的并没有一个个输入路径来访问都是上一页下一页从一个连接跳到另一个并没有改过url。 有个a标签buffer后可以跟上要连的url这样可以跳转了。3.研究http细节下面开始研究HTTP的细节字段先看HTTP的方法目前我们想获取资源请求大多是GET方法。GET方法作用是获取资源就是把远端的资源获取下来基于HTTP协议把别人的网页获取下来。因为浏览器支持HTTP协议所以用的大部分方法都是GET方法。还有种方法是POST也可用来获取网页但更多是要上传我们的东西(剩下方法了解就行见得少)。如果服务器启动之后通过浏览器向服务器提交数据就使用POST方法那我们平时是如何向服务器提交数据的呢(比如打开面板有个搜索框)数据都是通过表单提交的开发工具前端网页里有个叫from的东西是from表单input表示输入框button对应按钮还有输密码的输入框。我们想向我们服务器提交参数也需要from表单(网页注释!--注释--)下面完成一下from表单里有个action后面跟个路径表示想把表单提交给服务器中哪个资源当中。 method表示这个表单想以什么方法提交。input是输入框type表示输入框所对应的类型这是文本类型name...转化为这样value是缺省值可省掉下面测一下我们用的提交方法是get方法所以如果我们要提交参数给我们的服务器我们使用get方法的时候我们提交的参数是通过url提交的看到提交的内容拼到了url上。作为服务器来使用?作为分隔符左边是访问的资源右边是参数参数交给资源。下面把提交方法由get改为post启动服务访问后看到表单提交方式是post输入后提交看到了我们提交的HTTP它的请求方法会在提交表单时由浏览器自动帮我们构建HTTP请求请求方法是post提交路径是/a/b/hello.html这个资源路径参数放在空行之后。看一下抓的包(raw)所以post方法也支持参数提交采用请求的正文提交参数更私密一些(url上不显示)。get方法通过url进行提参参数数量受限不私密(url上可以看到)。其实都谈不上安全想安全得加密。下面说说HTTP的状态码一般HTTP在请求的时候会得到响应响应会包含状态码在状态行的第2列是个数字常见的 1~5数字开头的状态码。下面把代码改一下模拟返回 404一定是经过序列化再经过parse提上来资源路径后本身并不存在下面再次测试这也是一个响应。再说一下3开头的有2种重定向方式分别是永久重定向和临时重定向。我们先看一下重定向的效果我们使用一下302但还要了解一下HTTP报头属性中的Location属性看图感受一下现在向目标服务器发起了请求服务器可能是老版本服务器不想再给浏览器提供服务了此时返回了个响应。这个响应报头状态码写的3xx 响应报头中location: 后跟新的地址。相当于浏览器向别人发起请求它因某种原因不能给我们提供服务它告诉我应该去哪里访问资源。此时浏览器发起2次请求重新构建HTTP请求去访问新的地址。所以重定向功能是让服务器指导浏览器让浏览器访问新的地址。下面改一下代码体会访问时就跳到qq了。我们正常向别人发起请求响应时Location指明了新的地址4.谈谈http请求的属性下面谈http请求的属性Content-Length表明正文部分长度一般在请求和响应里都会携带。Host代表请求的目标主机是谁。User-Agent用来识别客户端的软件信息。refer表示上一次从哪个页面来的这样可配合前进一个页面。Connection:keep-alive下面解释一下我们代码是处理完请求后就把链接直接关了意味着HTTP请求是请求了一次。我们平时见的网页有无数的图片构成一个巨大的页面是会包含非常多的元素的每一个元素就是一个资源。比如网页上包含了100张图片我们要把整个网页让用户看到可能要发起101次请求。第一次是把网页本身请求到剩下的是浏览器基于每张图片都要发起HTTP请求。 HTTP协议底层是基于TCP的TCP是面向链接的每次发起HTTP请求交互 request和response前提条件是浏览器发起套接字基于TCP的connect连接建立请求。按照我们写的想要获取完整要建立101次链接每次建立好链接资源返回去链接就断开了这样方案非常低效。基于一次请求响应一个资源关闭链接我们称为短链接。后来HTTP协议的设计者就想TCP就是双向建立好的通信信道缓冲区在操作系统内以前是建好通信信道后基于连接请求一次再响应一次再把链接关了下次再访问重复这个动作现在变成在发送端同时构建好多HTTP请求然后把这么多请求按串行化全部都发给服务器然后一个个处理不断给客户端给响应这里只建立一个TCP链接可发送和返回多个HTTP的request和response。本轮请求全部完毕该链接才断开这种我们称为长链接。 HTTP/1.0支持的是短链接HTTP/1.1支持的是长链接。通信双方在建立HTTP请求时双方要协商HTTP版本看双方是否支持长链接技术都支持也可不用。Connect选项表明的是双方通信时是否选择长连接客户端和服务器Connection都填上keep-alive才会采用长链接的方案来通信否则短链接。下面让我们网页把图片也显示出来一旦请求首页我们应该看到向服务器发起了很多次请求现在让我们首页也包含很多图片现在有两个图片文件所有图片资源必须全在web根目录及Linux服务器下。现在服务器上要把图片文件拿上来想在首页支持图片首页里要包含h5标签把图片包进来。图片也是文件想看图片先获取网页根据网页标签语言继续发起请求拿图片。下面改代码(img表示图片若挂了显示alt文字根据src向我们的服务器浏览器自动发起二次请求)注释重定向到qq下面测试一下有时发现请求完了但会发现什么都没显示。因为HTTP在这要发起多次请求刚开始请求的是首页后来在标签语言驱使下由浏览器发起二次请求。请求image/1.jpg时要携带对应的正文但图片是二进制的得告诉浏览器图片是什么格式的什么类型的图片这样浏览器才好进行显示。所以我们这里需要有一个报头叫Content-Type这是Content-Type对照表。我们请求的资源非常多怎么知道请求什么类型的文件我们请求的资源在file-path中可根据文件的后缀来。所以我们这样现在读取请求时已知道资源后缀是什么还维护了一个简单的映射表。继续完善下面测试一下成功拿到了Content-Type。但是浏览器访问依旧没有显示图片因为按字符串方式读二进制可能把图片读漏了所以再改一下预先获取一大部分的空间强转一下把内容放到空间中拼接到响应后返回现在访问就可以了。还可设一下大小再来试一下请求的是首页它包含的资源也会自动请求还有个Cookie我们比如访问B站时请求第一个网页要登录请求第二张网页时怎么知道我们曾经登录过呢 HTTP协议默认是无状态的。下面说说HTTP对登录用户的会话保持功能第一次访问要登录输入账号和密码认证完后进行响应。响应中报头里会有Set-Cookie选项里面有用户名和密码。浏览器收到响应后会在内部把cookie内容保存起来把用来保存的文件一般称为cookie文件。从此浏览器继续访问同一个网站时每一次请求部分携带cookie文件中的内容用户名和密码。所以每请求任何一个b站网页都可做协商身份认证因此不用再输入了。浏览器有两种保存cookie文件的方法1.文件级。2.内存级。若是内存级进程内是可new空间的此时不关浏览器可一直访问关了再打开又要登录。若是文件级这样即便关机后再访问也不用登录。下面我们做个cookie下面测试一下看到cookie里有了内容再次刷新cookie也是一样内容。下面继续实验证明Set-Cookie这个选项允许服务器向浏览器中进行数据写入的(这是请求中的分隔符)测试一下看到cookie确实写到了浏览器中往后请求都会带上cookie选项交给服务器。想写多个cookie向浏览器就用多个cookie选项这样多个cookie就被写入了。了解上述说个题外话比如电脑不小心被黑客种了病毒在电脑中把 cookie文件偷走了这样两方比如都使用HTTP协议访问同一个服务就都可登录这样下来我就是你因为你访问的资源我也可访问。因此涉及两个问题1.cookie被盗取的问题。2.个人私有信息泄露。先谈谈第二的解决方案首次访问输入账号和密码b站进行认证认证成功后在服务器端为我们创建一个session文件session里保存的是用户合法信息与登录相关内容。服务器器形成session文件的同时还要给它生成一个全服务器内唯一的一个序号sessionID 然后进行响应时调用set-Cookie把当前用户的session ID写回到浏览器的cookie文件。浏览器再次发起请求携带cookie字段里是session ID 只不过session ID在整个站内唯一的认证ID来比对不存在就跳到登录这样私人信息就不泄露了。再说第一个问题Session ID是服务器统一管理分配的检测到不对会删了session信息重新登录。