linux服务器HTTP协议内容、实现程序代码

http协议

1. http简介

http协议叫做超文本传输协议。

  • 超文本:传输的不仅文本,还能传输图片、音频、视频等等。
  • 传输:是基于TCP/IP的一个请伴随一个响应方式传输。
  • 协议:无连接、无状态的应用层协议。

2.http工作原理

  • http协议工作于b/s框架上。浏览器作为http客户端和http服务端(web服务器)发送请求,服务端响应之后会关闭连接。
  • web服务器:作为HTTP请求的应答方,主流的三大Web服务器有Apache、Nginx,IIS。
  • 默认端口为80,改成8080或其他也可以。

3.http请求响应过程

例子:URL:http://www.example.com/filepath/index.html

  1. DNS服务器会对浏览器上输入的域名进行映射,先找到http://www.example.com的地址,http客户端会在80端口发起一个到服务器http://www.example.comTCP连接,发送请求报文,报文内容包括对filepath/index.html的请求。
  2. http服务器接受连接和请求,解析报文并检索出对象filepath/index.html,然后从从磁盘或内存中取出数据进行http报文封装并回发,发完就断开连接。
  3. 客户端接收到数据后,也断开连接,并从报文中取出资源文件。

4.http协议特点

  • 无连接:限制每次连接只处理一个请求,服务器处理完客户端的请求并受到客户应答之后就断开连接。这种方式可以节省传输时间。
  • 无状态:对于事务处理没有记忆能力,后续需要前面的消息则需要重传,这样导致每次连接传输的数据量增大,但服务器不需要先前信息时它应答更快。
  • 协议灵活:允许传输任意类型的数据对象,类型用Content-Type标记。
  • 简单快速:协议简单,使得服务器程序规模较小,因此通信速度很快。
  • 也能支持c/s模式。

5.客户端请求报文

如图,报文:请求方法+请求头部+请求数据。
linux服务器HTTP协议内容、实现程序代码

  • 请求方法:如图。linux服务器HTTP协议内容、实现程序代码
  • 请求头部:有四种,通用标头、请求表头、响应标头和实体标头。
  • 例如下图,第一行是请求方法,第二第三第四行都是请求头部及其对应值linux服务器HTTP协议内容、实现程序代码

6.服务器响应报文

如图,报文:状态行+消息报头+空行+响应正文
linux服务器HTTP协议内容、实现程序代码例如linux服务器HTTP协议内容、实现程序代码

7.http的优点和缺点

1. 优点

  • 简单灵活易扩展:协议简单,内容不多,相对*。
  • 应用广泛:应用非常广,可以跨平台、跨语言开发。
  • 无连接、无状态:不需要额外的资源来记录状态信息。实现上相对简单,并且能够减轻服务的负担。

2. 缺点

  • 无连接、无状态:没有记忆性,无法支持多个事务操作,每次都要对一下协议信息,增加了不必要的数据传输,因此出现了cookie。
  • 明文传输:使用可阅读的文本形式,可以用wireshark或tcpdump直接抓包并直接修改,容易被攻击,而且不安全,同时并不能判断通信双方的身份,也不能判断报文是否被更改,因此诞生了https。

3.性能

  • 一般,现在互联网是移动和高并发,不能保证连接质量,tcp层面上HTTP协议有时候会表现得不好。
  • 请求- 应答”会导致“对头阻塞”效应,就是当一个请求阻塞时,后面的请求序列也都会阻塞住,客户端得不到响应。

程序实现

这里程序使用epoll反应堆模型作为框架实现,reactor内容可以在我的博客里面查看。如果单纯想看了解http协议可以前往我主页查看另一篇博客关于tinyhttp开源项目。

下面是用epoll反应堆reactor(C1000K)模型下实现http服务器的介绍~

基于reactor模型的HTTP服务器

客户端结构体

struct qsevent{
    int fd;				//clientfd
    int events;			//事件:读、写或异常
    int status;			//是否位于epfd红黑监听树上
    void *arg;			//参数
    long last_active;	//上次数据收发的事件

    int (*callback)(int fd, int event, void *arg);	//回调函数,单回调,后面修改成多回调
    unsigned char buffer[MAX_BUFLEN];				//数据缓冲区
    int length;										//数据长度

    /*http param*/
    int method;						//http协议请求头部
    char resource[MAX_BUFLEN];		//请求的资源
    int ret_code;					//响应状态码
};

