应用层

应用层

内容概括:

  • 理解应用层的作用
  • 认识HTTP协议

应用层

协议:通信双方之间的一种自我约定

例如, 我们需要实现⼀个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进⾏计算, 最后再把结果返回给客户端。

// proto.h
  1 #pragma once                                                                                                                     
  2                                                                                                                                  
  3 /////////////////////////////////////                                                                                            
  4 // 应用层协议                                                                                                                    
  5 /////////////////////////////////////                                                                                            
  6                                                                                                                                  
  7 // C语言中结构体定义:为了后续命名中不加 struct, 所以在定义结构体时前面加typedef                                                 
  8 // 但 C++ 中已不存在此规定。                                                                                                     
  9 typedef struct Request                                                                                                           
 10 {                                                                                                                                
 11   int a;                                                                                                                         
 12   int b;                                                                                                                         
 13 }Request;                                                                                                                        
 14                                                                                                                                  
 15 typedef struct Response                                                                                                          
 16 {                                                                                                                                
 17   int sum;                                                                                                                       

 //plus_server.c  服务器端代码

  2 // 应用层协议的定制,包含两个方面:                                                                                              
  3 // 1. 制定好请求中包含哪些信息,响应中包含哪些信息。                                                                             
  4 // 2. 制定好如何进行序列化和反序列化。                                                                                           
  5 /////////////////////////////////////////////                                                                                    
  6 // 方案一:                                                                                                                      
  7 // 约定:                                                                                                                        
  8 // 客户端发送的请求包含两个数字,这两个数字使用','分割。                                                                         
  9 // 服务器给客户端响应的数据,只包含一个数字(也就是两个加数的和)                                                                  
 10 ///////////////////////////////////////////////////////////////                                                                  
 11 // 方案二:                                                                                                                      
 12 // 定义两个结构体:                                                                                                              
 13 // struct Request                                                                                                                
 14 // {                                                                                                                             
 15 //    int a;                                                                                                                     
 16 //    int b;                                                                                                                     
 17 // };                                                                                                                            
 18 //                                                                                                                               
 19 // struct Response                                                                                                               
 20 // {   
 21 //    int sum;                                                                                                                
 22 // };                                                                                                                            
 23 //                                                                                                                               
 24 ///////////////////////////////////////////////////                                                                              
 25 // 方案二VS方案一:(在该场景下)                                                                                                  
 26 // 1.节省空间                                                                                                                    
 27 // 2.解析方便                                                                                                                    
 28 /////////////////////////////////////////////                                                                                    
 29                                                                                                                                  
 30                                                                                                                                  
 31 #include <stdio.h>                                                                                                               
 32 #include <string.h>                                                                                                              
 33 #include <stdlib.h>                                                                                                              
 34 #include <unistd.h>                                                                                                              
 35 #include <sys/socket.h>                                                                                                          
 36 #include <netinet/in.h>                                                                                                          
 37 #include <arpa/inet.h>                                                                                                           
 38                                                                                                                                  
 39 #include "proto.h"  
 40
 41 typedef struct sockaddr sockaddr;                                                                                                
 42 typedef struct sockaddr_in sockaddr_in;                                                                                          
 43                                                                                                                                  
 44 // 处理请求                                                                                                                      
 45 void ProcessRequest(const Request* req, Response* resp)                                                                          
 46 {                                                                                                                                
 47   resp->sum = req->a + req->b;                                                                                                   
 48 }                                                                                                                                
 49                                                                                                                                  
 50 int main(int argc, char* argv[])                                                                                                 
 51 {                                                                                                                                
 52   if(argc != 3)                                                                                                                  
 53   {                                                                                                                              
 54     printf("Usage ./plus_server [ip] [port]\n");                                                                                 
 55     return 1;                                                                                                                    
 56   }                                                                                                                              
 57                                                                                                                                  
 58   // 1.创建文件描述符                                                                                                            
 59   int sock = socket(AF_INET, SOCK_DGRAM, 0);                                                                                     
 60   if(sock < 0)    
 61   {                                                                                                                              
 62     perror("socket");                                                                                                            
 63     return 1;                                                                                                                    
 64   }                                                                                                                              
 65                                                                                                                                  
 66   // 2.绑定端口号                                                                                                                
 67   sockaddr_in addr;                                                                                                              
 68   addr.sin_family = AF_INET;                                                                                                     
 69   addr.sin_addr.s_addr = inet_addr(argv[1]);                                                                                     
 70   addr.sin_port = htons(atoi(argv[2]));                                                                                          
 71   int ret = bind(sock, (sockaddr*)&addr, sizeof(addr));                                                                          
 72   if(ret < 0)                                                                                                                    
 73   {                                                                                                                              
 74     perror("bind");                                                                                                              
 75     return 1;                                                                                                                    
 76   }                                                                                                                              
 77                                                                                                                                  
 78   // 3.循环的读取数据                                                                                                            
 79   while(1)   
  80   {                                                                                                                              
 81     sockaddr_in peer;                                                                                                            
 82     socklen_t len = sizeof(peer);                                                                                                
 83     // 1.读取客户端的请求,把读到的字符串转换成 Request 结构体(反序列化)                                                         
 84     Request req;                                                                                                                 
 85     // 读到的客户端发送的字符串里面包含着两个加数                                                                                
 86     recvfrom(sock, &req, sizeof(req), 0, (sockaddr*)&peer, &len);                                                                
 87     printf("Request: a=%d, b=%d\n", req.a, req.b);                                                                               
 88                                                                                                                                  
 89     // 2.通过 Request 结构体,计算生成 Response 结构体。                                                                         
 90     // 服务器程序的核心,业务逻辑所在,可能会非常复杂。                                                                          
 91     // 不同的业务场景下,此处的实现也不同。                                                                                      
 92     // 处理请求                                                                                                                  
 93     Response resp;                                                                                                               
 94     ProcessRequest(&req, &resp);                                                                                                 
 95                                                                                                                                  
 96     // 3. 把 Response 结构体转成一个字符串(序列化),再发送给客户端                                                               
 97     sendto(sock, &resp, sizeof(resp), 0, (sockaddr*)&peer, sizeof(peer));                                                        
 98   }  
 99                                                                                                                                  
100   return 0;                                                                                                                      
101 }  

// plus_client.c  客户端代码

  1 #include <stdio.h>                                                                                                               
  2 #include <string.h>                                                                                                              
  3 #include <stdlib.h>                                                                                                              
  4 #include <unistd.h>                                                                                                              
  5 #include <sys/socket.h>                                                                                                          
  6 #include <netinet/in.h>                                                                                                          
  7 #include <arpa/inet.h>                                                                                                           
  8                                                                                                                                  
  9 #include "proto.h"                                                                                                               
 10                                                                                                                                  
 11 typedef struct sockaddr sockaddr;                                                                                                
 12 typedef struct sockaddr_in sockaddr_in;                                                                                          
 13                                                                                                                                  
 14                                                                                                                                  
 15 int main(int argc, char* argv[])                                                                                                 
 16 {                                                                                                                                
 17   if(argc != 3)                                                                                                                  
 18   {                                                                                                                              
 19     printf("Usage ./plus_server [ip] [port]\n");                                                                                 
 20     return 1;                                                                                                                    
 21   }                                   
 23   // 1.创建文件描述符                                                                                                            
 24   int sock = socket(AF_INET, SOCK_DGRAM, 0);                                                                                     
 25   if(sock < 0)                                                                                                                   
 26   {                                                                                                                              
 27     perror("socket");                                                                                                            
 28     return 1;                                                                                                                    
 29   }                                                                                                                              
 30                                                                                                                                  
 31   sockaddr_in server_addr;                                                                                                       
 32   server_addr.sin_family = AF_INET;                                                                                              
 33   server_addr.sin_addr.s_addr = inet_addr(argv[1]);                                                                              
 34   server_addr.sin_port = htons(atoi(argv[2]));                                                                                   
 35                                                                                                                                  
 36   // 3.循环的读取数据                                                                                                            
 37   while(1)                                                                                                                       
 38   {                                                                                                                              
 39     // 1.先从标准输入读入两个整数                                                                                                
 40     Request req;                                                                                                                 
 41     printf("input a: ");                                                                                                         
 42     fflush(stdout); 
 43     scanf("%d", &req.a);                                                                                                         
 44     printf("input b: ");                                                                                                         
 45     fflush(stdout);                                                                                                              
 46     scanf("%d", &req.b);                                                                                                         
 47                                                                                                                                  
 48     // 2.把 Request 结构体发送给服务器                                                                                           
 49     sendto(sock, &req, sizeof(req), 0, (sockaddr*)&server_addr, sizeof(server_addr));                                            
 50                                                                                                                                  
 51     // 3.从服务器中读取 Response 结构体                                                                                          
 52     Response resp;                                                                                                               
 53     recvfrom(sock, &resp, sizeof(resp), 0, NULL, NULL);                                                                          
 54                                                                                                                                  
 55     // 4.将结果显示到标准输出上                                                                                                  
 56     printf("Response: sum = %d\n", resp.sum);                                                                                    
 57   }                                                                                                                              
 58   return 0;                                                                                                                      
 59 }              

序列化:将结构体转换成字符串
反序列化:将字符串转换成结构体

将客户端给服务器发送的请求进行序列化的工具有:

google出品的protobuf 、xml 、json

  1. xml 相比于 html
// 以上面的两个加数相加为例:
// 对客户端发来的两个操作数相加的请求进行序列化:
<request>
<plusnum> 1 </plusnum>
<plusnum> 2 </plusnum>
</request>
优点:可以进行格式化的解析数据,方便人们肉眼去阅读,解析方便,可读性好。
缺点:因为加了很多的标签,导致所占用的空间太大。    

2. json :来源于 javascrpt
此处注意:java 和 javascript 毫无关系。

// 对客户端发来的两个操作数相加的请求进行序列化:
{
    request:[
        {plusnum:1},
        {plusnum:2},
    ]
}
json 和 xml 优缺点一样,两者只是在解析格式上不同。   

3. google protobuf:
优点:占用空间小。
缺点:可读性不好,因为全是二进制代码,导致根本不可读。

HTTP协议

超文本传输协议,它是应用层协议。应用层协议是我们程序员⾃⼰定的。

1.认识URL

URL就是我们俗称的“网址”
http://user:pass@www.example.jp:80/dir/index.htm?uid=1#ch1
http://:协议名
user:pass :登录信息
www.example.jp :服务器地址
80:服务器端口号
/dir/index.htm:带层次的文件路径,表示你要请求服务器上的哪个文件
uid=1:查询字符串
例如:搜索“鲜花”,将被解析成:

// 采用键值对的方式进行解析
query=%CF%CA%BB%A8
&_asf=www.sogou.com
&_ast=&w=01019900
&p=40040100&sut=4547
&sst0=1530349658694
&lkt=0%2C0%2C0
// & 是各个键值对之间的分隔符
// = 是键和值之间的分隔符 

. #:提醒我们要跳转到浏览器的哪个地方
ch1:片段标识符
拓展
http默认端口号:80
https默认端口号:443

2. urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
⽐如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进⾏转义。
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不⾜4位直接处理),每2位做⼀位,前⾯加上%,编码成%XY格式。
例如:
这里写图片描述
“+” 被转义成了 “%2B”
urldecode就是urlencode的逆过程。

