应用层

本文主要总结关于网络通信中应用层的概念,功能以及HTTP协议的初识;实现简单的HTTP服务器;
一.什么是应用层???
负责应用程序之间的数据沟通(通常由程序员写的关于解决一个个实际问题,满足我们日常需求的网络程序, 都是在应用层)

我么知道协议是一种 “约定”; socket api的接口, 在读写数据时, 都是按 “字符串” 的方式来发送接收的;如果我们要传输一些 “结构化的数据” 怎么办?

二.自定制协议:结构化的数据传输;
例如, 我们需要实现一个服务器版的加法器,我们需要客户端把要计算的两个加数发过去,然后由服务器进行计算,最后再把结果返回给客户端;

约定方案一:客户端发送一个形如"1+1"的字符串; 这个字符串中有两个操作数, 都是整形; 两个数字之间会有一个字符是运算符, 运算符只能是 + ; 数字和运算符之间没有空格; …

约定方案二:定义结构体来表示我们需要交互的信息;发送数据时将这个结构体按照一个规则转换成字符串;接收到数据的时候再按照相同的规则把字符串转化回结构体;这个过程叫做 “序列化” 和 "反序列;

无论我们采用方案一, 还是方案二, 还是其他的方案;只要保证一端发送时构造的数据,在另一端能够正确的进行解析,就是ok的。这种约定, 就是应用层协议;

序列化和反序列化的概念:
(1)序列化:对数据对象进行转换成为传输或持久化存储时的数据格式;

(2)反序列化:按照序列化的格式进行一个解析的过程;

三.应用层协议:----------HTTP协议
在应用层上,虽然很多的协议都是程序员自己定的,但在应用层中应用最广泛的协议就是HTTP协议;
1.URL的概念及功能:
URL:统一资源定位符(我们平常所说的网址)是互联网上标准的资源的地址(Address);互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。

在生活中我们上网的时候登录的就是网址,通过网址登录才能打开我们需要的网页进行查找;那网址到底是由哪些部分组成???各个部分的功能到底是干什么的???在这里插入图片描述
以上是一个网址的一般结构:
第一个表示协议方案名:共两种http://和https://;第一种为一般的形式,第二种是进行加密后的协议方案名;
第二个表示登录信息(认证):指定用户名和密码作为从服务器获取资源时的必要登录信息,可选填;
第三个表示服务器地址:使用绝对URI必须指定待访问的服务器地址;
第四个表示服务器端口号:指定服务器连接的端口号,可选填;
第五个表示带层次的文件路径:指定服务器上的文件路径来定位特指的资源;
第六个表示查询字符串:针对已指定的文件路径内的资源;
第七个表示片段标识符:使用片段标识符通常可标记处已获取资源中的子资源,可选填;

2.URL编码和解码:urlencode 和 urldecode
URL编码:将特殊字符转换为16进制字符串,并且在前面使用%进行标记,表明两个字符是已经经过编码的;
URL解码:当查询字符串中出现%则认为后序两个字符是需要URL解码的;

为什么要进行URL编码???
编码主要针对的是查询字符串,因为URL中特殊字符串有特殊含义;当向服务端提交的数据中出现了特殊字符就会造成URL的二义性,因此查询字符串中不允许出现特殊字符;当检测到特殊字符时将特殊字符转换为16进制字符串数据在编码的字符串前加上%表示这个数据是经过编码的,使用时需要进行解码;

编码规则:将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY 格式;
在这里插入图片描述
由上图可得:"+" 被转义成了 “%2B” ;

3.HTTP协议:
HTTP协议是作用于客户端和服务端的通信,而客户端和服务端是一个相对的角色,但是必须要通过请求和响应的交换来达成通信,而且HTTP协议规定,客户端先发出请求建立通信,服务器在没有接收到请求时是不会发送响应的。
(1)HTTP的协议格式:
HTTP协议的总体格式分为三大部分:首行,头部,正文;
以下是访问网址后进行抓包,通过此包进行解析:
在这里插入图片描述
HTTP由请求和响应两部分组成;

HTTP请求:
在这里插入图片描述
由上图可知:
(1)请求首行:以空格符间隔分为三大部分;
请求方法:POST请求;向服务端提交一些数据;
获取方法:GET方法,从服务器端获取某些数据资源;
HTTP协议版本:该版本是1.1版本;
(2)头部信息:请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束 ;
(3)正文信息:空行后面的内容都是正文,正文允许为空字符串;如果正文存在, 则在头部中会有一个 Content-Length属性来标识正文的长度;

HTTP响应:
在这里插入图片描述
由上图可知:
(1)响应首行:服务端接收到信息之后要进行回复;
HTTP的版本信息;
状态码:告诉客户端这次的请求是怎么处理的;
状态码的描述信息;
(2)头部信息:请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束 ;
(3)正文信息:空行后面的内容都是正文,正文允许为空字符串;如果正文存在, 则在头部中会有一个 Content-Length属性来标识正文的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在正文中;

(2)关于HTTP的请求方法种类:

