HTTP详解,以及简易HTTP web服务器的实现

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/weixin_43949535/article/details/102644963

2019年10月19日23:23:49

万维网

万维网并不是某种特殊的计算机网络,它是一个大规模的、联机式的信息储藏所。万维网使用链接的方法可以非常方便地从互联网上一个站点去访问另一个站点,从而主动地按需获取到丰富的信息。万维网提供的分布式服务特点如下:在这里插入图片描述
万维网是一个分布式的超媒体系统,是超文本系统的扩充。

超文本:指包含指向其他文档的链接的文本(仅包含文本信息),它是万维网的基础。超媒体:还包含其他形式的信息:图形、声音、动画等。

万维网以C/S方式工作:上面的浏览器就是以后主机上的一个client程序;而万维网文档所处的主机(运行着server程序),被称为万维网服务器。

client程序向server程序发出请求,server程序向client程序送回以后所需要的万维网文档(在一个client程序主窗口显示出的万维网文档被称为 页面)。
在这里插入图片描述
万维网使用统一资源定位符URL来标识万维网上的各种文档,并使得每一个文档都有一个在Internet上唯一的标识符URL。万维网的client程序和server程序之间的需要遵守的协议——HTTP(基于TCP连接实现可靠的传送)。HTML语言使得用户可以实现 用链接从本页面到Internet的其他页面 和 能够在自己的主机屏幕上将这些页面给显示出来。最后 用户是通过搜索软件在Internet上查找所需信息。

超文本传输协议HTTP

HTTP的操作流程

HTTP协议定义了 万维网client怎么向server请求万维网文档,以及server怎么把文档传送给浏览器。(通俗来说:就是用于从www服务器传输超文本到本地浏览器的应用层 传递协议:浏览网页)

  1. HTTP是面向事务的应用层协议。是万维网可靠地交换文件的重要基础。事务:值一系列的信息交换(是一个不可分割的整体),也即要么所有的信息交换全部成功,要么一次交换也不进行。
  2. HTTP不仅传递 完成超文本跳转的所必需的信息,而且也传递 任何可以从互联网上得到的信息(文本、超文本、声音 图像等)。
  3. HTTP协议就是 在浏览器和服务器之间的请求和响应的交互过程中,所必需遵照的 规定的格式和一定的规则
  4. HTTP是一个应用层协议(一种通信协议),由请求和响应构成,是一个标准的C/S模型。
  5. HTTP协议通常承载于(TCP/IP通信协议)TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。默认HTTP的端口号为80,HTTPS的端口号为443。
  6. HTTP的client通常是一个应用程序(web浏览器等),来实现通过建立连接到server,以向server发送一个或者多个HTTP请求的过程。而HTTP的server也是一个应用程序,通常是由web服务器来实现的。也是通过接收来自client的请求然后向client回送HTTP响应数据。
  7. HTTP协议永远都是client发起请求,server回送响应。所以这样就限制了使用HTTP协议:无法实现在client没有发起请求的时候,server将消息推送给客户端。

