应用层(http协议) http与https区别

在协议分层的TCP/IP(或四层)通讯协议采用了5层的层级结构,5层分别包括:应用层、传输层、网络层、数据链路层、物理层。5层一些简单功能和著名协议可参考这篇博客:https://blog.csdn.net/sophia__yu/article/details/82717115
一.应用层
程序员写的一些网络程序都在应用层。应用层负责应用程序间的数据沟通。
网络通信协议:网络数据传输中数据格式的约定
自定制协议:程序员自己设置的数据传输格式(下面网络加法器的定义的结构体)
知名协议:http协议
如果要实现一个网络加法器,客户端需要把计算的两个加数发过去,服务端再将进行计算,然后将计算结果返回给客户端。
对于网络加法器有两种方案:
方案一:(网络通信协议)
客户端发送⼀一个形如"1+1"的字符串;
这个字符串中有两个操作数, 都是整形;
两个数字之间会有⼀一个字符是运算符, 运算符只能是 + ; 数字和运算符之间没有空格;
方案二(自定制协议)
定义结构体来表⽰示我们需要交互的信息;
发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符 串转化回结构体; 或者也可以不是字符串,只要客户端和服务端约定好接收和发送数据的类型即可;
这个过程叫做 “序列化” 和 “反序列化”
序列化:将结构化数据/对象转换成内存中的二进制字符串;
反序列化:将内存中的二进制字符串转换成结构化数据/对象。

//客户端简单计算器代码
//客户端传输一个格式化的数据(两个数字和运算符)到服务器,
//传输的格式化数据用结构体表示

#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>

typedef struct cal_req
{
        int x;
        int y;
        unsigned char sym; //操作符
}cal_req;

int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("Usage ./ ip port\n");
        }
        //1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(sockfd<0)
        {
                perror("socket error");
                return -1;
        }
        //2.绑定地址信息,但不推荐手动绑定信息
        //3.向服务端发起连接请求
        struct sockaddr_in ser_addr;
        ser_addr.sin_family=AF_INET;
        ser_addr.sin_port=(htons)(atoi(argv[2]));
        ser_addr.sin_addr.s_addr=(inet_addr)(argv[1]);//因为argv[]是char*,用atoi使字符串转成整型
        int len=sizeof(struct sockaddr_in);
        int ret=connect(sockfd,(struct sockaddr*)&ser_addr,len);
        if(ret<0)
        {
                perror("connect error");
                close(sockfd);
                return -1;
        }
//连接成功,socket描述符里有服务端和客户端IP地址和port
        //发送加法的数据和运算符
        cal_req req;
        req.x=1;
        req.y=2;
        req.sym='+';
        len=sizeof(cal_req);
        ret=send(sockfd,(void*)&req,len,0);
        if(ret<0)
        {
                perror("send error");
                close(sockfd);
                return -1;
        }
        //接收运算结果
        int sum=0;
        recv(sockfd,(void*)&sum,sizeof(int),0);
        printf("sum:%d\n",sum);
        close(sockfd);
        return 0;
}
//网络版计算器,需要传输两个数据和一个运算符
//自定制协议:
//前四个字节是第一个数据
//中间四个字节是第二个数据
//最后一个字节是运算符
//为了对数据协议更加容易组织和解析,所以用结构化数据
//1.创建套接字
//2.绑定地址信息
//3.开始监听
//4.接受客户端连接
//5.接受客户端发送来的数据
//6.将接受的数据进行解析
//7.返回结果
//8.关闭描述符 
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<netinet/in.h>

#include<arpa/inet.h>
#include<sys/socket.h>

