计算机网络(应用层http)

应用层我要发送一条message并不只是一个字符串,而应该是一个结构 化的数据,包含了昵称、头像等等,将一个结构化的数据转化成为一个长报文或者一个长字符串的过程我们称之为序列化。

双方直接发结构化的数据要求双方的平台是一样的(因为不同操作平台对于结构化的数据有不同的要求,比方说内存对其的方法),这时候就需要序列化。序列化和反序列化的目的主要是为了方便网络通信。协议本质就是双方约定好的某种格式的数据,常见的就是结构体或者类来进行表达,常见的就是用结构体或者类来进行表达。结构化的数据方便被上层设置与读取。

为什么不能直接发送结构体对象呢?但是要求接收两端的平台一致,不然会有不同平台内存对齐、结构体成员变量大小不统一的问题,但是这个要求很难满足,所以需要进行序列化。

指令: netstat,常用于检查各种网络相关的统计信息,包括接口统计、路由表、网络连接、监听端口等。这个工具在网络问题诊断和性能分析中非常有用。

指令:telnet 127.0.0.1 8888,可以向服务器发起tcp登录。 


Read和write并不是把数据发送到对端,并没有这么简单,双方在通信的时候,tcp协议会有自己的发送缓冲区也会有自己的接收缓冲区,我们在用户从也要定义自己的缓冲区,比如说定义的string对象,当我们调用write函数的时候,并不是把数据发送到网络,而是把用户层定义的数据拷贝到tcp的发送缓冲区,拷贝完成write函数就返回了。剩下的事情什么时候发如何发由tcp协议决定,这也是tcp叫做传输控制协议的原因,同理read是从tcp的接收缓冲区里面将数据拷贝到用户层。这里的read和write并不是从网络读数据或者将数据发到网络里面,而是名正言顺的拷贝函数,接下来tcp协议会自顶向下层层封装发送到对端,对端再自底向上解封装,以及如何发什么时候发这些都是tcp协议负责的内容,这个过程抽象出来也是一个拷贝过程,即tcp发送缓冲区经过网络拷贝到对方的tcp接收缓冲区,所以网络通信的本质也是拷贝。 上图也可以看出tcp协议是全双工的。  

添加报头跟你是什么样的字符串、什么样的协议是没有关系的 ,而进行再定协议的工作就是重新定义request和response这样的类 。

协议就是双方约定好的一种结构化的字段,这种协议具体应该有哪些字段、每个字段的含义完全由业务逻辑决定,你想解决说明问题,协议定义完成之后要对协议定出序列化和反序列化的方案,方便在网络进行传输。还要保证两端可以对数据进行正确的读取发送,所以就有了报头,根据报头提取有效荷载。

应用层当中几乎都必须做到自定义协议、选择序列化和反序列化方法以及业务的处理逻辑.

HTTP

http可以把文本、音视频等内容(资源)从远端拿到本地浏览器,网络传递的信息无非就是一种资源的拷贝。

域名解析服务:将域名www.baidu.com转换成为ip地址。

下图是一个http的请求

现在的登录都是网页级别的,不会在域名中出现登录信息了。所以登录信息这个字段现在已经基本不用了。server端的port不能随意指定,必须是众所周知的且不能随意更改的,端口号和成熟的应用层协议是一一对应的,协议名称和端口号形成了一种一一对应的强相关,所以端口号绑定这部分工作由浏览器承担了,没有暴露给用户罢了。带层次的文件路径对应我们想要的资源,web根目录和Linux的根目录是两个不同的概念,这里的第一个文件分隔符是web根目录,根目录是在真个linux系统层面上从根目录定位某一个文件,而web根目录是通过web服务器去解释的。?是用来将url进行分割成为前后两部分,其中后面这部分可以理解成为是客户端要交给服务器做处理的数据,而这些参数是属于kv的,&是and符号级联多个kv参数。url是访问网络资源的超链接,超链接一定要直接或者间接的绑定ip地址和port等等,所以url叫做统一资源定位符可以确定全网唯一一份的资源。