在这里插入图片描述
我详细地讲一下,上图的一个执行流程:

  • 浏览器分析超链指向的页面的URL(只要单击某个超链,HTTP的工作开始)
  • 浏览器向DNS请求解析 www.tsinghua.edu.cn的IP地址
  • DNS解析出清华大学服务器的IP地址
  • 浏览器和服务器建立TCP连接
  • 浏览器发出取文件命令:GET/** / ** /index.html
  • 服务器给出响应,把上面的这个文件发给浏览器
  • TCP连接释放
  • 浏览器显示“清华大学院系设置”文件index.html中所有文件

在上述过程中:

  • 请求方式的格式为:统一资源标识符(URL)、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。
  • 响应信息其格式为一个状态行,包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
  • 客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后client与server断开连接。
  • 如果在以上过程中的某一步出现错误,那么产生错误的信息将返回到客户端,由显示屏输出。对于用户来说,这些过程是由HTTP自己完成的,用户只要用鼠标点击,等待信息显示就可以了。

在这里插入图片描述

  1. 简单快速体现在client在向server请求服务时,只需要提交 方法和路径即可。
  2. HTTP使用的是 面向连接的TCP作为 传输层协议,来保证数据的可靠传输。(也就是说HTTP不用考虑 数据在传输过程中的一些问题)。注:HTTP是无连接的,在通信的双方交换HTTP报文之前不需要去建立所谓的HTTP连接。但是这样的话 就限制了每次连接只处理一个请求,server处理完client的请求之后收到client的应答之后 就断开连接,不过这样可以使得大大节省传输时间。
  3. 无状态:同一个client第二次访问同一个server上的页面时,server的响应与上次被访问时的相同(前提:该页面没有更新)。也就是说:server不记得曾经访问的这个用户,也不记得给client访问的次数。(同一个客户端的这次请求和上次请求是没有对应关系)这样的直接的好处就是:简化服务器的设计,使服务器更容易支持大量并发的HTTP请求。

请求一个万维网文档所需的时间

在这里插入图片描述
上述过程为:请求一个万维网文档时,HTTP协议首先和server建立TCP,前两次握手在完成之后。此时万维网client就把HTTP请求报文 作为这第三个报文的数据。完成建立连接之后,万维网server收到HTTP请求报文后,就把所请求的文档作为响应报文返还client。

在上面这个过程中:总时间=2*RTT+文档传输时间(与文档大小成正比)

HTTP1.0和1.1的区别

  • 首先说一下1.0的缺陷何在:如上,没请求一个文档就需要总时间=2*RTT+文档传输时间的时间时间开销。所以说,若是一个主页上面有大量的链接对象(如图片)需要去依次进行链接,那么每一次的链接下载就需要 总时间=2 * RTT+文档传输时间的支出。且第二点:每一次万维网client和server建立起TCP连接,就需要分配缓存和变量等。尤其是当server同时要去处理大量的client请求时,这种 非持续连接会使得server负担很重。(不过好在server可以同时打开多个并行的TCP连接,每个TCP连接处理一个client的一个请求)
  • 1.1使用了持续连接 解决了上述问题。持续连接:万维网server在发送响应之后 仍然保持这条连接一段时间。这样使得同一个用户(浏览器)和该服务器可以继续在这条连接上 传送后续的HTTP请求和响应报文。
  • 区别:一言以蔽之,在1.0中 建立连接后,client发送一个请求,server回送一个响应后就关闭连接。 也即 当浏览器再次请求时,又要建立连接(这是一个不断建立连接的过程);而在1.1的持续连接下:通过这个连接,浏览器可以建立连接后,client发送请求,server回送响应后 继续发送请求并再次等到返回信息。也即:client可以连续发送多个请求,而不用等待每一个响应的到来。

HTTP报文结构

HTTP的两种报文:

  • 请求报文:client向server发送请求报文
  • 响应报文:从server到client的应答
    在这里插入图片描述
    因为HTTP是面向文本的,因此 报文中的每个字段都是ASCII码串,故而每个字段的长度不确定。如上图所示:两种报文都是由三部分组成,两种报文格式的区别就是 开始行的不同。
  • 开始行:用于区分是请求报文还是响应报文。在请求报文的开始行叫做请求行(Requesr-Line),在响应报文中的开始行叫做状态行(Status-Line)。在开始行的三个字段之间使用空格分割开,“CR” 和 “LF” 分别表示回车和换行(编写程序时用 \r\n)
  • 首部行:用来说明 浏览器、服务器或报文主体的一些信息。首部可以有多行,但也可以不使用。在每一个首部行中都有首部字段名和其对应的值,每一行在结束的地方都要有“回车”和“换行。整个首部行结束时,还有一行空行将首部行与后面的实体主体分开。
  • 实体主体:在请求报文中一般不用这个字段,而在响应报文中也可能没有这个字段。

HTTP请求报文

请求报文的第一行"请求行"只有三个内容,即方法、请求资源的URL以及HTTP协议的版本。 方法就是:对所请求的对象进行的操作,这些方法实际上也就是一些命令。因此,请求报文的类型是由它所采用的方法决定的。在这里插入图片描述
如上图(谢老师书中的截图),GET HEAD POST是HTTP1.0最初定义的三种请求方法,其余5种由HTTP1.1所规定。
这部分可以看一下这位老哥的博客:HTTP报文的请求体和方法详解
在这里插入图片描述
接下来 我们分析一个完整的HTTP请求报文:
在这里插入图片描述
接下来详谈几个常用方法:

  • GET方法:请求指定的页面信息,并返回实体主体。通常请求server发送某个资源:在这里插入图片描述
  • HEAD方法:类似于GET请求,只不过返回的响应中没有具体的内容,主要是用来获取报头(只返回首部,不会返回实体的主体部分)。在这里插入图片描述
  • POST方法:向指定资源提交数据 进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的建立或已有资源的修改。在这里插入图片描述
  • TRACE方法:回显server收到的请求,主要用于测试或诊断(或者说用于验证是否如愿穿过了请求/响应链)。注:TRACE请求中不能带有实体的主体部分。如下图所示:TRACE请求会在目的服务器端发起一个“环回”诊断。行程最后一站的服务器会弹会一条TRACE响应,并在响应主体中携带它收到的原始请求报文。在这里插入图片描述

在面试中最常问的方法就是上面的post和get的区别:

方法\区别 目标 安全特性 传输数据大小 字符集
POST方法 向server发送数据 安全高 可传输大量数据,上传文件时只可用POST 支持标准字符集,可传递中文字符
GET方法 从server上获取资源 不安全:URL可见,会泄露信息,如密码等 受URL长度限制,传输数量少但效率较高 只支持ASCII字符,向服务器传的中文字符可能会出现乱码

不安全的原因:GET传输数据是通过URL请求,以field(字段): value的形式,放在URL后,并用"?“进行连接,而多个请求数据间用”&"连接,这个过程我们用户是可见的(所以说GET是不安全的,就是因为URL是可见的,可能会泄露一些重要信息,如密码等)。POST传输数据将字段与对应值封存在请求实体中发送给服务器,这个过程对用户是不可见的。

HTTP响应报文

每一个请求报文发出后都能够收到一个响应报文。响应报文的第一行就是状态行
其状态行主要包含三部分内容:HTTP版本,状态码以及解释状态码的简单短语。

下面分析一个具体的HTTP响应报文:在这里插入图片描述
这里面重要的就是状态码
常见的状态码有:

  • 200 Accepted{接收}
  • 301 Moved Permanently{永久地转移到其他URL}
  • 302 Found{临时重定向}
  • 400 Bad Request{错误的请求}
  • 404 Not Found{找不到}
  • 500 Internal Server Error{服务器内部错误}

这一部分可以详见这位老哥的博客:HTTP状态码
以及另一位老哥的博客:
巧记HTTP状态码

HTTP状态码由三位数字组成,第一个数字定义了状态码的类型,后两个数字没有分类的作用。状态码共分为5大类:
在这里插入图片描述

  • 1**:表示通知信息,如请求收到了 或 正在进行处理
  • 2** :表示成功,如接收 或 知道了
  • 3** :表示重定向,如要完成请求 还必须采取进一步的行动
  • 4** :表示客户的差错,如请求中有错误的语法 或 不能完成
  • 5** :表示服务器的差错,如服务器失效无法完成请求

昨天面试的时候,面试官问我关于HTTP与HTTPS的区别:
其实HTTPS就是在http上面加上加密处理(一般是SSL安全通信线路)+ 认证 + 完整性保护的措施。其端口号:443.且两者的URL开头不一样: http 的URL 以http:// 开头,https以https:// 开头。简而言之:http 是超文本传输协议,信息是明文传输(不加密,内容可能被窃听),https 则是具有安全性的ssl加密传输协议(验证报文完整性,避免被篡改)。且https 通过证书可以验证通信方身份,避免伪装。


简易HTTP web服务器的实现

基本的文件内容如下:在这里插入图片描述
在这里插入图片描述
效果展示:当输入192.168.0.102,默认传输主页面index.html在这里插入图片描述
点击next ,进行跳转next.html
在这里插入图片描述
接下来分析一下:server上的返还的有关报文信息。
首页相关信息:
在这里插入图片描述
当我们在浏览器输入了服务器网址 192.168.0.102后,我们可以看到服务器端控制台打印出了两个请求报文和响应报文,因为我们在index.html文件中,使用到了图片资源,那么客户端请求主页也会请求这张图片资源。

然后当我们点击下一页后,我们从控制台看到了请求报文的请求行中,资源URL变为了/next.html,因此服务器将此页面传送给client后,client就可以在浏览器中看到想要的页面了。
在这里插入图片描述
接着我们访问一个 不存在的页面next1.html在这里插入图片描述
到此为止,这个简易的HTTP web服务器就完成了。它具有以下的功能点:

  1. 接收并建立与client的连接
  2. 将client所需要的资源给予响应。若是没有确切的资源URL指向,则返还默认index.html文件,否则则将所请求的资源文件返还。若是该文件不存在,则显示404错误
  3. server端可以处理来自server端的HTTP请求,并发送相对应的HTTP响应报文。且这两个报文都可以显示在server端控制台

http_ser.c源文件代码如下:

#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>

#define PATH "/home/song/newlinux/k0923/苏峰 线程/k1017"
/*
create_socket函数负责创建套接字、命名套接字 
和 创建监听队列等操作,最后返回创建成功的socket描述符
*/
int create_socket()
{
	//使用socket函数指定协议族ipv4,并使用字节流传输服务
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    if(sockfd== -1)//若创建失败,则直接将-1返回
    {
        return -1;
    }
    struct sockaddr_in saddr;
    
    memset(&saddr,0,sizeof(saddr));
    saddr.sin_family=AF_INET;
    saddr.sin_port=htons(80);//端口(使用http协议默认的80号端口)
	
	//创建专用socket地址:设置服务器ip
    //saddr.sin_addr.s_addr=inet_addr("192.168.43.72");
    saddr.sin_addr.s_addr=inet_addr("192.168.0.102");

	//命名套接字:将上面创建好的本地专用socket地址绑定到创建的socket文件描述符上
    int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if(res==-1)
    {
        return -1;
    }
    listen(sockfd,5);//创建sockfd的监听队列,设置可以进行连接的队列长度为5
    return sockfd;//将创建好的socket描述符返回,在主程序中进行其余操作
}