HTTP协议格式

利用fiddler抓包工具
HTTP请求

// 例如:
GET http://map.baidu.com/newmap=1&s=con%26wd%3D%E6%AF%94%E7%89%B9%E7%A7%91%E6%8A%80%26c%3D233&from=alamap&tpl=mapdots HTTP/1.1
Host: map.baidu.com
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Referer: https://www.baidu.com/link?url=nW3KaSbHoXM4TNir9xSAoE0Gs8tRzOg375yWJYQfrlsxI_0lf_w0w4FwgdHqpBxvpohpBiWeDWGsL5oY6kAJh8dWLS4o5QmCsWU4vQJnYTbvCTcG_uOTmcYXVKcCBvjvWSkARk0YPt5F1wm2VjtlzSTuXsncnpDoyb74CiVl4KW&wd=&eqid=99acac6a0004ed36000000065b377485
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: BAIDUID=65D331D0049FB2C50A32D0BC56B2FE26:FG=1; BIDUPSID=65D331D0049FB2C50A32D0BC56B2FE26; PSTM=1528800418; BDORZ=FFFB88E999055A3F8A630C64834BD6D0; H_PS_PSSID=1448_21087; PSINO=7; BDRCVFR[7MNccJyLkSD]=aeXf-1x8UdYcs; BDRCVFR[DnMS6Y3XFW_]=aeXf-1x8UdYcs


