使用C++从零开始,自己写一个MiniWeb

news2024/11/26 2:49:10

第一步:新建项目

1、打开VS点击创建新项目

2、选择空项目并点下一步(切记不能选错项目类型)

3、填写项目名称和路径,点击创建即可

 

新建好后项目是这样的比较干净 

4、右击源文件,点击添加,新建http.cpp文件

第二步:前期准备

在http.cpp最上面引入依赖,并撰写main方法,打印错误日志的方法

#include<stdio.h>
#include<string.h>
#include<WinSock2.h>
#include<sys/types.h>
#include<sys/stat.h>
#pragma comment(lib,"WS2_32.lib")
#define PRINTF(str) printf("[%s - %d]"#str"%s",__func__,__LINE__,str);

//打印错误日志
void error_die(const char* str) {
	perror(str);
	exit(1);

}



int main(void) {


	return 0;
}

第三步:网络初始化

初始化可以分为五步:1、网络通讯初始化===>>>2、创建套接字===>>>3、绑定端口===>>>4、绑定套接字===>>>5、创建监听队列

代码实现如下:

int startup(unsigned short *port) {

	//1、网络通讯初始化
	WSADATA data;
	int res = WSAStartup(MAKEWORD(1,1), &data);
	if (res) {
		error_die("init fail");
	}
	//2、创建套接字
	int server_socket = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);

	if (server_socket == -1) {
		error_die("sock create fail");
	}

	//3、绑定端口
	int opt = 1;
	res = setsockopt(server_socket,SOL_SOCKET,SO_REUSEADDR,(const char*) & opt, sizeof(opt));

	if (res) {
		error_die("port bing fail");
	}

	//4、绑定套接字
	struct sockaddr_in server_addr;
	memset(&server_addr,0,sizeof(server_addr));
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(*port);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	res = bind(server_socket,(struct sockaddr*) &server_addr, sizeof(server_addr));

	if (res<0) {
		error_die("sock bing fail");
	}

	//5、创建监听队列
	int nameLen = sizeof(server_addr);
	if (*port == 0) {
		res = getsockname(server_socket, (struct sockaddr*)&server_addr,&nameLen);
		if (res) {
			error_die("dynamic sock create fail");
		}

		*port = server_addr.sin_port;
	}


	res = listen(server_socket, 5);
	if (res < 0) {
		error_die("listen queque create fail");
	}
	return server_socket;

};

main方法修改如下:

int main(void) {
	//1、初始化
	unsigned short port = 8000;
	int server_sock = startup(&port);
	printf("http have benn started ,listening [%d] port...",port);

	return 0;
}

第四步:处理用户请求

1、报文背景知识

浏览器发起新的访问时,会向服务器端发送一个请求报文。例如,在浏览器地址输入 127.0.0.1:8000 回车后,服务器端收到的完整报文如下:

GET / HTTP/1.1\n
Host: 127.0.0.1:8000\n
Connection: keep-alive\n
Cache-Control: max-age=0\n
Upgrade-Insecure-Requests: 1\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\n
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\n
Sec-Fetch-Site: none\n
Sec-Fetch-Mode: navigate\n
Sec-Fetch-User: ?1\n
Sec-Fetch-Dest: document\n
Accept-Encoding: gzip, deflate, br\n
Accept-Language: zh-CN,zh;q=0.9\n
\n

请求报文由4四个部分组成:请求行、请求头部行、空行、请求数据。具体格式如下:

2、具体处理

具体处理代码如下:

//从指定的客户端套接字读取一行数据,保持到buff中,返回实际读取到了字节数
int get_line(int sock, char* buff, int size) {
	char c = 0;
	int i = 0;
	while (i < size - 1 && c != '\n') {
		int n = recv(sock, &c, 1, 0);
		if (n > 0) {
			if (c == '\r') {
				n = recv(sock, &c, 1, MSG_PEEK);
				if (n > 0 && c == '\n') {
					recv(sock, &c, 1, 0);
				}
				else {

					c = '\n';
				}
			}
			buff[i++] = c;
		}
		else {

			c = '\n';
		}
	}
	buff[i] = 0;
	return 0;
}