//listen 从close状态转换为监听状态  
typedef struct cla_req
{
        int x;
        int y;
        unsigned char sym;
}cla_req;
int main(int argc,char* argv[])
{
        if(argc!=3)
        {
                printf("Usage:./ ip  port\n");
                return -1;
        }
        //创建套接字
        int sockfd;
        sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(sockfd<0)
        {
                perror("socket error");
                return -1;
        }
        //2.绑定地址信息
        struct sockaddr_in lis_addr;
        lis_addr.sin_family=AF_INET;
        lis_addr.sin_port=htons(atoi(argv[2]));
lis_addr.sin_addr.s_addr=inet_addr(argv[1]);
        int len=sizeof(struct sockaddr_in);
        int ret=bind(sockfd,(struct sockaddr*)&lis_addr,len);
        if(ret<0)
        {
                perror("bind error");
                close(sockfd);
                return -1;
        }
        //3.监听,连接成功队列最多有个结点
        if(listen(sockfd,5)<0)
        {
                perror("listen error");
                close(sockfd);
                return -1;
        }
        while(1)
        {
                //4.获取新连接的客户端
                struct sockaddr_in cli_addr;
                len=sizeof(struct sockaddr_in);
                int newsockfd;
 newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len);
                if(newsockfd<0)
                {
                        perror("accpet error");
                        close(newsockfd);
                        continue;
                }
                //5.接受客户端的数据
                cla_req req;
                len=sizeof(req);
                ret=recv(newsockfd,(void*)&req,len,0);
                if(ret<0)
                {
                        perror("recv  error");
                        close(newsockfd);
                        continue;
                }
                else if(ret==0)
                {
                        perror("peer shutdown");
                        close(newsockfd);
                        continue;
 }

                int sum=0;
                if(req.sym=='+')
                {
                        sum=req.x+req.y;
                }
                //6.将计算结果返回给客户端
                ret=send(newsockfd,(void*)&sum,sizeof(int),0);
                if(ret<0)
                {
                        perror("send error");
                        close(newsockfd);
                        return -1;
                }
                close(newsockfd);
        }
        close(sockfd);
        return 0;
}

在这里插入图片描述
只要保证, 一端发送时构造的数据, 在另一端能够正确的进行解析, 就可以。 这种约定, 就是 应用层协议。
http协议
http协议(超文本传输协议)是应用层知名协议。
URL是平常所说的网址。
在这里插入图片描述
端口号如果没有指定则默认是80,(为什么说端口号0-65535之间一个数字,但0-1024不推荐使用,是因为0-1024被一些知名协议所占用,如80被知名协议http占用);
/是相对根目录;
查询字符串在标准代码称QUERY_STRING
ch1:直接跳转某个网页的那个标签;
URL编码:
为什么存在URL编码:
因为在URL中特殊字符在很多地方有特殊含义,因此为了防止提交的数据也包含特殊字符造成歧义,因此对提交的数据进行URL编码操作.
URL编码是什么?
url编码(查询字符串):将特殊字符高四位转换成16进制数字,低四位转换成16进制数字,为了告诉对方这是已经编码过的字符,因此在转义后的两个16进制字符前加上%;
URL编码过程:
比如:特殊字符’+’:
在这里插入图片描述
Http协议格式(Http数据结构)
http是一个明文字符串传输。
http请求:
http请求协议格式分为四个部分:首行、协议头、空行、正文。
首行:[请求方法]+[URL]+[协议版本]
比如:GET https:: //www.baidu.com/ HTTP/1.1。
请求方法包括:GET和POST。
GET和POST区别和联系:
联系:GET和POST都是为了获取资源或者提交资源。
区别:
GET :更多是为了获取资源,但也可以提交资源,但是提交资源的URL长度有限(限制早期是IK,现在nginx服务器默认的限制是Ik或者8K,这是根据服务器的硬件配置有关,一般为内存一页的大小,目前大部分为4K,即4096字节。)
POST:更多用于提交表单数据,也可以获取资源,提交的数据是在正文中,正文的长度没有限制。
总结:GET将数据发送到URL,POST将数据发送在正文中,两者区别是有无正文。
协议头:
请求的属性,冒号分割的键值对;每组属性之间用\r\n分割;遇到空行则表示Header部分结束。下文会有具体的协议头。
空行:
空行其实是协议头和正文之间一行,标识协议头结束,正文开始。
**正文:**空行后面的内容都是正文,正文允许为空字符串,如果正文存在,则在协议头中会有一个Content-Length属性来标识正文的长度。
整体如下:首行 \r\n协议头1\r\n协议头2\r\n\r\n正文
http响应:
http响应协议格式包括四部分:首行、协议头、空行、正文。
首行:[版本号]+[状态码]+[状态码解释];
比如:HTTP/1.1 302 Moved Temporarily
协议头:
请求的属性,冒号分割的键值对;每组属性之间用\r\n分割;遇到空行则表示Header部分结束。下文会有具体的协议头。
空行:
空行其实是协议头和正文之间一行,标识协议头结束,正文开始。
正文:
空行后面的内容都是正文,正文允许为空字符串,如果正文存在,则在协议头中会有一个Content-Length属性来标识正文的长度;如果服务器返回一个html页面,那么html页面内容就是在正文中。
HTTP状态码:
在这里插入图片描述
最常见的状态码:200(OK),404(NOT FOUND),403(Forbidden),302(Redirect,重定向),504(Bad Gateway)
实现一个十分简单的HTTP服务器,只在网页上输出"hello world",只要我们按照HTTP协议格式的要求构造数据,就很容易能做到:

