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

第一步:新建项目

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博客

  • 28
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要用C++一个web后台服务器,你需要掌握以下几个方面的知识: 1. Socket编程:C++中可以使用系统提供的Socket API实现网络通信,包括TCP和UDP协议。 2. 多线程编程:为了提高服务器的并发处理能力,需要使用多线程技术。 3. HTTP协议:Web服务器需要遵循HTTP协议,包括解析请求、构建响应等。 4. 数据库操作:Web服务器通常需要与数据库进行交互,需要掌握数据库的基本操作方法。 下面是一个简单的示例代码,可以作为你的参考: ```c++ #include <iostream> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <unistd.h> #include <string.h> #include <thread> void clientHandler(int clientSocket) { char buffer[1024] = {0}; read(clientSocket, buffer, 1024); std::cout << "Received Message: " << buffer << std::endl; char response[] = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n<html><body><h1>Hello, World!</h1></body></html>"; write(clientSocket, response, sizeof(response)); close(clientSocket); } int main() { int serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == -1) { std::cerr << "Failed to create socket" << std::endl; return -1; } int port = 8080; sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(port); serverAddr.sin_addr.s_addr = INADDR_ANY; if (bind(serverSocket, (sockaddr*)&serverAddr, sizeof(serverAddr)) == -1) { std::cerr << "Failed to bind to port " << port << std::endl; return -1; } if (listen(serverSocket, 5) == -1) { std::cerr << "Failed to listen on socket" << std::endl; return -1; } std::cout << "Server started listening on port " << port << std::endl; while (true) { sockaddr_in clientAddr; socklen_t clientAddrSize = sizeof(clientAddr); int clientSocket = accept(serverSocket, (sockaddr*)&clientAddr, &clientAddrSize); if (clientSocket == -1) { std::cerr << "Failed to accept client connection" << std::endl; continue; } std::thread t(clientHandler, clientSocket); t.detach(); } close(serverSocket); return 0; } ``` 这段代码实现了一个简单的Web服务器,监听8080端口,并在接收到客户端请求时返回一个"Hello, World!"的HTML页面。每当有客户端连接时,都会创建一个新的线程进行处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小海海不怕困难

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值