Linux之HTTP服务器的构建

在这里插入图片描述

欢迎诸位来阅读在下的博文~
在这里,在下会不定期发表一些浅薄的知识和经验,望诸位能与在下多多交流,共同努力!
江山如画,客心如若,欢迎到访,一展风采

参考环境

  1. VMware Workstation Pro
  2. Ubuntu20.04(运行服务器)

参考书籍

《Linux C/C++ 服务器开发实践》——朱文伟 李建英

一、HTTP的工作原理

HTTP(HyperText Transfer Protocol,超文本传输协议)是互联网上应用最广泛的协议之一,它定义了客户端(通常是浏览器)和服务器之间交换数据的规则。浏览器作为HTTP客户端通过URL向HTTP服务器端,即Web服务器发送所有请求。常见的Web服务器有:Apache服务器、IIS服务器等。
其模型如下:
在这里插入图片描述

以下是 HTTP 的工作原理的简化概述:

1. 建立连接

  • 客户端发起请求:用户通过浏览器或其他客户端软件输入 URL 或点击链接,客户端软件解析 URL 并向服务器发起连接请求。
  • DNS解析:客户端首先通过 DNS(Domain Name System)将域名解析为服务器的 IP 地址。
  • TCP连接:客户端通过 TCP(Transmission Control Protocol)与服务器建立连接。HTTP 通常使用 TCP 的 80 端口,但是也可以改为8080或者其他端口。

2. 发送请求

  • 请求行:客户端发送一个 HTTP 请求,通常包括请求方法(如 GET、POST)、请求的 URL 和 HTTP 版本(如 HTTP/1.1 或 HTTP/2)。
  • 请求头:请求还包括一系列的请求头,如 Host(指定服务器的主机名)、User-Agent(标识客户端类型)、Accept(指定客户端可接受的响应类型)等。
  • 请求体(可选):如果请求方法(如 POST)需要发送数据,请求体将包含这些数据。

3. 服务器处理请求

  • 服务器接收请求:服务器接收到请求后,根据请求方法、URL 和其他信息处理请求。
  • 资源定位:服务器确定请求的资源位置,这可能是静态文件或需要动态生成的数据。
  • 处理请求:如果是动态请求,服务器可能需要执行数据库查询、业务逻辑处理等。

4. 发送响应

  • 状态行:服务器发送一个 HTTP 响应,包括 HTTP 版本、状态码(如 200 表示成功)和状态短语(如 “OK”)。
  • 响应头:响应还包括一系列的响应头,如 Content-Type(指定响应内容的类型)、Content-Length(指定响应内容的长度)等。
  • 响应体:这是服务器返回给客户端的实际数据,可以是 HTML 页面、图片、视频等。

5. 断开连接

  • 关闭连接:一旦响应被客户端接收,TCP 连接通常会被关闭。对于 HTTP/1.1,如果请求头中包含 Connection: keep-alive,则连接可能会被保持打开状态,以便后续请求重用。

6. 客户端处理响应

  • 解析响应:客户端软件解析响应内容,并根据内容类型进行相应的处理(如渲染 HTML 页面、显示图片等)。
    HTTP 是无状态的,这意味着每个请求都是独立的,服务器不会保存关于客户端的任何信息(除非使用 cookie 或其他机制)。这使得 HTTP 适用于简单的请求-响应模型,但也有一些局限性,比如在需要维护会话状态的应用中。

二、客户端请求的消息结构

客户端发送一个HTTP请求到服务器,改请求消息由请求行(状态行)、请求头部(首部行)、空行和请求数据(实体)四个部分组成,如图所示
在这里插入图片描述

注:sp 指空格,cr指回车符,lf指换行符

HTTP协议定义了8种请求方法,来表明对Request-URI指定资源的不同操作方式,具体如下:

  1. GET:请求获取由Request-URI标识的资源。它通常用于请求服务器发送一个文件或数据。
  2. POST:用于向服务器提交数据,通常用于提交表单数据或上传文件。POST请求可能会导致服务器上的状态改变或副作用。
  3. PUT:用于将请求中的数据存储在服务器上。它通常用于更新资源,但如果资源不存在,可能会创建一个新的资源。
  4. DELETE:请求服务器删除由Request-URI标识的资源。
  5. HEAD:与GET方法类似,但服务器在响应中只返回头部信息,不返回实体主体(即不返回实际内容)。
  6. OPTIONS:用于获取服务器支持的HTTP请求方法和其他选项信息。
  7. TRACE:用于沿着到目标资源的路径执行消息回环测试,主要用于诊断和调试。
  8. CONNECT:用于建立一个隧道,将连接转换为透明的TCP/IP通道,通常用于SSL加密的连接。