//向指定套接字,发送一个未支持提示还没有实现的错误页面
void unimplement(int client) {
	
char buf[1024];
 
    sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
 
    sprintf(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</TITLE></HEAD>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);

}

//向指定套接字,发送一个未支持提示还没有实现的错误页面
void not_found(int client) {
	
char buf[1024];
 
    sprintf(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, SERVER_STRING);
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "Content-Type: text/html\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<HTML><TITLE>Not Found</TITLE>\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "<BODY><P>The server could not fulfill\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "your request because the resource specified\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "is unavailable or nonexistent.\r\n");
    send(client, buf, strlen(buf), 0);
    sprintf(buf, "</BODY></HTML>\r\n");
    send(client, buf, strlen(buf), 0);

}




//发送响应的头信息
void headers(int client) {
	
	char buff[1024];
	strcpy(buff, "HTTP/1.0 200 OK\r\n");
	send(client, buff, strlen(buff), 0);


	strcpy(buff, "Server:MyHttpd/0.1\r\n");
	send(client, buff, strlen(buff), 0);

	strcpy(buff, "Content-type:text/html\n");
	send(client, buff, strlen(buff), 0);


	strcpy(buff, "\r\n");
	send(client, buff, strlen(buff), 0);

}


//发送文件
void cat(int client,FILE* resource) {
	char buff[4096];
	int count = 0;
	while (1) {
		int ret = fread(buff, sizeof(char), sizeof(buff), resource);
		if (ret <= 0) {
			break;
		}
		send(client, buff, ret, 0);
		count += ret;
	}
	printf("total send [%d] to client\n",count);
}




void server_file(int client,const char* fileName) {
	char numchars = 1;
	char buff[1024];
	while (numchars > 0 && strcmp(buff, "/n")) {
		numchars = get_line(client, buff, sizeof(buff));
		PRINTF(buff);
	}

	FILE*  resource = fopen(fileName,"r");

	if (resource==NULL) {
		not_found(client);
	}
	else {
		//发送头信息
		headers(client);
		//发送文件
		cat(client, resource);

		printf("file send success");

	}
	fclose(resource);

}



DWORD WINAPI accept_request(LPVOID arg) {
	char buff[1024];
	int client = (SOCKET)arg;
	//1、获取第一行
	int numchars = get_line(client, buff,sizeof(buff));
	PRINTF(buff);
	
	char method[255];
	int j = 0 ,i =0;
	while (!isspace(buff[j])&&i < sizeof(method)-1) {
		method[i++] = buff[j++];
	}
	method[i] = 0;
	PRINTF(method);


	//2、检查请求方法是否支持
	if (stricmp(method,"GET")&& stricmp(method, "POST")) {
		//向浏览器返回错误提示页面
		unimplement(client);
		return 0;
	}


	//3、解析资源路径
	char url[255];
	i = 0;
	while (isspace(buff[j]) && j < sizeof(buff)) {
		j++;
	}
	while (!isspace(buff[j])&& sizeof(url)-1 && j < sizeof(buff)) {
		url[i++] = buff[j++];
	}
	url[i] = 0;
	PRINTF(url);

	char path[512] = "";
	sprintf(path, "htdocs%s", url);
	if (path[strlen(path)-1]=='/') {
		strcat(path, "index.html");

	}
	PRINTF(path);

	struct stat status ;
	if (stat(path,&status)==-1) {
		//把请求包里的东西读完
		while (numchars>0&&strcmp(buff,"/n")) {
			numchars = get_line(client, buff, sizeof(buff));
		}
		PRINTF(buff);
		not_found(client);
	}else {
		if ((status.st_mode & S_IFMT)==S_IFDIR) {
			strcat(path, "index.html");
		}
		server_file(client,path);
	}

	closesocket(client);

	return 0;
}

github地址:

https://github.com/1756336885/miniWeb.git

gitee地址:

miniWeb: 迷你版的web,用C++撰写,后期会添加数据库,中间件相关的操作