int main()
{
    int sockfd=create_socket();//得到创建好的文件描述符
    assert(sockfd!= -1);

    struct sockaddr_in caddr;
    while(1)
    {
        int len=sizeof(caddr);
		//accept接收新的客户连接,得到连接文件描述符
        int c=accept(sockfd,(struct sockaddr*)&caddr,&len);

        if(c<0)
        {
            continue;//没有新连接则继续循环监听,重启server太不划算了
        }
        
        char buff[1024]={0};
        int n=recv(c,buff,1023,0);
        if(n<=0)
        {
            close(c);
            continue;
        }
        printf("read(%s):\n%s\n",inet_ntoa(caddr.sin_addr),buff);
		
		//以下为HTTP请求报文的构造部分
        //解析报文 得到文件名
        char *pname=NULL;
        pname=strtok(buff," ");
        if(NULL==pname)
        {
            close(c);
            continue;
        }
		//得到"方法字段",下面进行打印
        printf("Method:%s\n",pname);

        char *filename=strtok(NULL," ");
        if(NULL==filename)
        {
            close(c);
            continue;
        }

       // send(c,"recv ok",7,0);
        //构造资源URL"部分
        char path[256]={PATH};
        if(strcmp(filename,"/")==0)
        {
			//这里默认返回主页index.html
            strcat(path,"/index.html");
        }
        else
        {
			//不然直接将客端请求的页面返回
            strcat(path,filename);
        }
        
		//下面是处理具体的这个文件:打开资源文件,准备返回给client
        int fd=open(path,O_RDONLY);
        if(fd==-1)//失败 为404错误的响应报文
        {
			char error_buff[256] = { "HTTP/1.1 404 Not Found\r\n" };
			strcat(error_buff, "Server: myHttp\r\n");
			strcat(error_buff, "Content-Length: 3\r\n");
			strcat(error_buff, "\r\n");
			strcat(error_buff, "404\r\n");
			
			// 将错误信息直接返回给client 
			send(connfd, error_buff, strlen(error_buff), 0);
            close(c);
            continue;
        }
        
		//这下面就是 请求成功时的响应报文
        int filesize=lseek(fd,0,SEEK_END);//将文件指针重新定位到文件末尾来获取文件大小
        lseek(fd,0,SEEK_SET);//完成之后,还得将文件指针置回文件起始位置

		//状态行字段:协议版本、状态码、短语.最后有回车和换行(\r\n)
        char head_buff[256]={"HTTP/1.0 200 OK\r\n"};
        strcat(head_buff,"Server:myHttp\r\n");
        //strcat(head_buff,"Content-Length: 10\r\n");
        sprintf(head_buff+strlen(head_buff),"Content-Length: %d\r\n",filesize);
        strcat(head_buff,"\r\n");
        //strcat(head_buff,"HelloSong");

		//将上面响应报文信息在server端进行打印
        printf("send:\n%s\n",head_buff);
		//将响应报文发送给客户端
        send(c,head_buff,strlen(head_buff),0);

		//接下来是将client请求的资源文件(例如html文件,默认为index.html)
		//发送给client,若是html文件则由客端的web浏览器负责解释执行
        char data[512]={0};
        int num=0;
        while((num=read(fd,data,512))>0)
        {
            send(c,data,num,0);
        }
        close(fd);
        close(c);//关闭连接套接字
    }

}

猜你喜欢

转载自blog.csdn.net/weixin_43949535/article/details/102644963