方法说 明支持的HTTP版本协议
GET获取资源1.0,1.1
POST传输实体主体1.0,1.1
PUT传输文件1.0,1.1
HEAD获得报文首部1.0,1.1
DELETE删除文件1.0,1.1
OPTIONS询问支持的方法1.1
TRACE追踪路径1.1
CONNECT要求用用隧道协议连接代理1.1
LINK建立和资源之间的联系1.0
UNLINE断开连接关系1.0

其中最常用的是GTE和POST方法;

GET请求方法:此种请求能够被缓存(即用户点击刷新后对页面没有影响),请求会保存在浏览器的浏览记录中;这种方式请求的URL能够保存为浏览器书签请求,但有长度限制;主要用以获取数据,只允许ASCII码类型数据;

POST请求方法:请求不能被缓存下来(用户刷新后需要重新提交),请求不会保存在浏览器浏览记录中,请求的URL无法保存为浏览器书签,请求没有长度限制,允许二进制数据;

拓展:
HTTP1.1相比起HTTP1.0来说,多了持续连接和管线化的特性,持续连接可以减少因为每次对话断开和连接的开销,而管线化可以实现并发发送多个请求的功能。
因为HTTP协议是一种无状态协议,所以为了实现保持登录状态的功能,引进了Cookie的技术。
Cookie技术通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
Cookie会根据服务端发送的响应报文内的一个叫做"Set-Cookie"的首部字段信息,通知客户端保存Cookie。当下次客户端再次发送请求时,客户端会自动在请求报文中加入Cookie值后发送给服务端,服务端接收到客户端发送过来的Cookie之后,会去检查此Cookie是从哪个客户端发过来的,然后对于服务器之前的数据,最后得到之前客户端的状态信息

(3)关于HTTP的状态码:
HTTP状态码:(HTTP Status Code)是用以表示网页服务器超文本传输协议响应状态的3位数字代码;

类 别原因短语
1XX信息状态码接收的请求正在处理
2XX成功状态码请求正常处理完毕
3XX重定向状态码需要进行附加操作已完成请求
4XX客户端错误状态码服务器无法处理请求
5XX服务器错误状态码服务器处理请求出错

具体关于状态码的详解可点击一下链接:https://www.runoob.com/http/http-status-codes.html

常见的状态码:

状态码状态码英文名称状态码中文描述
200OK请求成功,一般用于GET和POST请求
202Accept已经接受请求,但未完成处理
302Found临时移动;资源临时被移动,客户端应继续使用原有URI
403Forbidden服务器理解请求客户端的请求,但拒绝执行此请求
404Not Found服务器无法根据客户端的请求找到资源
504Gateway Time-out充当网关或代理的服务器,未及时从远端服务器获取请求

常见的头部信息特殊字符:
Content-Type: 数据类型(text/html等);

Content-Length: 正文的长度 (标注正文的长度原因:因为TCP会粘包,会造成两个请求可能会粘到一起,这时就不知道正文会获取多长);

Host: 指定请求资源的Intenet主机和端口号(请求时必须包含主机头域,否则会以400状态码返回);客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;

User-Agent: 声明用户的操作系统和浏览器版本信息;(头域的内容包含了发出请求的用户信息)

referer: 当前页面是从哪个页面跳转过来的;

Location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;

Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;

4.简单的HTTP服务器:
实现一个简单的HTTP服务器, 只在网页上输出 “hello world”; 只要我们按照HTTP协议的要求构造数据, 就很容易 能做到;

 #include <sys/socket.h> 
 #include <netinet/in.h> 
 #include <arpa/inet.h> 
 #include <unistd.h> 
 #include <stdio.h> 
 #include <string.h> 
 #include <stdlib.h>
 
void Usage() {  
   printf("usage: ./server [ip] [port]\n"); 
}
 
 int main(int argc, char* argv[]) {  
    if (argc != 3) {    
       Usage();    
       return 1;
    }  
    int fd = socket(AF_INET, SOCK_STREAM, 0);  
    if (fd < 0) {    
       perror("socket");    
       return 1;  
    }  
    struct sockaddr_in addr;  
    addr.sin_family = AF_INET;  
    addr.sin_addr.s_addr = inet_addr(argv[1]);  
    addr.sin_port = htons(atoi(argv[2]));
 
     int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));  
     if (ret < 0) {    
        perror("bind");    
        return 1;  
     }  
     ret = listen(fd, 10);  
     if (ret < 0) {    
        perror("listen");    
        return 1;  
     }   
     for (;;) {    
        struct sockaddr_in client_addr;    
        socklen_t len;    
        int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);    
        if (client_fd < 0) {      
           perror("accept");      
           continue;    
        }    
        char input_buf[1024 * 10] = {0};  // 用一个足够大的缓冲区直接把数据读完.    
        ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);    
        if (read_size < 0) {      
           return 1;    
        }    
        printf("[Request] %s", input_buf);    
        char buf[1024] = {0};    
        const char* hello = "<h1>hello world</h1>";    
        sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);    
        write(client_fd, buf, strlen(buf));  
     }  
        return 0;
}

编译, 启动服务. 在浏览器中输入 http://[ip]:[port], 就能看到显示的结果 “Hello World”;

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值