使用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.mzph.cn/news/780668.shtml

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

相关文章

品质领航,流量赋能,2024喜尔康浙江省经销商培训会在喜尔康总部成功举行

3月29日&#xff0c;以“新零售、新流量、新风口”为主题的2024喜尔康浙江省经销商培训会在喜尔康总部正式开始举办。活动旨在智能新时代赋能经销商伙伴&#xff0c;通过抓住行业智能化风口&#xff0c;实现喜尔康与经销商的共赢&#xff0c;决胜未来新零售商机。 喜尔康始终致…

Charles for Mac 强大的网络调试工具

Charles for Mac是一款功能强大的网络调试工具&#xff0c;可以帮助开发人员和测试人员更轻松地进行网络通信测试和调试。以下是一些Charles for Mac的主要特点&#xff1a; 软件下载&#xff1a;Charles for Mac 4.6.6注册激活版 流量截获&#xff1a;Charles可以截获和分析通…

nuxt学习

一、遇到的问题 1、nuxt初始化失败问题解决方案 使用npm和pnpm初始化都失败 原因&#xff1a;主机连不上DNS服务器 解决方案 Step1: 打开文件夹 Windows:路径&#xff1a;C:\Windows\System32\drivers\etc Mac: 路径&#xff1a;/etc/hosts Step2: 使用记事本方式打开 …

44 el-dialog 的 appendToBody 属性, 导致 vue 响应式失效

前言 我们经常会碰到 一些 模型和视图 不同步的问题 通常意义上 主要的问题为 列表的某响应式数据更新着更新着 后面就变成非响应式对象了, 然后 就造成了 数据一直在更新, 但是 视图的渲染后面就未渲染了, 这是一个由于 模型上的问题 导致的数据的不在响应式更新 又或者 是…

【倪琴神品品鉴】全新倪诗韵神品级古琴

倪琴朱砂神品仲尼&#xff0c;仅此放漏一张&#xff1b;龙池侧签海门倪诗韵制&#xff0c;雁足上方刻“雷音琴坊”方章&#xff0c;凤沼下方有随形章“神品”二字&#xff1b;老木材纹理竖直&#xff0c;共振良好&#xff0c;是难得的佳器&#xff1b;附带倪老师亲笔签名收藏证…

图扑数字孪生智慧城市,综合治理一屏统览

现代城市作为一个复杂系统&#xff0c;牵一发而动全身&#xff0c;城市化进程中产生新的矛盾和社会问题都会影响整个城市系统的正常运转。智慧城市是应对这些问题的策略之一。领导曾在中央城市工作会议上指出&#xff0c;城市工作要树立系统思维&#xff0c;从构成城市诸多要素…

Hyper-V 虚拟机设置静态 IP 和外网访问

文章目录 环境说明1 问题简介2 解决过程 环境说明 宿主机操作系统&#xff1a;Windows 11 专业版漏洞复现操作系&#xff1a;debian-live-12.5.0-amd64-standard 1 问题简介 在 Windows 上用自带的 Hyper-V 虚拟机管理应用创建了一个 Debian 12 虚拟机&#xff0c;配置静态 IP…

windows安装Chocolatey

其实官网就有介绍&#xff0c;贴上原址&#xff1a; Chocolatey Software | Installing Chocolatey 安装步骤&#xff1a; 1、winX选择Windows Powershell(管理员) 2、复制以下指令 Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]:…

接口自动化框架搭建(九):接入钉钉消息通知

1&#xff0c;jenkins安装钉钉插件 2&#xff0c;在钉钉群聊设置机器人 3&#xff0c;jenkins配置钉钉 根据情况选择&#xff1a; 除了这些&#xff0c;其他不用配置&#xff0c;配置完成点击确认 4&#xff0c;项目配置 添加后保存 5&#xff0c;测试下效果 构建完成后&a…

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

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

从供方协议管理到外部供方管理

从GJB 5000A的供方协议管理到GJB 5000B的外部供方管理&#xff0c;军用软件的研制对承接单位有了更高的标准和要求&#xff0c;也对外部供方管理有了更改的要求&#xff0c;让我们看看具体的变化吧&#xff01; 供方协议管理的目的&#xff1a; 管理供方产品的获取工作。 外部…

FL Studio21.2.3中文版音乐制作编曲软件功能展示讲解

FL Studio 21&#xff0c;确实被广大音乐制作人亲切地称为“水果”。这款软件以其强大的功能和用户友好的界面在音乐制作领域占据了重要地位。 FL Studio 21&#xff08;水果&#xff09;是一款全能的音乐创作软件&#xff0c;也是一款强大的编曲软件&#xff0c;可以作为编曲…

【Linux】体验一款开源的Linux服务器运维管理工具

今天为大家介绍一款开源的 Linux 服务器运维管理工具 - 1panel。 一、安装 根据官方那个提供的在线文档&#xff0c;这款工具的安装需要执行在线安装&#xff0c; # Redhat / CentOScurl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start…

代码随想录算法训练营DAY11|C++栈和队列Part.2|LeetCode:20.有效的括号、 1047.删除字符串中所有相邻重复项、150.逆波兰表达式

文章目录 20.有效的括号思路CPP代码 1047.删除字符串中所有相邻重复项思路CPP代码 150.逆波兰表达式思路什么是逆波兰表达式本题的思路 CPP代码 20.有效的括号 力扣题目链接 文章链接&#xff1a;20.有效的括号 视频链接&#xff1a;LeetCode&#xff1a;20. 有效的括号 状态&a…

你在测试金字塔的哪一层(下)

​在《你在测试金字塔的哪一层&#xff08;上&#xff09;》中介绍了自动化测试的重要性以及测试金字塔。测试金字塔分为单元测试、服务测试、UI测试&#xff0c;它们分别是什么呢&#xff1f;本期文章让我们一起详细看看测试金字塔的不同层次。 一、单元测试 单元测试是指对程…

web使其盒子向里面倾斜

右侧向内倾斜 transform: perspective(500px) rotateX(0deg) rotateY(-10deg) rotateZ(0deg);transform-origin: 270px 424px;效果 左侧向内倾斜 transform: perspective(500px) rotateX(0deg) rotateY(10deg) rotateZ(0deg);transform-origin: 270px 424px;效果

蓝桥杯小白月赛第八场第三题

题目描述&#xff1a; 思路&#xff1a; 根据上面的次方数&#xff0c;我们可以看出来从1次方到4次方 和 5 - 8次方&#xff0c;中间有什么规律&#xff1f; 是不是可以看出来1次方和5次方的尾数相同 2次方和6次方的尾数相同 3次方和7次方的尾数相同 4次方和8次方的尾数相同 …

2024年N1叉车司机证考试题库及N1叉车司机试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年N1叉车司机证考试题库及N1叉车司机试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机出的…

Kaggle注册验证码问题(Captcha must be filled out.)

Kaggle注册验证码问题 Captcha must be filled out.使用Edge浏览器 Header Editor 插件安装 下载插件Header Editor 导入重定向脚本 点击扩展插件&#xff0c; 打开Header Editor插件&#xff0c;进行管理 点击导入输入下载链接进行下载或者导入本地json文件(二者任选其一…

QT 最近使用的项目配置文件

目录 1 QT 最近使用的项目配置文件所在路径 2 QtCreator.ini 1 QT 最近使用的项目配置文件所在路径 C:\Users\your username\AppData\Roaming\QtProject QtCreator.ini最好先备份一份 2 QtCreator.ini ProjectExplorer 下面的 RecentProjects\FileNames RecentProjects\…