因为url本身会采用一些特殊字符‘?’、‘#’等等,所以当我们要实际使用这些字符的时候会被进行相应的转换(比如用ascii码表的数字表示)是为了urlencode用来和url本身用于功能实现的字符加以区分。其中中文也会被url编码。http协议是基于socket在上层做的文本发送、文本接收、文本分析,也就是凭借协议进行字符串处理。协议就是双方约定好的结构化字段,具体结构化字段约定什么、有哪些字段、字段的含义由业务逻辑决定,协议定好之后要对对应的协议字段制定序列化和反序列化的方案。 

http是采用空行将有效载荷和报头进行分离的,http的序列化和反序列化是不依赖第三方库而是自己去实现的,因为http的消息都是按照行陈列的,所以通过\r\n将多行累加在一起有多行字符串变成一个大的字符串从而实现序列化,反序列化就是通过\r\n将每行提取出来。双方进行协议版本的协商是为了让新老客户端使用服务器的不同功能 。http采用按行读取,只要读到空行就认为报头读取完毕,实现报头和有效载荷的分离。序列化和反序列化就是采用\r\n把多行请求或者响应全部累加在一起,由多行字符串变成一个字符串,反序列化的时候按行为单位进行解析。

http的请求

通过浏览器直接给服务器发送http请求,服务器不做任何处理直接打印http请求:

connection字段是表示连接模式是长连接还是短连接。为什么当我们在浏览器收索某个软件下载时,浏览器可以自动匹配对应的平台版本,比如windows、mac版本等等,因为搜索的本质是像服务器发起请求,而请求中会有一个User-Agent字段来表示浏览器的相关信息。

每一个网页可能包含多个资源构成,这就说明我们需要多次向服务器进行http的请求,一张网页如果包含9张图片一个网页,相当于有10个资源。而http底层是 tcp,每一次访问资源的时候都要重新建立连接,建立连接的成本太高导致传输的效率会降低,所以http1.0当中request请求当中有一个选项叫做connection:keep-alive,也就是我们要访问一张网页 ,一张网页有很多的资源,我们基于一个建立好的连接像这个连接塞上10个http请求,对方收到之后会直接把10个请求依次处理,然后再基于同样的连接返回10个响应。这种策略就不用每次访问资源都得再次建立连接,基于一个连接就可以将资源传输完毕,这就叫做长连接。

http的响应

通过telnet命令获得,该请求只包含了请求行和空行GET / HTTP/1.0,报头和有效荷载都没有带。响应内容中有效荷载字段就是百度的默认首页

上面关于响应的荷载段可读性非常的差,因为存在一定的压缩,比如说/r/n这种空行都没有必要还占用字符,可以进行压缩。

//当我们响应如下时:
response = "HTTP/1.0 200 OK" + SEP;
response += SEP;
response += "hello world"

对于http协议,我们可以通过空行的读取到响应或者请求的报头,但是如何确定有效载荷的长度呢?在报头中有一个字段时Content-Length:Body的长度,表示有效载荷的长度。Content-Type:Body的种类,资源的类型。

string body="<html> <body> <h1>this is a test</h1></body></html>";//资源,图片(.png, .jpg...),网页(.html, .htm),视频(),音频(..)->都是文件!都要有自己的后缀!这里是将网页信息也就是响应报文的有效荷载硬编码到我们的c语言当中,并非常规做法,因为会导致我们一旦想要修改资源就需要进行重新编译。建议的做法时直接将网页的内容放到一个文件当中。
response = "HTTP/1.0 500 OK" + SEP;
response += "Content-Length: " + std::to_string(body.size()) + SEP;
response += "ContentType: text/html"+ SEP; 
response += SEP;
response += body;

http的方法

对于GET方法是获取资源,而对于POST方法则是传输实体主体,这些方法都是由浏览器客户端发起的,他会构建一个http request,携带的方法GET/POST,但是浏览器如何知道我们要用什么方法呢?上图也可以看出,这两个方法都可以向服务器提交客户端的参数,只是提交的方式存在不一样。GET方法调教参数的方式并不私密,会回显到浏览器的url上面(并不是不安全),所有的登录注册支付等行为,都要是POST(不是只用)方法,post方法是通过http正文来传参,两者都不去安全。url是http请求行的字符串,一般大小是有限制的,而请求报文的有效荷载部分理论上是可以非常大的。

http状态码