HTTP请求格式:

  • 首行:方法(method:GET/POST) url 版本号
  • header:若干行的键值对,每一行是一个键值对。键值对的键和值之间使用“: ”来分割。header部分一共有多少行,这个不是能直接确定的。
  • 空行:只包含一个\n,作为header的结束标记。
  • body:数据格式取决于header中的conten-Type字段,比较常见的用法是body格式和query-string的格式相同。数据的长度取决于header中的conten-length字段。

HTTP的方法

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

最常用的就是GET方法和POST方法。

对header中的一些重要字段进行解析:

  • Content-Type: 数据类型(text/html等)
  • Content-Length: Body的⻓度
  • Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端⼝上
    -User-Agent:包含两个方面:(a)你的操作系统是什么 (b) 你的浏览器是什么 用来判断当前访问我的客户端是什么样的(电脑还是手机)
  • referer:告知当前页面的来源,是通过哪一个页面跳转而来的。
  • location: 搭配3xx状态码使⽤, 告诉客户端接下来要去哪⾥访问;
  • cookie:服务器将当前的客户信息写回给浏览器,浏览器临时就存储了这些信息(以字符串的格式),一个cookie一般大小为4k(4094)。浏览器会根据域名去存储这些信息。

HTTP响应

// 例如:
HTTP/1.1 200 OK
Cache-Control: private,max-age=0
Content-Type: text/html;charset=utf-8
Date: Sat, 30 Jun 2018 12:16:23 GMT
Expires: -1
Http_x_bd_logid: 4206575744
Http_x_bd_logid64: 5258179523571509041
Http_x_bd_product: map
Http_x_bd_subsys: webmap
Server: apache
Vary: Accept-Encoding
Content-Length: 160652

