C/C++实现高并发http服务器

http高并发服务器实现

基础知识

html,全称为html markup language,超文本标记语言。

http,全称hyper text transfer protocol,超文本传输协议。用于从万维网(WWW:World Wide Web)服务器传输超文本到本地浏览器的传送协议。

客户端请求的格式:


请求方法有:GET、POST等。URL:请求地址。协议版本:HTTP的版本。

服务器响应的格式:

-----响应代号代号描述
服务器上存在请求的内容,并可以响应给客户端200OK
客户端的请求有异常,方法有问题501Method Not Implemented
服务器收到请求后,因为自生的问题没法响应500Internal Server Error
请求的内容不存在404NOT FOUND
客户端发送的请求格式有问题等(一般不存在)400BAD REQUEST

http服务器实现

文件概念

文件的Inode元信息表示文件的索引节点,存储着文件的元信息,例如文件得创建者,文件创建日期,文件大小等。每个inode都有一个号码,操作系统用inode号码来识别不同的文件,使用命令ls -i可以查看inode号码。

stat函数

stat是C++用于读取文件资源管理器的库函数,头文件为:

#include<sys/stat.h>
#include<sys/types.h>
#include<unisted.h>int stat(const char *path,struct stat *buf);
int fstat(int fd,struct stat *buf);
int lstat(const char *path,struct stat *buf);parameter:path:文件路径buf:传入的保存文件状态的指针,用于保存文件的状态fd:文件描述符return:成功返回0,失败返回-1,并设置errno

stat的结构体内容如下所示:

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 */
};

并发和并行

并发与并行的区别(超级通俗易懂)这个博客十分清晰的展示了并发与并行的基本概念,简单来说所谓的并发指的是多个进程按照一定的时间间隔进行,只不过这个时间间隔很小,人类难以感受到而已,实际上在微观角度,进程的并发执行还是顺序执行

高并发:高并发是互联网分布式框架设计中必须要考虑的因素之一,通常指的是,通过设计系统能够同时并行处理很多请求。

线程可以并行的执行任务,更多C++多线程的解析参考C++ 多线程

//头文件
#include<pthread.h>//函数
int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void *(*start_routine)(void *),void *arg);pthread_t:当前Linux中可理解为:typedef unsigned long int pthread_t
args1:传出参数,保存系统为我们分配好的线程ID;
args2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。
args3:函数指针,指向线程主函数(线程体),函数运行结束,则线程结束。
args4:线程主函数执行期间所需要使用的函数。

在一个线程中调用pthread_create()创建新的线程后,当前线程从pthread_create()返回继续往下执行,而新的线程所执行的代码由我们传给pthread_create的函数指针start_routine决定。start_routine函数接收一个参数,是通过pthread_createarg参数传递给它的,该参数的类型为void *,这个指针按什么类型解释由调用者自己定义。start_routine的返回值类型也是void *,这个指针的含义同样由调用者自己定义。start_routine返回时,这个线程就退出了,其它线程可以调用pthread_join得到start_routine的返回值。
pthread_create成功返回后,新创建的线程的id被填写到thread参数所指向的内存单元。
attr参数表示线程属性。

pthread_exit (status) 

pthread_exit用于显式地退出一个线程。通常情况下,pthread_exit() 函数是在线程完成工作后无需继续存在时被调用。
如果 main() 是在它所创建的线程之前结束,并通过 pthread_exit() 退出,那么其他线程将继续执行。否则,它们将在 main() 结束时自动被终止。

gcc/g++ 编译时需要添加 -pthread进行编译。

gcc test.c -pthread -o test

简单的多线程实例:

