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

news2024/9/26 3:19:54

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     5
 
void *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 80

void 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\n
    len = 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 tag
        method[i] = '\0';
        printf("method: %s\n", method);

        // method is GET request
        if (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 request
            do
            {
                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=dedefe
            char *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 FOUND
            if (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 request
            fprintf(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 head
    headers(client_sock, resource);
    // send http body
    cat(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 socket
       that 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.coloradmin.cn/o/738461.html

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

相关文章

绝了!贸易企业增值税高,税负重怎么办?这些处理技巧请收藏!

绝了&#xff01;贸易企业增值税高&#xff0c;税负重怎么办&#xff1f;这些处理技巧请收藏&#xff01; 《税筹顾问》专注于园区招商、企业税务筹划&#xff0c;合理合规助力企业节税&#xff01; 贸易企业始终都逃脱不了税负重的问题&#xff0c;这是因为贸易企业自身的行业…

ChatGPT潮落 资金逃离AI概念股

ChatGPT访问量首次出现负增长&#xff0c;寒气传导到证券市场。 上半年经历暴涨的一众AI概念股偃旗息鼓&#xff0c;蓝色光标、三六零、昆仑万维等知名个股均较高点跌超30%。微软、英伟达也未能幸免&#xff0c;同样有不同程度回调。 回顾这波过山车般的市场表现&#xff0c;…

【JavaEE】你真的了解Cookie和Session吗?

博主简介&#xff1a;想进大厂的打工人博主主页&#xff1a;xyk:所属专栏: JavaEE初阶 目录 文章目录 一、什么是Cookie 二、什么是Session 三、Cookie和Session有什么不同 四、关于Sessionid 五、关于浏览器禁止Cookie 六、如何考虑分布式Session问题&#xff1f; 七、Serv…

springboot请求重定向失败问题解决方案

今天晚上在写登录页面时&#xff0c;发现自己的首页无法正常访问&#xff0c;用户名和密码正常的情况下还是无法访问首页。于是开始进行debug&#xff0c; 程序执行至此处时无任何异常&#xff0c;但是就是在进行重定向页面时出现了404&#xff0c;在检查导航栏后发现地址栏也发…

做好POC测试 需要重点关心的5点

在日常POC测试过程中&#xff0c;往往遇到&#xff1a;测试计划时间结束才达到测试目标&#xff0c;导致结果未记录&#xff1b;不了解测试达标要求&#xff0c;未达到测试要求&#xff0c;后期大量的工作进行补救&#xff1b;测试遇到的问题&#xff0c;没有充分地相互沟通等问…

Dell Inspiron 15-3567电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网&#xff0c;转载需注明出处。&#xff08;下载请直接百度黑果魏叔&#xff09; 硬件型号驱动情况 主板Dell Inspiron 15-3567 处理器Intel Core i5 Kabylake, 7200U, 2.70 GHz已驱动 内存16GB LPDDR4X 3200MHz已驱动 硬盘三星 MZVLW1T0HMLH-000L2 (…

pycharm找不到conda可执行文件怎么办?

问题&#xff1a;pycharm配置conda环境找不到conda可执行文件 解决办法&#xff1a; 1. 找到 anaconda 安装目录&#xff08;D:\Users\wl\anaconda3&#xff09; 2. 打开pycharm &#xff08;看图吧<手动狗头>&#xff09; 找到anaconda3\condabin\conda.bat 选择你要…

【从删库到跑路】MySQL数据库的 存储引擎

&#x1f38a;专栏【MySQL】 &#x1f354;喜欢的诗句&#xff1a;更喜岷山千里雪 三军过后尽开颜。 &#x1f386;音乐分享【如愿】 &#x1f970;欢迎并且感谢大家指出小吉的问题 文章目录 &#x1f33a;存储引擎简介&#x1f384;查询当前数据库支持的存储引擎&#x1f384;…

C++笔记之是否知道了一个数组首元素的地址就可以获取该整个数组?-面试问题

C笔记之数组名和指针GPT问答记录 事前提要&#xff1a;前段时间去面一个公司&#xff0c;面试官说知道了一个数组首元素的地址&#xff0c;就可以获取该整个数组&#xff0c;我当时就困惑&#xff0c;不知道大小和长度&#xff0c;只知道地址怎么就能获取。但也没反驳面试官&a…

ChatGPT炒股:批量下载北交所上市公司的招股说明书

打开北京证券交易所官网&#xff0c;点击发行上市&#xff0c;然后点击公开发行信息披露&#xff0c;然后在查询框里面输入关键词&#xff1a;在北京证券交易所上市招股说明书&#xff0c;然后选择时间&#xff0c;点击查询&#xff0c;就可以看到所有北交所上市公司的招股说明…

数据集【NO.13】复杂场景下的鸟类检测数据集

写在前面&#xff1a;数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0c;方便大家下载数据集&#xff0c;若无法下载可关注后私信领取。关注免费领取整理好的数据集资料&#xff01;今天分享…

一款可定时发圈和标签群发的微信管理软件是什么样的?

什么是时间管理&#xff1f;它是一种科学的解释&#xff0c;意味着时间的流逝是不可阻挡的&#xff0c;但是可以通过掌握时间来获取更多的改变。那如何在有限的生命里而做更多有意义的事情呢&#xff1f;学习一下时间管理&#xff0c;你一定会从中得到启发和答案的。 1.先确定…

MYSQL建库及查询

目的&#xff1a; 1.创建数据库&#xff0c;删除数据库&#xff0c;查询创建数据的语句&#xff0c;使用数据库&#xff0c;查询当前默认的数据库以及使用的编码方式校验规则 2.数字&#xff0c;文本&#xff0c;日期 在一章表中定义多个字段&#xff0c;要使用提到的所有的数…

vue 多环境打包指令配置及编译

1.创建多环境: 在根目录创建.env.xxx文件,如下为例(我创建了两个) 文件内容主要包括&#xff1a; # 页面标题 VUE_APP_TITLE "标题"# 生产环境配置 ENV production# DNA检测仓储管理系统/生产环境 VUE_APP_BASE_API https://xxxxxx 2.设置: 修改根目录下的package…

Parasoft Jtest 2023.1版本-全新的Jtest让你的Java测试更加容易

测试用例的创建和执行以及测量代码覆盖率是现代开发过程的重要组成部分。新发布的Parasoft Jtest 2023.1增加了重要的新功能&#xff0c;使Java开发和QA团队在测试实践中更具有生产力和效率。 通过使用最新版本的Jtest&#xff0c;您可以在Visual Studio code IDE中提高Java代…

设计模式之二:观察者模式

假定我们需要为Weather-O-Rama公司建立一个气象站系统&#xff0c;除已有的WeatherData有数据源类&#xff0c;还需要更新三个布告板的显示&#xff1a;目前状况&#xff08;温度、湿度、气压&#xff09;、气象统计和天气预报。 1 以下是一个可能的实现 class WeatherData { …

雅思口语备考模仿练习有没有用?

如何对于雅思口语进行模仿练习&#xff1f;这是很多同学需要了解的事情&#xff0c;那么接下来就和一起来了解一下雅思口语备考模仿练习有没有用&#xff1f; 模仿练习 1. 方法 众所周知&#xff0c;模仿最基本的方法便是跟读。跟读的途径可以是两种&#xff1a;一种是听录音…

【AcWing算法基础课】第四章 数学知识(未完待续)

文章目录 前言课前温习番外&#xff1a;秦九韶算法核心模板 一、质数1. 试除法判定质数核心模板1.1题目描述1.2思路分析1.3代码实现 2、试除法分解质因数核心模板1.4题目描述1.5思路分析1.6代码实现 二、筛素数1.朴素筛法求素数核心模板2.线性筛法求素数&#xff08;O(n)&#…

寻找nacos数据库连接名及密码

首先找到resources下的bootstrap.properties 找到其中的 spring.cloud.nacos.username***** spring.cloud.nacos.password**** 这个为nacos登陆的密码 到nacos中找到相应服务配置信息 spring.datasource.druid.urljdbc:mysql://ip地址 spring.datasource.druid.username用户…

防水防尘防震的工业三防平板电脑

工业三防平板电脑是一种具有防尘、防水和防震功能的平板电脑。它们被广泛应用于工业领域&#xff0c;特别是在恶劣环境下的工作场所。这些平板电脑具有坚固耐用的外壳和先进的技术&#xff0c;能够在恶劣的工作条件下提供稳定可靠的性能。 工业三防平板电脑的功能&#xff1a; …