<!DOCTYPE HTML>
<html>
<head>
<!--STATUS OK-->
<meta charset="utf-8" />
<meta name="description" content="浏览地图、搜索地点、查询公交驾车线路、查看实时路况,您的出行指南、生活助手。提供地铁线路图浏览,乘车方案查询,以及准确的票价和时间信息。" />
<meta name="keywords" content="景区热力图,景区人流图,景区人多不多,人群分布热力图,景区拥挤指数,春节拥挤指数,热力地图,地铁线路,地铁专题,地铁方案,地铁票价,地铁时间,地铁换乘"/>
<meta http-equiv="imagetoolbar" content="no" />
<meta property="wb:webmaster" content="10fd28588b2f9686" />
<meta name="sogou_site_verification" content="Mtd2WIt6Ne"/>
<meta name="msvalidate.01" content="2D36557CC0E331F85475A3B85DAAA4FC" />
<meta http-equiv="X-UA-Compatible" content="IE=Edge,chrome=1" />
<meta name="renderer" content="webkit" />
<title>百度地图</title>
<script>
var corePerfMonitor={pageStartTime:+new Date,firstRender:true,components:{"CityIndex":1,"PoiSearch":1,"BusTrans":1,"NavWalk":1,"NavTrans":1},componentName:"CityIndex",data:{},timer:null,onTilesLoaded:function(){var mapType=this.getMapType();var tileLoadedTime=(+new Date)-corePerfMonitor.pageStartTime;var mapTypeStr="normal";if(mapType==="B_SATELLITE_MAP"){mapTypeStr="satellite"}else{if(mapType==="B_EARTH_MAP"){mapTypeStr="earth"}}corePerfMonitor.data["z_mapload_first_"+mapTypeStr+"_"+corePerfMonitor.componentName.toLowerCase()]=tileLoadedTime;corePerfMonitor.timer&&clearTimeout(corePerfMonitor.timer);corePerfMonitor.timer=setTimeout(function(){corePerfMonitor.firstRender=false;alog("cus.fire","time",corePerfMonitor.data);map.removeEventListener("tilesloaded",corePerfMonitor.onTilesLoaded);delete corePerfMonitor.timer;delete corePerfMonitor.data},5000)}};
</script>
<script>

HTTP响应格式:

  • 首行: [版本号] + [状态码] + [状态码解释]
  • header:若干行的键值对,每一行的键值对的键和值之间使用“: ”分割。
  • 空行:header的结束标记。
  • body:body的数据格式取决于content-Type往往是一个html的格式;数据长度取决于conten-length;set-cookie字段决定了客户端应该保存哪些cookie。

HTTP的状态码

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

最常⻅的状态码, ⽐如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)。

HTTP两个重要的面试题:

  1. GET方法 VS POST方法
    GET方法:一般无body
    POST方法:有body
  2. HTTP的状态码有哪些?
    查看上面的状态码表,并列举出一些常见的状态码。