以下是这些方法的归纳:

  • GET:检索数据。
  • POST:提交数据。
  • PUT:更新或创建数据。
  • DELETE:删除数据。
  • HEAD:检查数据的存在性或状态。
  • OPTIONS:查询服务器能力。
  • TRACE:跟踪请求链。
  • CONNECT:建立隧道。

虽然HTTP的请求方式有8种,但是在实践中常用到的是GET和POST,其他方法可以通过这两种基本方式间接实现。

三、服务器响应的消息结构

HTTP响应由四个部分组成,分别是:状态行、消息报头(响应头)、空行和响应正文,如图:
在这里插入图片描述

在这里插入图片描述

四、HTTP状态码

当浏览者访问一个网页时,浏览器会向网页所在服务器发出请求。在浏览器接收并显示网页前,此网页所在的服务器会返回一个包含HTTP状态码的信息头用以响应浏览器的请求。
HTTP状态码的英文时HTTP Status Code。下面时常见的HTTP状态码:
200 - 请求成功
301 - 资源(网页等)被永久转移到其它URL
404 - 请求的资源(网页等)不存在
500 - 内部服务器错误

类型 描述
1** 信息,服务器收到请求,需要请求者继续执行操作
2** 成功,操作被成功接收并处理
3** 重定向,需要进一步的操作以完成请求
4** 客户端错误,请求包含语法错误或无法完成请求
5** 服务器错误,服务器在处理请求的过程中发生了错误

五、从零开始搭建HTTP服务器

现在有很多现成开源的HTTP服务器,比如非常流行的Apache,诸位若想详细了解,可以浏览下面博客一二:
在linux上架设Web服务器Apache(Ubuntu)
Linux系统的Apache2如何启动cgi模块(Ubuntu)

但是,我今天打算自己手动搭建一个,诸位若有兴趣,也可以一起来吧~

main.c

#include "httpserver.h"

int main(void)
{
   
    int server_sock = -1;
    u_short port = 8888;
    int client_sock = -1;
    struct sockaddr_in client_name;
    socklen_t client_name_len = sizeof(client_name);
    pthread_t newthread;

    server_sock = startup(&port);
    printf("httpd running on port %d\n", port);

    while (1)
    {
   
        client_sock = accept(server_sock,
                       (struct sockaddr *)&client_name,
                       &client_name_len);
        if (client_sock == -1)
        error_die("accept");
        /* accept_request(client_sock); */
        if (pthread_create(&newthread , NULL, (void*(*)(void*))accept_request, (void*)(intptr_t)client_sock) != 0)
        perror("pthread_create");
    }

    close(server_sock);

    return(0);
}

httpserver.h

#ifndef _HTTPSERVER_H_
#define _HTTPSERVER_H_
#include "myhead.h"

#define ISspace(x) isspace((int) (x))

#define SERVER_STRING "Server: jdbhttpd/0.1.0\r\n"

//处理从套接字上监听到的一个HTTP请求,此函数很大部分体现服务器处理请求流程
void accept_request(void*);

//返回给客户端这是一个错误请求
void bad_request(int);

//读取服务器上某个文件,写道套接字中
void cat(int, FILE *);

//处理发生在执行cgi程序事出现的错误
void cannot_execute(int);

//打印错误信息,并退出
void error_die(const char *);

//运行cgi程序的处理,是主要的函数
void execute_cgi(int, const char *, const char *, const char *);

//读取套接字的一行,把回车符换行符等情况都统一为换行符结束
int get_line(int, char *, int);

//把HTTP响应头部写到套接字中
void headers(int, const char *);

//处理找不到请求文件的情况,即发送404
void not_found(int);

//读取文件并返回给客户端,里面调用cat
void serve_file(int, const char *);

//初始化http服务,包括建立套接字、绑定端口、进行监听等。
int startup(u_short *);

//返回浏览器,表明method不支持
void unimplemented(int);

#endif

httpserver.c

#include "myhead.h"
#include "httpserver.h"

int startup(u_short *port)
{
   
    int httpd = 0;
    struct sockaddr_in name;

    httpd = socket(PF_INET, SOCK_STREAM, 0);
    if (httpd == -1)
        error_die("socket");
    memset(&name, 0, sizeof(name));
    name.sin_family = AF_INET;
    name.sin_port = htons(*port);
    name.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
        error_die("bind");
    if (*port == 0) /* if dynamically allocating a port */
    {
   
        socklen_t namelen = sizeof(name);
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值