参考文章:

2-创建项目_哔哩哔哩_bilibili

C语言手写HTTPD网站服务器_126775241csdn-CSDN博客

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

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

相关文章

【华为云】容灾方案两地三中心实践理论

应用上云之后&#xff0c;如何进行数据可靠性以及业务连续性的保障是非常关键的&#xff0c;通过华为云云上两地三中心方案了解相关方案认证地址&#xff1a;https://connect.huaweicloud.com/courses/learn/course-v1:HuaweiXCBUCNXI057Self-paced/about当前内容为灾备常见理论…

代码随想录 Leetcode55. 跳跃游戏

题目&#xff1a; 代码(首刷自解 2024年2月9日&#xff09;&#xff1a; class Solution { public:bool canJump(vector<int>& nums) {int noz 0;for (int i nums.size() - 2; i > 0; --i) {if (nums[i] 0) {noz;continue;} else {if (nums[i] > noz) noz …

【Java八股面试系列】JVM-class文件结构

Class文件结构总结 根据 Java 虚拟机规范&#xff0c;Class 文件通过 ClassFile 定义&#xff0c;有点类似 C 语言的结构体。我们之前都是使用javap命令来对字节码文件进行反编译查看的&#xff0c;我们可以使用WinHex软件&#xff08;Mac平台可以使用010 Editor&#xff09;来…

假期day7

设计qq界面 代码 ui->lab1->setPixmap(QPixmap(":/pictrue/denglu.webp"));ui->lab1->setScaledContents(true);ui->lab2->setPixmap(QPixmap(":/pictrue/passwd.jpg"));ui->lab2->setScaledContents(true);ui->lab3->setP…

Elasticsearch:使用查询规则(query rules)进行搜索

在之前的文章 “Elasticsearch 8.10 中引入查询规则 - query rules”&#xff0c;我们详述了如何使用 query rules 来进行搜索。这个交互式笔记本将向你介绍如何使用官方 Elasticsearch Python 客户端来使用查询规则。 你将使用 query rules API 将查询规则存储在 Elasticsearc…

Java奠基】对象数组练习

目录 商品对象信息获取 商品对象信息输入 商品对象信息计算 商品对象信息统计 学生数据管理实现 商品对象信息获取 题目要求是这样的&#xff1a; 定义数组存储3个商品对象。 商品的属性&#xff1a;商品的id&#xff0c;名字&#xff0c;价格&#xff0c;库存。 创建三个…

Pytorch卷积层原理和示例 nn.Conv1d卷积 nn.Conv2d卷积

内容列表 一&#xff0c;前提 二&#xff0c;卷积层原理 1.概念 2.作用 3. 卷积过程 三&#xff0c;nn.conv1d 1&#xff0c;函数定义&#xff1a; 2, 参数说明: 3,代码: 4, 分析计算过程 四&#xff0c;nn.conv2d 1, 函数定义 2, 参数&#xff1a; 3, 代码 4, 分析计算过程 …

Netty应用(七) 之 Handler Netty服务端编程总结

目录 15.Handler 15.1 handler的分类 15.1.1 按照方向划分 15.1.2 handler的结构 15.2 输入方向ChannelInboundHandlerAdapter 15.2.1 输出方向Handler的顺序 15.2.2 多个输入方向Handler之间的数据传递 15.2.2.1 handler消失了 15.2.2.2 手动编写netty提供的new Strin…

一个查看armv8系统寄存器-值-含义的方式

找到解压后的SysReg_xml_v86A-2019-12目录 wget https://developer.arm.com/-/media/developer/products/architecture/armv8-a-architecture/2019-12/SysReg_xml_v86A-2019-12.tar.gz wget https://developer.arm.com/-/media/developer/products/architecture/armv8-a-archi…

(已解决)将overleaf上的文章paper上传到arxiv上遇到的问题。

文章目录 前言初级问题后续问题 前言 首先说一点&#xff0c;将paper的pdf文件直接上传arxiv是不行的&#xff0c;arxiv要求我们要上传源文件&#xff0c;所以才这么麻烦。 初级问题 首先上传文件之后有可能会在下面这个界面出现问题&#xff0c;这里一般都比较常见的问题&a…

