使用C语言实现Linux下的并发Http服务器

使用C语言实现Linux下的并发Http服务器

文章目录

  • 使用C语言实现Linux下的并发Http服务器
    • 先备知识
    • Http协议
        • 请求格式:
          • 客户端请求
          • 服务端响应
        • Demo
    • 实现Mini的Http服务器流程
    • 接收Http请求
      • 实现按行读取请求头部
      • 请求头部的结束
    • 解析请求
    • 响应请求
    • 读取文件(http需要发送html下的html文件
      • stat函数
    • 响应文件的头部
    • 发送body
    • 错误访问
            • 404
            • 500
            • 400
            • 501
    • 一些待实现的函数
    • 所需头文件
    • 完整代码与讨论
  • Q:723550115

先备知识

  • 熟悉Linux的网络编程

  • 熟悉C语言的使用

  • 熟悉Get等Http请求

*html*,全称Hypertext Markup Language,也就是“超文本链接标示语言”。HTML文本是由 HTML命令组成的描述性文本,HTML 命令可以说明文字、 图形、动画、声音、表格、链接等。 即平常上网所看到的的网页。

Http协议

HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

请求格式:
客户端请求

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。

在这里插入图片描述

服务端响应

服务器响应客户端的HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

请添加图片描述

内容响应代号代号描述
服务器上存在请求的内容,并可以响应给客户端200OK
客户端的请求有异常,方法有问题501Method Not Implemented
服务器收到请求后,因为自生的问题没法响应500Internal Server Error
请求的内容不存在404NOT FOUND
客户端发送的请求格式有问题等400BAD REQUEST
Demo
浏览器请求:
GET /demo.html HTTP/1.1
Host: 47.100.162.**
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.26 Safari/537.36 Core/1.63.6788.400 QQBrowser/10.3.2767.400
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie:cna=BT0+EoVi1FACAXH3Nv5I7h6k;isg=BIOD99I03BNYvZDfE2FJUOsMB0ftUBZcBFi4E7VgYOJZdKOWPcvRinAl6kSfVG8y
服务器响应:
HTTP/1.0 200 OK
Server: Martin Server
Content-Type: text/html
Connection: Close
Content-Length: 526---一下为content-----

实现Mini的Http服务器流程

请添加图片描述

接收Http请求

实现按行读取请求头部

inline int get_line(int , char *, int );

//返回值: -1 表示读取出错, 等于0表示读到一个空行, 大于0 表示成功读取一行

int get_line(int sock, char *buf, int size)
{int count = 0;int len = 0;char ch = '\0';while (count < (size - 1) && ch != '\n'){len = read(sock, &ch, 1);if (len == 1){if (ch == '\r'){continue;}else if (ch == '\n'){break;}buf[count] = ch;count++;}else if (len == -1){perror("read faild");count = 0;break;}else{fprintf(stderr, "client close the connect \n");count = 0;break;}}if (count >= 0)buf[count] = '\0';return count;
}

请求头部的结束

*如果碰到两个连续的回车换行,即,意味着请求头部结束*

解析请求

返回值和参数都使用void* 方便后续使用多线程实现并发功能

void * do_http_request(void * pclient_sock)
{int len;char buf[512];char url[256];char method[64];int client_sock = *(int *)pclient_sock;len =  get_line(client_sock, buf, sizeof(buf));// 读取客户端的请求// 读取请求行// printf("%s\n", buf);if (buf == '\0'){if (debug){printf("No request line ,The buff is null\n");}}if (debug)printf("The len is %d\n", len);if (len > 0){int i = 0, j = 0;while (!isspace(buf[j]) && (i < (sizeof(method) - 1))){method[i] = buf[j];i++;j++;}method[i] = '\0';// printf("the method is \t:%s\n", method);if (strncasecmp(method, "GET", i) == 0){// 处理GET请求if (debug)printf("method = GET\n");while (isspace(buf[j++])) // 跳过白空格i = 0;while (!isspace(buf[j]) && (i < (sizeof(url) - 1))){url[i] = buf[j];i++;j++;}url[i] = '\0';if (debug)printf("The url is\t:%s\n", url);do{len = get_line(client_sock, buf, sizeof(buf));if (debug)printf("read: %s\n", buf);/* code */} while (len > 0);/* 定位本地服务器的html文件*///{char *pos = strchr(url, '?');if (pos){*pos = '\0';printf("real url: %s\n", url);}}// 读取本地文件char path[512];sprintf(path, "./html_docs/%s", url);if (debug)printf("path is %s\n", path);struct stat st;// 读取文件if (stat(path, &st) == -1){fprintf(stderr, "stat %s failed,reson :%s \n", path, strerror(errno));fprintf(stderr, "warning! file not found\n");// 返回404not_found(client_sock);}else{// 文件存在if (S_ISDIR(st.st_mode)){strcat(path, "/index.html");// 返回目录fprintf(stderr, "warning! file is a dir\n");// 返回404}do_http_response(client_sock, path);}}else{fprintf(stderr, "warning! other request[%s]\n", method);// 处理其他请求if (debug)printf("method != GET\n");// read http header ,and send error code to the client// 响应客户都 501 the method is not defineddo{len = get_line(client_sock, buf, sizeof(buf));if (debug)printf("read: %s\n", buf);} while (len > 0);// unimplemented(client_sock);//在响应时实现}// 读取Http的头部}else{// request errorbad_request(client_sock);}close(client_sock);if(pclient_sock) free(pclient_sock);//释放动态分配的内存return NULL;
}

响应请求

void do_http_response(int client_sock, const char *path)
{FILE *resource = NULL;resource = fopen(path, "r");if (resource == NULL){not_found(client_sock);return;}int size = fseek(resource, 0, SEEK_END);// 发送http头部send_headers(client_sock, resource);// 发送http_bodysend_body(client_sock, resource);fclose(resource);return;
}

读取文件(http需要发送html下的html文件

文件概念简介

请添加图片描述

inode - “索引节点”,储存文件的元信息,比如文件的创建者、文件的创建日期、文件的大小等等。每个inode都有一个号码,操作系统用inode号码来识别不同的文件。ls -i 查看inode 号

stat函数

****作用:****返回文件的状态信息

头文件

#include <sys/types.h>#include <sys/stat.h>#include <unistd.h>

函数体

int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);

path:

​ 文件的路径

buf:

​ 传入的保存文件状态的指针,用于保存文件的状态

返回值:

​ 成功返回0,失败返回-1,设置errno

结构体

struct stat {dev_t     st_dev;     /* ID of device containing file */ino_t     st_ino;     /* inode number */mode_t    st_mode;    /* S_ISREG(st_mode)  是一个普通文件  S_ISDIR(st_mode)  是一个目录*/nlink_t   st_nlink;   /* number of hard links */uid_t     st_uid;     /* user ID of owner */gid_t     st_gid;     /* group ID of owner */dev_t     st_rdev;    /* device ID (if special file) */off_t     st_size;    /* total size, in bytes */blksize_t st_blksize; /* blocksize for filesystem I/O */blkcnt_t  st_blocks;  /* number of 512B blocks allocated */time_t    st_atime;   /* time of last access */time_t    st_mtime;   /* time of last modification */time_t    st_ctime;   /* time of last status change */};

响应文件的头部

void send_headers(int client_sock, FILE *fp)
{struct stat st;if (fstat(fileno(fp), &st) == -1){perror("fstat");return;}char buf[1024] = {0};strcpy(buf, "HTTP/1.0 200 OK\r\n");strcat(buf, "Content-Type: text/html; charset=utf-8\r\n");strcat(buf, "Server:Lucifer Web Server\r\n");strcat(buf, "Connection:close\r\n");char tmp[128];sprintf(tmp, "Content-Length: %ld\r\n", st.st_size);strcat(buf, tmp);if (debug){fprintf(stdout, "send_body buf: %s\n", buf);fflush(stdout);}if (send(client_sock, buf, strlen(buf), 0) < 0){sprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}// sendfile(client_sock, fileno(fp), NULL, st.st_size); // 发送文件内容
}

发送body

}void send_body(int client_sock, FILE *fp)
{char buf[1024];fgets(buf, sizeof(buf), fp);while (!feof(fp)){int len = write(client_sock, buf, strlen(buf));if (len < 0){ // 发送body 的过程中出现问题,怎么办?1.重试? 2.fprintf(stderr, "send body error. reason: %s\n", strerror(errno));break;}if (debug)fprintf(stdout, "%s", buf);fgets(buf, sizeof(buf), fp);}
}

错误访问

404
void not_found(int client_sock)
{const char *reply = "HTTP/1.0 404 NOT FOUND\r\n\Content-Type: text/html\r\n\\r\n\<HTML lang=\"zh-CN\">\r\n\<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\<HEAD>\r\n\<TITLE>NOT FOUND</TITLE>\r\n\</HEAD>\r\n\<BODY>\r\n\<P>file not find \r\n\<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent.\r\n\</BODY>\r\n\</HTML>";int len = write(client_sock, reply, strlen(reply));if (debug)fprintf(stdout, reply);if (len <= 0){fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}
500
void iner_error(int client_sock)
{const char *reply = "HTTP/1.0 500 Internal Sever Error\r\n\Content-Type: text/html\r\n\\r\n\<HTML>\<HEAD>\<TITLE>Method Not Implemented</TITLE>\</HEAD>\<BODY>\<P>Error prohibited CGI execution.\</BODY>\</HTML>";int len = write(client_sock, reply, strlen(reply));if (debug)fprintf(stdout, reply);if (len <= 0){fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}
400
void bad_request(int client_sock)
{// 400const char *reply = "HTTP/1.0 400 Bad Request\r\n\Content-Type: text/html\r\n\\r\n\<HTML>\<HEAD>\<TITLE>Bad Request</TITLE>\</HEAD>\<BODY>\<P>Error prohibited CGI execution.\</BODY>\</HTML>\r\n";int len = send(client_sock, reply, strlen(reply), 0);if (len < 0){perror("send");}if (debug)printf("send bad request\n");return;
}
501
void unimplemented(int client_sock)
{const char *reply = "HTTP/1.0 501 Not Implemented\r\n\Content-Type: text/html\r\n\\r\n\<HTML>\<HEAD>\<TITLE>Not Implemented</TITLE>\</HEAD>\<BODY>\<P>Error prohibited CGI execution.\</BODY>\</HTML>";int len = write(client_sock, reply, strlen(reply));if (debug)fprintf(stdout, reply);if (len <= 0){fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}

一些待实现的函数

请添加图片描述

所需头文件

请添加图片描述

完整代码与讨论

Q:723550115

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/578935.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数字图像处理】改变图像灰度级别

改变图像灰度级别 首先&#xff0c;对原始图像 O O O进行灰度级量化: q int ⁡ ( O 2 i ) 2 i , q\operatorname{int}\left(\frac{O}{2^{i}}\right) \times 2^{i}, qint(2iO​)2i, 灰度级别256&#xff0c;128&#xff0c;64&#xff0c;32&#xff0c;16&#xff0c;8&…

【Vue3源码学习】— CH2.6 effect.ts:详解

effect.ts&#xff1a;详解 1. 理解activeEffect1.1 定义1.2 通过一个例子来说明这个过程a. 副作用函数的初始化b. 执行副作用函数前c. 访问state.countd. get拦截器中的track调用e. 修改state.count时的set拦截器f. trigger函数中的依赖重新执行 1.3 实战应用1.4 activeEffect…

【数据结构】堆、堆排序(包你学会的)

文章目录 前言堆&#xff08;Heap&#xff09;1、堆的概念及结构2、堆的分类2.1、小堆的结构2.2、大堆的结构2.3、找到规律并证明 3、堆的实现&#xff08;小堆&#xff09;3.1、堆的结构以及接口3.2、初始化、销毁3.3、交换父子结点&#xff08;后续需要&#xff09;3.4、插入…

代码随想录算法训练营第二十四天| 理论基础,77. 组合

题目与题解 参考资料&#xff1a;回溯法理论基础 带你学透回溯算法&#xff08;理论篇&#xff09;| 回溯法精讲&#xff01;_哔哩哔哩_bilibili 77. 组合 题目链接&#xff1a;​​​​​​​​​​​​​​77. 组合 代码随想录题解&#xff1a;77. 组合 视频讲解&#xff…

c语言中的联合体和枚举

这篇文章总结一下c语言中的联合体和枚举。看看这两个东西到底是什么。大家一起学习。 文章目录 一、联合体1.联合体类型的声明。2.联合体的大小。3.相同成员的结构体和联合体对比4.联合体大小的计算。 二、枚举类型1.枚举类型的声明。2.枚举类型的优点。枚举类型的使用。 一、联…

C++王牌结构hash:哈希表开散列(哈希桶)的实现与应用

目录 一、开散列的概念 1.1开散列与闭散列比较 二、开散列/哈希桶的实现 2.1开散列实现 哈希函数的模板构造 哈希表节点构造 开散列增容 插入数据 2.2代码实现 一、开散列的概念 开散列法又叫链地址法(开链法)&#xff0c;首先对关键码集合用散列函数计算散列地址&…

一文教你如何轻松领取腾讯云优惠券

腾讯云作为国内领先的云计算服务商&#xff0c;为用户提供了丰富的云产品和服务。为了让更多用户享受到腾讯云服务的优质体验&#xff0c;腾讯云推出了各种优惠券&#xff0c;让用户在购买云服务时能够获得更多实惠。本文将为大家详细介绍如何轻松领取腾讯云优惠券&#xff0c;…

智慧公厕,为智慧城市建设注入了新的活力

随着智慧城市的快速发展&#xff0c;公共厕所不再是简单的功能设施&#xff0c;而是成为了提升城市形象、改善民生服务的重要一环。智慧公厕作为新形态的公共厕所&#xff0c;通过精准监测公厕内部的人体活动状态、人体存在状态、空气质量情况、环境变化情况、设施设备运行状态…

使用PopLDdecay软件绘制LD衰减图

前记 PopLDdecay是一款用于进行种群遗传学和关联分析的软件。它可以在全基因组水平上进行基因型数据的相关性和衰减分析&#xff0c;帮助研究人员探索种群间的遗传差异和突变选择的模式。 使用PopLDdecay可以实现以下功能&#xff1a; 遗传距离的计算&#xff1a;可以计算遗…

bugku-web-eval

页面源码 <code><span style"color: #000000"> <span style"color: #0000BB"><?php <br /> </span><span style"color: #007700">include </span><span style"color: #DD0000"&…

37-巩固练习(一)

37-1 if语句等 1、问&#xff1a;输出结果 int main() {int i 0;for (i 0; i < 10; i){if (i 5){printf("%d\n", i);}return 0;} } 答&#xff1a;一直输出5&#xff0c;死循环 解析&#xff1a;i5是赋值语句&#xff0c;不是判断语句&#xff0c;每一次循…

类与对象中C++

加油&#xff01;&#xff01;&#xff01; 文章目录 前言 一、类的6个默认成员函数 ​编辑 二、构造函数 1.概念 三、析构函数 1.概念 2.特性 四、拷贝构造函数 1.概念 2.特征 拷贝构造函数典型调用场景 五、赋值运算符重载 1.运算符重载 2.赋值运算符重载 赋值运算符重载格式…