404not found是客户端的问题,因为客户端向服务器请求服务器无法提供的资源,请求也是分正常请求和非正常请求的,服务器的资源是有限的。临时重定向不更改浏览器的任何地址信息,永久重定向会更改浏览器的本地书签,发生冲定向的时候服务器给用户返回3xx状态码+loacation(告诉客户端接下来要去哪里访问),location是搭配3xx状态码使用的。反爬就是对request请求的报头字段做一些分析。

关于http的会话保持的功能

http本身是无状态的,记不住历史放访问了哪个资源,比如说5s之后访问同一个网址http是不记得之前访问过的。但是为什么我们登录网站会员之后关闭浏览器重新打开这个网站我的会员自动登录了?http进行超文本传输,不会直接做这个工作,但是用户需要这个工作,不然难道我每看一部vip电影都要登录一次账号吗,每看一部新的电影相当于向新的资源做一次请求,而vip的资源是无法知道这个请求是我发出来的,需要对我的身份进行验证的,也就是服务器会问你是谁呀,这就导致每换一部vip电影我都要做一次身份认证。所以我们需要一个功能就是用户是否在线要持续的记录下来,这个称为会话保持。http不直接做,解决方案就是cookie&&session,当我们进行认证通过的时候,服务器会通过http的一些选项将我的私人信息比如说用户名密码写入到http相应里面并作为一个response发给我们的客户端浏览器,浏览器将response中响应的cookie信息在本地进行保存(保存方案分为内存级和文件级),在往后我们向该网站发送的request当中都携带cookie属性报头,将保存的历史的cookie信息都携带上,服务器段往后可以通过request当中的这些信息在后台进行自动认证。 

服务器再收到账号和密码之后进行认证,认证成功身份合法之后回形成一个session对象,用当前用户的基本信息填充,并且回形成session id,然后只把session id给服务器,服务器收到给response之后会在本地浏览器的cookie文件中保存该session id,里面会有该id的过期时间。未来服务器在访问该网站的时候都会携带cookie选项,里面回携带session id信息,服务器只要确定该session id是合法的就可以成功认证了。这种做法的好处是首先不用担心用户的私人信息被泄露,因为他是服务器维护的,并通过session id来表示,黑客可能拿到session id,但是无法通过它反推用户信息。 服务器通过ip地址溯源,行为检测等等识别客户端是否是非法用户,如果感觉是非法用户就让session id失效,此时客户端需要进行从新认证,所以黑客拿到了cookie信息里面的session id是没有办法进行身份的重新认证行为的,虽然无法彻底解决cookie被盗窃的问题。cookie + session就是会话保持的功能。

response += "Set-Cookie: sessionid=1234abcd" + SEP;//服务器给客户端浏览器的响应中含有这个Set-Cookie字段,将来浏览器收到之后会在本地保存这个信息,并在将来访问该服务器时,在往该服务器发送request的时候在报头携带一个Cookie字段用来存放该信息。

class Session // redis -> Session
{
public:
    std::string name;
    std::string passwd;
    uint64_t loginTime;
    // 用户的其他信息
    int status;
    int fd;
    int sesisonid;
};
std::unordered_map<int, Session*> sessions;//服务器用来保存每一个session id和session对象

 bool Login(std::string &message)//这里时模拟服务器读取到一个完整的请求报文message,注意这个函数的代码是一个粗糙的,伪代码理解用的。
 {
     // post-> 正文包含账号密码
     std::string name;//通过报文中的正文也就是有效荷载部分取到用户名和密码
     std::string passwd;

     if(check(name, passwd))
     {
         Session* session = new Session(name, passwd...);//用户名和密码正确就生成一个session对象
         int random = rand();//这个随机数就是session id
         sessions.insert(std::pair<int, Session*>(random, session));
     }

     http response;//构建响应
     Set-Cookie: sessionid=random;//在响应当中设置Set-Cookie字段
 }

一个网页可能是由很多类型的资源构成的, 所以我们像网页发送请求的时候会包含许多个请求,而http底层时tcp,所以每一次发起请求的时候都要建立连接,成本太高了。所以http里面有一个选项就是Connection:keep-alive,保持长连接,也就是基于一条建立好的连接,后续请求哪个资源就像这个公共的连接里面塞入一个请求,对方收到之后把我这些请求依次处理并且基于同样的连接返回对应个数的响应,这样不用每次访问资源都要建立连接,基于一条连接就可以将资源传输完毕,这就是长连接。 

  • 22
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值