//十分简单的http服务端程序,它可以接收浏览器的请求,但是,不管什么请求,都只回复同一个资源
//<hl>hello world</hl>
//http是应用层协议,在传输层使用的是tcp协议,所以我们要写的是http服务端程序实际上是tcp服务端程序
//假设我们现在http服务端程序监听的是10000端口,也就意味着浏览器请求的是时候url中服务器地址也需要手动指定为
//10000,否则浏览器默认使用80端口



#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
//整体的http响应:
//首行  HTTP/1.1 200 OK   
// 头信息
// Content-Length: 
// Content-Type:text/html; charset=UTF-8
int main()
{
        int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
        if(sockfd<0)
        {
                perror("sockfd error");
                return -1;
        }
        struct sockaddr_in lis_addr;
        lis_addr.sin_family=AF_INET;
        lis_addr.sin_port=htons(9001);
        lis_addr.sin_addr.s_addr=(inet_addr)("192.168.61.128");
        int len=sizeof(struct sockaddr_in);
        int ret=bind(sockfd,(struct sockaddr*)&lis_addr,len);
        if(ret<0)
        {
                perror("bind error");
                return -1;
        }
        if(listen(sockfd,5)<0)
        {
         perror("listen error");
                return -1;
        }
        while(1)
        {
                struct sockaddr_in cli_addr;
                len=sizeof(struct sockaddr_in);
                int newsockfd=accept(sockfd,(struct sockaddr*)&cli_addr,&len);
                if(newsockfd<0)
                {
                        perror("accept error");
                        continue;
                }
                char buff[1024]={0};
                //接收数据
                ret=recv(newsockfd,buff,1023,0);
                if(ret>0)
                {
                        printf("req:[%s]\n",buff);
                }
                else
                {
                        perror("recv error");
                        close(newsockfd);
                        continue;
                }
                memset(buff,0x00,1024);
                char* str="<h1>hello world</h1>";
                //sprintf(buff,"%s\r\n%s%d\r\n%s\r\n%s\r\n\r\n%s","HTTP/1.1 302 FOUND","Content-Length: ",strlen(str),"Content-Type: text/html; charset=UTF-8","Location: http://www.baidu.com/",str);
                //sprintf(buff,"%s\r\n%s%d\r\n%s\r\n\r\n%s","HTTP/1.1 404 NOT FOUND","Content-Length: ",strlen(str),"Content-Type: text/html; charset=UTF-8",str);
               sprintf(buff,"%s\r\n%s%d\r\n%s\r\n\r\n%s","HTTP/1.1 200 OK","Content-Length: ",strlen(str),"Content-Type: text/html; charset=UTF-8",str);
                printf("%s\n",buff);
                //sprintf(buff,"HTTP/1.0 200 OK\r\nContent-Length: %lu\r\n\r\n%s",strlen(str),str);
                send(newsockfd,buff,strlen(buff),0);
           close(newsockfd);
        }
        close(sockfd);
        return 0;
}

注:一般在运行时,需要关闭防火墙:service iptables stop(临时关闭防护墙)
如果状态码是200,结果如下:
在这里插入图片描述
在这里插入图片描述
如果状态码是302,即重定向为百度,需要和Location一起使用:
在这里插入图片描述
如果是404(NOT FOUND),结果如下:在这里插入图片描述

HTTP常见协议头:
User-Agent :操作系统信息,根据不同的操作系统,返回不同的页面
Cookie :用户认证登录信息
Referer :从 哪一个页面跳转
Content-Type:数据类型(text/html等)
Content-Length :当前正文长度(字符)
Location:搭配3xx状态码使用,告诉客户端接下来去哪儿访问。
http和https区别:
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。简单来说,HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全。
区别:
 1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
 2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
 3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
 4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
 客户端在使用HTTPS方式与Web服务器通信时有以下几个步骤,如图所示。
(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。
(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。
(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。
(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。
(5)Web服务器利用自己的私钥解密出会话密钥。
(6)Web服务器利用会话密钥加密与客户端之间的通信。

猜你喜欢

转载自blog.csdn.net/sophia__yu/article/details/82940081
今日推荐