『运维备忘录』之 HTTP 响应状态码速查

运维人员不仅要熟悉操作系统、服务器、网络等只是&#xff0c;甚至对于开发相关的也要有所了解。很多运维工作者可能一时半会记不住那么多命令、代码、方法、原理或者用法等等。这里我将结合自身工作&#xff0c;持续给大家更新运维工作所需要接触到的知识点&#xff0c;希望大…

java学习07---综合练习

飞机票 1.需求: 机票价格按照淡季旺季、头等舱和经济舱收费、输入机票原价、月份和头等舱或经济舱。 按照如下规则计算机票价格&#xff1a;旺季&#xff08;5-10月&#xff09;头等舱9折&#xff0c;经济舱8.5折&#xff0c;淡季&#xff08;11月到来年4月&#xff09;头等舱7…

[C#] 如何使用ScottPlot.WPF在WPF桌面程序中绘制图表

什么是ScottPlot.WPF&#xff1f; ScottPlot.WPF 是一个开源的数据可视化库&#xff0c;用于在 WPF 应用程序中创建高品质的绘图和图表。它是基于 ScottPlot 库的 WPF 版本&#xff0c;提供了简单易用的 API&#xff0c;使开发人员能够通过简单的代码创建各种类型的图表&#…

【项目技术点总结之三】使用Java生成复杂好看的word或pdf报告的解决方案

前言 项目中往往会遇到需要生成报告的场景&#xff0c;不管是简单报告还是复杂报告&#xff0c;其实都需要找很多资料去尝试&#xff0c;本文会提出几种个人完美解决报告生成的解决方案&#xff0c;而且会提出几个失败但是能生成报告的设想&#xff0c;当然都是踩过坑的&#…

Peter算法小课堂—背包问题

我们已经学过好久好久的动态规划了&#xff0c;动态规划_Peter Pan was right的博客-CSDN博客 那么&#xff0c;我用一张图片来概括一下背包问题。 大家有可能比较疑惑&#xff0c;优化决策怎么优化呢&#xff1f;答案是&#xff0c;滚动数组&#xff0c;一个神秘而简单的东西…

java nio零拷贝

零拷贝是一种计算机执行IO操作的优化技术&#xff0c;其核心目标是减少数据拷贝次数&#xff0c;从而提高系统性能。它主要体现在以下几个方面&#xff1a; 1. **定义与原理**&#xff1a;零拷贝字面上的意思包括“零”和“拷贝”。其中&#xff0c;“拷贝”是指数据从一个存储…

《21天精通IPv4 to IPv6》第3天:IPv6地址配置——如何为不同的系统配置IPv6?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

SpringCloud-Nacos服务分级存储模型

Nacos 服务分级存储模型是 Nacos 存储服务注册信息和配置信息的核心模型之一。它通过将服务和配置信息按照不同级别进行存储&#xff0c;实现了信息的灵活管理和快速检索&#xff0c;为微服务架构下的服务发现和配置管理提供了高效、可靠的支持。本文将对 Nacos 服务分级存储模…

CentOS7搭建Hadoop集群

准备工作 1、准备三台虚拟机&#xff0c;参考&#xff1a;CentOS7集群环境搭建&#xff08;3台&#xff09;-CSDN博客 2、配置虚拟机之间免密登录&#xff0c;参考&#xff1a;CentOS7集群配置免密登录-CSDN博客 3、虚拟机分别安装jdk&#xff0c;参考&#xff1a;CentOS7集…

springboot179基于javaweb的流浪宠物管理系统的设计与实现

简介 【毕设源码推荐 javaweb 项目】基于springbootvue 的 适用于计算机类毕业设计&#xff0c;课程设计参考与学习用途。仅供学习参考&#xff0c; 不得用于商业或者非法用途&#xff0c;否则&#xff0c;一切后果请用户自负。 看运行截图看 第五章 第四章 获取资料方式 **项…