实现一个最简单的HTTP服务器
// 只在网页上输出"helloworld"
  1 #include <stdio.h>                                                                                                               
  2 #include <stdlib.h>                                                                                                              
  3 #include <string.h>                                                                                                              
  4 #include <unistd.h>                                                                                                              
  5 #include <pthread.h>                                                                                                             
  6 #include <sys/socket.h>                                                                                                          
  7 #include <netinet/in.h>                                                                                                          
  8 #include <arpa/inet.h>                                                                                                           
  9                                                                                                                                  
 10 typedef struct sockaddr sockaddr;                                                                                                
 11 typedef struct sockaddr_in sockaddr_in;                                                                                          
 12                                                                                                                                  
 13 int ServerInit(const char* ip, short port)                                                                                       
 14 {                                                                                                                                
 15   // 1.创建文件描述符                                                                                                            
 16   int listen_sock = socket(AF_INET, SOCK_STREAM, 0);                                                                             
 17   if(listen_sock < 0)                                                                                                            
 18   {                                                                                                                              
 19     perror("socket");                                                                                                            
 20     return -1;                                                                                                                   
 21   }   
 22   // 2.绑定端口号                                                                                                                
 23   sockaddr_in addr;                                                                                                              
 24   addr.sin_family = AF_INET;                                                                                                     
 25   addr.sin_addr.s_addr = inet_addr(ip);                                                                                          
 26   addr.sin_port = htons(port);                                                                                                   
 27   int ret = bind(listen_sock, (sockaddr*)&addr, sizeof(addr));                                                                   
 28   if(ret < 0)                                                                                                                    
 29   {                                                                                                                              
 30     perror("bind");                                                                                                              
 31     return -1;                                                                                                                   
 32   }                                                                                                                              
 33   // 3.监听                                                                                                                      
 34   ret = listen(listen_sock, 5);                                                                                                  
 35   if(ret < 0)                                                                                                                    
 36   {                                                                                                                              
 37     perror("listen");                                                                                                            
 38     return -1;                                                                                                                   
 39   }                                                                                                                              
 40   return listen_sock;                                                                                                            
 41 }                                                                                                                                
 42 
 43 void* ThreadEntry(void* arg)                                                                                                     
 44 {                                                                                                                                
 45   int64_t new_sock = (int64_t)arg;                                                                                               
 46   // 1.读数据并解析,由于我们此处无脑返回 helloworld, 所以只是把数据读出来,并不打算解析。                                        
 47   char buf[1024*10]={0};                                                                                                         
 48   read(new_sock, buf, sizeof(buf)-1);                                                                                            
 49   // 2.根据请求计算响应,由于我们此处无脑返回 helloworld, 所以此处跳过了计算步骤,直接写 helloworld 就行了。                     
 50   // 3.把响应写回给客户端                                                                                                        
 51   const char* first_line = "HTTP/1.1 200 OK\n";                                                                                  
 52   const char* blank_line = "\n";                                                                                                 
 53   const char* body = "<h1>helloworld</h1>";                                                                                      
 54   char header[1024]={0};                                                                                                         
 55   sprintf(header, "Content-Length: %lu\n",strlen(body));//strlen()返回的类型是size_t,为无符号长整型,所以用lu                    
 56   write(new_sock, first_line, strlen(first_line));                                                                               
 57   write(new_sock, header, strlen(header));                                                                                       
 58   write(new_sock, blank_line, strlen(blank_line));                                                                               
 59   write(new_sock, body, strlen(body));                                                                                           
 60   close(new_sock);                                                                                                               
 61   return NULL;                                                                                                                   
 62 } 
 64 int main(int argc, char* argv[])                                                                                                 
 65 {                                                                                                                                
 66   if(argc != 3)                                                                                                                  
 67   {                                                                                                                              
 68     printf("Usage ./http_server [ip] [port]\n");                                                                                 
 69     return 1;                                                                                                                    
 70   }                                                                                                                              
 71                                                                                                                                  
 72   //////////////////////////////////////////////////////////                                                                     
 73   // HTTP 服务器往往是基于TCP实现的。                                                                                            
 74   // 1. 创建一个TCP服务器                                                                                                        
 75   // 2. 按照HTTP协议的格式解析请求,并且按照HTTP协议的格式构造响应。                                                             
 76   //////////////////////////////////////////////////////////                                                              
 77                                                                                                                                  
 78   int listen_sock = ServerInit(argv[1], atoi(argv[2]));                                                                          
 79   if(listen_sock < 0)                                                                                                            
 80   {                                                                                                                              
 81     printf("ServerInit failed!\n");                                                                                              
 82     return 1;                                                                                                                    
 83   }                                                                                                                              
 84   printf("ServerInit OK\n"); 
 85                                                                                                                                  
 86   while(1)                                                                                                                       
 87   {                                                                                                                              
 88     sockaddr_in peer;                                                                                                            
 89     socklen_t len = sizeof(peer);                                                                                                
 90     int64_t new_sock = accept(listen_sock, (sockaddr*)&peer, &len);                                                              
 91     if(new_sock < 0)                                                                                                             
 92     {                                                                                                                            
 93       perror("accept");                                                                                                          
 94       return 1;                                                                                                                  
 95     }                                                                                                                            
 96                                                                                                                                  
 97     pthread_t tid;                                                                                                               
 98     pthread_create(&tid, NULL, ThreadEntry, (void*)new_sock);                                                                    
 99     pthread_detach(tid);                                                                                                         
100   }                                                                                                                              
101   return 0;                                                                                                                      
102 } 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值