int http_response(struct qsevent *ev)

当客户端发送tcp连接时,服务端的listenfd会触发输入事件会调用ev->callback即accept_cb回调函数响应连接并获得clientfd,连接之后,http数据报文发送上来,服务端的clientfd触发输入事件会调用ev->callback即recv_cb回调函数进行数据接收,并解析http报文。

int http_request(struct qsevent *ev)
{
    char linebuf[1024] = {0};//用于从buffer中获取每一行的请求报文
    int idx = readline(ev->buffer, 0, linebuf);//读取第一行请求方法,readline函数,后面介绍
    if(strstr(linebuf, "GET"))//strstr判断是否存在GET请求方法
    {
        ev->method = HTTP_METHOD_GET;//GET方法表示客户端需要获取资源

        int i = 0;
        while(linebuf[sizeof("GET ") + i] != ' ')i++;//跳过空格
        linebuf[sizeof("GET ") + i] = '\0';
        sprintf(ev->resource, "./%s/%s", HTTP_METHOD_ROOT, linebuf+sizeof("GET "));//将资源的名字以文件路径形式存储在ev->resource中
        printf("resource:%s\n", ev->resource);//回显
    }
    else if(strstr(linebuf, "POST"))//POST的请求方法,暂时没写,方法差不多
    {}
    return 0;
}

int http_response(struct qsevent *ev)

服务器对客户端的响应报文数据进行http封装储存在buffer中,事件触发时在send_cb回调函数发送给客户端。详细解释请看代码注释。

int http_response(struct qsevent *ev)
{
    if(ev == NULL)return -1;
    memset(ev->buffer, 0, MAX_BUFLEN);//清空缓冲区准备储存报文

    printf("resource:%s\n", ev->resource);//resource:客户端请求的资源文件,通过http_reques函数获取
    int filefd = open(ev->resource, O_RDONLY);//只读方式打开获得文件句柄
    if(filefd == -1)//获取失败则发送404 NOT FOUND
    {
        ev->ret_code = 404;//404状态码
        ev->length = sprintf(ev->buffer,//将下面数据传入ev->buffer
        					 /***状态行***/
        					 /*版本号 状态码 状态码描述 */
                             "HTTP/1.1 404 NOT FOUND\r\n"
                             /***消息报头***/
                             /*获取当前时间*/
                             "date: Thu, 11 Nov 2021 12:28:52 GMT\r\n"
                             /*响应正文类型;              编码方式*/
                             "Content-Type: text/html;charset=ISO-8859-1\r\n"
                             /*响应正文长度          空行*/
                             "Content-Length: 85\r\n\r\n"
                             /***响应正文***/
                             "<html><head><title>404 Not Found</title></head><body><H1>404</H1></body></html>\r\n\r\n");
    }
    else 
    {
        struct stat stat_buf;			//文件信息
        fstat(filefd, &stat_buf);		//fstat通过文件句柄获取文件信息
        if(S_ISDIR(stat_buf.st_mode))	//如果文件是一个目录
        {

            printf(ev->buffer, //同上,将404放入buffer中
                   "HTTP/1.1 404 Not Found\r\n"
                   "Date: Thu, 11 Nov 2021 12:28:52 GMT\r\n"
                   "Content-Type: text/html;charset=ISO-8859-1\r\n"
                   "Content-Length: 85\r\n\r\n"
                   "<html><head><title>404 Not Found</title></head><body><H1>404</H1></body></html>\r\n\r\n" );

        } 
        else if (S_ISREG(stat_buf.st_mode)) //如果文件是存在
        {

            ev->ret_code = 200;		//200状态码

            ev->length = sprintf(ev->buffer, //length记录长度,buffer储存响应报文
                                 "HTTP/1.1 200 OK\r\n"
                                 "Date: Thu, 11 Nov 2021 12:28:52 GMT\r\n"
                                 "Content-Type: text/html;charset=ISO-8859-1\r\n"
                                 "Content-Length: %ld\r\n\r\n", 
                                 stat_buf.st_size );//文件长度储存在stat_buf.st_size中

        }
        return ev->length;//返回报文长度
    }
}

git clone代码

自行git clone git clone https://github.com/qiushii/reactor.git

上一篇:一文了解什么是websocket协议(附linux下C实现程序)


下一篇:js中的防抖和节流