#include <iostream>
#include <cstdlib>
#include <pthread.h>using namespace std;#define NUM_THREADS     5void *PrintHello(void *threadid)
{  // 对传入的参数进行强制类型转换,由无类型指针变为整形数指针,然后再读取int tid = *((int*)threadid);cout << "Hello Runoob! 线程 ID, " << tid << endl;pthread_exit(NULL);
}int main ()
{pthread_t threads[NUM_THREADS];int indexes[NUM_THREADS];// 用数组来保存i的值int rc;int i;for( i=0; i < NUM_THREADS; i++ ){      cout << "main() : 创建线程, " << i << endl;indexes[i] = i; //先保存i的值// 传入的时候必须强制转换为void* 类型,即无类型指针        rc = pthread_create(&threads[i], NULL, PrintHello, (void *)&(indexes[i]));if (rc){cout << "Error:无法创建线程," << rc << endl;exit(-1);}}pthread_exit(NULL);
}

最终代码

服务器代码样例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <errno.h>
#include<pthread.h>#define SERVER_PORT 80void do_http_request(int client_sock);
int get_line(int client_sock, char *buf, int size);
void do_http_response(int client_sock, const char *path);
void headers(int client_sock, FILE *resource);
void cat(int client_sock, FILE *resource);
void not_found(int client_sock);
void inner_error(int client_sock);int main(void)
{int sock;struct sockaddr_in server_addr;sock = socket(AF_INET, SOCK_STREAM, 0);// printf("wait \n");bzero(&server_addr, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = htonl(INADDR_ANY);server_addr.sin_port = htons(SERVER_PORT);bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));listen(sock, 128);printf("wait client connect\n");int done = 1;while (done){struct sockaddr_in client;int client_sock, len, i;char client_ip[64];char buf[256];socklen_t client_addr_len;client_addr_len = sizeof(client);client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);printf("client ip: %s \t port is : %d \n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port));pthread_t tid;int* ptr_int=NULL;int err=0;ptr_int=(int*)malloc(sizeof(int));*ptr_int=client_sock;if(err=pthread_create(&tid,NULL,do_http_request,(void*)ptr_int)){printf(stderr,"cannot create thread. reason: %s\n",strerror(errno));if(ptr_int) free(ptr_int);}// do_http_request(client_sock);// close(client_sock);}close(sock);return 0;
}void* do_http_request(void* p_client_sock)
{int len = 0;char buf[256], method[64], url[256], path[256];int client_sock=*(int *)p_client_sock;struct stat st;// http response struct: request_method url protocol_version \r\nlen = get_line(client_sock, buf, sizeof(buf));if (len > 0){int i = 0, j = 0;while (!isspace(buf[j]) && i < sizeof(method) - 1){method[i] = buf[j];i++;j++;}// set end tagmethod[i] = '\0';printf("method: %s\n", method);// method is GET requestif (strncasecmp(method, "GET", i) == 0){printf("request method is GET\n");while (isspace(buf[j])){j++;}i = 0;while (!isspace(buf[j]) && i < sizeof(url) - 1){url[i] = buf[j];i++;j++;}url[i] = '\0';if (strncasecmp(url, "/favicon.ico", i) == 0){strcpy(url, "/hello.html");}printf("url:%s\n", url);// read surplus requestdo{len = get_line(client_sock, buf, sizeof(buf));printf("%s\n", buf);} while (len > 0);// get local url file, and process ? in url, eg. url=128.0.0.2/hel.html?wang=dedefechar *pos = strchr(url, '?');if (pos){// \0 represent string end tag*pos = '\0';printf("real url: %s\n", url);}sprintf(path, "./html_doc%s", url);printf("path:%s\n", path);// execute http response// if file is exist, to response 200, ok,and send html file,else response 404 NOT FOUNDif (stat(path, &st) == -1){printf("--------------------");fprintf(stderr, "stat %s failed. reason :%s\n", strerror(errno));not_found(client_sock);}else{printf("*****************");if (S_ISDIR(st.st_mode)){strcat(path, "/index.html");}do_http_response(client_sock, path);}}else{// request method is not GET,read http head, and response client requestfprintf(stderr, "warning, other request [%s]\n", method);do{len = get_line(client_sock, buf, sizeof(buf));printf("%s\n", buf);} while (len > 0);// unimplement()}}else{printf("method is error");}close(client_sock);if(p_client_sock) free(p_client_sock);
}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;}// send http headheaders(client_sock, resource);// send http bodycat(client_sock, resource);// printf("end response!!!!");fclose(resource);
}void headers(int client_sock, FILE *resource)
{struct stat st;int fileid = 0;char temp[64];char buf[1024] = {0};strcpy(buf, "HTTP/1.0 200 OK\r\n");strcat(buf, "Server: Martin Server\r\n");strcat(buf, "Content-Type: text/html\r\n");strcat(buf, "Connection: Close\r\n");fileid = fileno(resource);/* fstat: Get file attributes for the file, device, pipe, or socketthat file descriptor FD is open on and put them in BUF.  */if (fstat(fileid, &st) == -1){inner_error(client_sock);}snprintf(temp, 64, "Content-Length:%d\r\n\r\n", st.st_size);strcat(buf, temp);printf(stdout, "header: %s", buf);if (send(client_sock, buf, strlen(buf), 0) < 0){fprintf(stderr, "send fail,data %s, reason %s", buf, strerror(errno));}
}
void cat(int client_sock, FILE *resource)
{char buf[1024];fgets(buf, sizeof(buf), resource);while (!feof(resource)){int len = write(client_sock, buf, strlen(buf));if (len < 0){fprintf(stderr, "send boady error. reason %s\n", strerror(errno));break;}fprintf(stdout, "%s", buf);fgets(buf, sizeof(buf), resource);}
}int get_line(int client_sock, char *buf, int size)
{int count = 0;char ch = '\0';int len = 0;while (count < size - 1 && ch != '\n'){len = read(client_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 fail");count = -1;break;}else{fprintf(stderr, "client close.\n");count = -1;break;}}if (count >= 0)buf[count] = '\0';return count;
}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>文件不存在!\r\n\<P>The server could not fulfill your request because the resource specified is unavailable or none xistent.\r\n\
</BODY>\r\n\
</HTML>";int len = write(client_sock, reply, strlen(reply));fprintf(stdout, reply);if (len < 0){fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}
void inner_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 lang=\"zh-CN\">\r\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\r\n\
<HEAD>\r\n\
<TITLE>Inner Error</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\r\n\<P>服务器内部出错.\r\n\
</BODY>\r\n\
</HTML>";int len = write(client_sock, reply, strlen(reply));fprintf(stdout, reply);if (len <= 0){fprintf(stderr, "send reply failed. reason: %s\n", strerror(errno));}
}

客户端代码样例:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>#define SERVER_PORT 666
#define SERVER_IP "127.0.0.1"int main(int argc, char *argv[])
{int sockfd;char *message;struct sockaddr_in servaddr;int n;char buf[64];if (argc != 2){fputs("Usage: ./echo_client message \n", stderr);exit(1);}message = argv[1];printf("message: %s\n", message);sockfd = socket(AF_INET, SOCK_STREAM, 0);// 重置结构体的内存空间memset(&servaddr, '\0', sizeof(struct sockaddr_in));servaddr.sin_family = AF_INET;inet_pton(AF_INET, SERVER_IP, &servaddr.sin_addr);servaddr.sin_port = htons(SERVER_PORT);connect(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));write(sockfd, message, strlen(message));n = read(sockfd, buf, sizeof(buf) - 1);if (n > 0){buf[n] = '\0';printf("receive: %s\n", buf);}else{perror("error!!!");}printf("finished.\n");close(sockfd);return 0;
}

参考文献

  1. https://blog.csdn.net/scarificed/article/details/114645082
  2. https://www.runoob.com/cplusplus/cpp-multithreading.html

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

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

相关文章

java-error-No converter found for return value of type

java-error-No converter found for return value of type 问题描述 &#xff1a; 日志如下 &#xff1a; 14-Jul-2023 15:27:46.747 严重 [http-nio-8080-exec-5] org.apache.catalina.core.StandardWrapperValve.invoke 在路径为[]的上下文中&#xff0c;Servlet[action]的…

集群基础2——LVS负载均衡apache(nat模式)

文章目录 一、环境说明一、配置调度器网卡二、配置后端服务器三、配置调度器四、验证五、设置https负载均衡 一、环境说明 使用lvs中的nat模型&#xff0c;对apache负载均衡集群。 主机IP角色安装服务真实IP&#xff1a;192.168.161.129VIP&#xff1a;192.168.161.130调度服务…

阿里云无影云电脑具体价格表(1元入口)

阿里云无影云电脑配置费用&#xff0c;4核8G企业办公型云电脑可以免费使用3个月&#xff0c;无影云电脑地域不同费用不同&#xff0c;无影云电脑是由云桌面配置、云盘、互联网访问带宽、AD Connector、桌面组共用桌面session等费用组成&#xff0c;阿里云百科分享阿里云无影云电…

前端学习——JS进阶 (Day1)

作用域 局部作用域 全局作用域 作用域链 JS垃圾回收机制 闭包 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name&qu…

如何建立统一的自动化测试平台?

前面的文章中我们为大家介绍了中通科技自动化测试当时正在面临的一些困境。第一个是自动化测试框架太多&#xff0c;测试工程师在选择框架和脚本语言的时候很难统一&#xff0c;脚本编写门槛高。第二个是运行脚本的平台不统一&#xff0c;脚本运行时不够稳定。第三个是不同的Je…

使用SiO2和高介电常数介质的SiC功率MOSFET的栅极阻抗分析

Impedance n.阻抗 dielectric n.电解质 propagation n.传播 标题&#xff1a;Gate Impedance Analysis of SiC power MOSFETs with SiO2 and High-κ Dielectric 阅读日期&#xff1a;2023.7.13 研究了什么 这篇论文研究了SiC功率MOSFET的门电阻Zgg特性&#xff0c;包括SiO2…

简易评分系统

目录 一、实验目的 二、操作环境 三、实验内容和过程 1.实验内容 2.代码 2.1 用户验证功能 2.2 菜单函数 2.3 评分功能 四、结果分析 总体的输出结果&#xff1a; 保存文件成功截图&#xff1a; 五、小结 一、实验目的 1.巩固和提高学生学过的基础理论和专业知识&am…

Python自动化办公:pptx篇

文章目录 简介能做什么PPT要素介绍官方demo高阶引申参考文献 202201笔记迁移 简介 python-pptx包是用来自动化处理ppt的。 使用的第一步是安装 pip install python-pptx相比python-docx&#xff0c;python-pptx的使用更为麻烦一些&#xff0c;原因有很多&#xff0c;比如说&…

SpringBoot与Vue前后端分离项目。用Nginx代理。

Nginx代理主要是解决跨域与负载均衡的作用。 我这里用的自己的电脑&#xff0c;用的windows系统&#xff0c;不过配置基本是和Linux一样的。 下载Nginx nginx: download Nginx常用命令&#xff0c;先cd到解文件夹路径&#xff1a; nginx.exe&#xff1a;开启服务。nginx -s…

数据可视化分析,2023结婚全品类消费趋势洞察报告

结婚消费与人们的关系密切相关。结婚是一个重要的人生事件&#xff0c;往往伴随着大量的消费。人们倾向于在婚礼仪式、婚纱摄影、宴会等方面进行豪华的投资&#xff0c;以展示社会地位和个人品味。此外&#xff0c;结婚还涉及到婚戒、婚庆、蜜月旅行等费用。然而&#xff0c;随…

LayUI之增删改查

目录 一、前言 1.1 前言 1.2 前端代码(数据表格组件) 1.3 封装JS 二、LayUI增删改查的后台代码 2.1 编写Dao方法 2.1 增加 2.2 删除 2.3 修改 三、LayUI增删改查的前端代码 3.1 增加 一、前言 1.1 前言 上一篇文章我们一起做了LayUI的动态添加选项卡&#xff0c;这一篇…

[SSM]MyBatis的缓存与逆向工程

目录 十三、MyBatis的缓存 13.1一级缓存 13.2二级缓存 13.3MyBatis集成EhCache 十四、MyBatis的逆向工程 14.1逆向工程配置与生成 14.2测试 十三、MyBatis的缓存 缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高程序的执行效率。 myb…