C++高并发服务器项目和代码详解

  1. 项目预期结果:该http服务器的主要功能是,在任何系统的浏览器端访问服务器ip+html文件名,如果服务端如果存在我们想要的html文件,则将其展示在浏览器端否则发送NOT FOUND页面,同时允许上千个浏览器进行访问。

网络通信的结构框图:

  1. 项目具体子函数解析:
  1. 检查错误函数per:主要是检查write或者read函数是否出错。

void per(int sock1, const char* des)
{
    if (sock1 == -1)
    {
        printf("%s error! The reason is: %s\n", des, strerror(errno));
        exit(1);
    }
}
  1. 创建服务器的socket,直接先上代码:基本就是模板操作,先定义int sock_sever,再定义好sever_addr,再设好值后进行bind和listen。per函数进行校验错误。返回res进行检验函数是否出错。

//创建服务器套接字和addr,发生错误返回-1
int server_get_sock(int * sock_sever)
{
    *sock_sever=(socket(AF_INET, SOCK_STREAM, 0));//创建套接字,IPV4格式ip地址;TCP协议
    per(*sock_sever, "socket");
    struct sockaddr_in server_addr;                 //创建套接字的地址和端口号
    bzero(&server_addr, sizeof(server_addr));//清零
    server_addr.sin_family = AF_INET;  //  IPV4
    server_addr.sin_port = htons(SEVER_PORT); //端口号主机字节序转网络字节序
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //接收本地所有的IP地址

    int res(bind(*sock_sever, (struct sockaddr*)&server_addr, sizeof(server_addr))); //绑定套接字
    per(res, "bind");

    res = (listen(*sock_sever, 64));  //准备接受信息,最多等待64个客户端
    per(res, "listen");
    printf("Waiting:...\n");
    return res;
}
  1. 创建客户端socket:创建客户端socket和addr

int client_get_sock(int sock_sever)//创建客户端套接字和addr,错误执行返回-1
{    
    int client_sock;
    struct sockaddr_in client_addr;
    socklen_t client_addr_len(sizeof(client_addr));   //固定数据格式socklen_t

    client_sock = accept(sock_sever, (struct sockaddr*)&client_addr, &(client_addr_len));//accept函数需要传入client_addr_len地址 
    per(client_sock, "accept");

    //打印出客户端端口号和IP地址
    char ip_buf[50];
    printf("client ip:%s, port is %d\n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip_buf, sizeof(ip_buf)), ntohs(client_addr.sin_port));
    return client_sock;
}

HTTP协议数据包格式

  1. 对服务器来说,最重要的部分就是请求方法和URL。且每一行都是\r\n作为收尾,遇到连续两个\r\n\r\n则表示请求结束。故可以采用按行来进行读取数据区解析数据包

int get_line(int sock, char *buf, int lenth) //返回-1表示出错,返回0表示空行 ,大于0表示读取到正常数据
{
    int count(0);
    int len(0);
    char ch('\0');
    while ((count < lenth - 1)&& ch != '\n')
    {
        len = read(sock, &ch, sizeof(ch));  //先逐个读取,去掉空格和回车
        if (len == 1)
        {
            if (ch == '\r')
                continue;
            else if (ch == '\n')
            {
                break;
            }
            buf[count] = ch;
            count++;
        }
        else if(len == -1)//读取出错误
        {
            per(len, "read");
            count = -1;   //向其他函数表示此时读取出错
            break;
        }
        else//断网了客户端关闭sock连接 read返回0
        {
            printf("client close\n");
            count = -1;   //向其他函数表示此时读取出错
            break;
        }
    }
    if(count >0)  //读取到了数据,说明请求头没结束
        buf[count] = '\0';
    return count;
}
  1. 处理请求数据:优先读取出第一行数据得到请求方法(重点处理GET方法)和获取URL方便回传给浏览器目标html文件。此处的函数定义为void* xxx(void *)是为了后续的并发线程函数

void* do_http_request(void* p_sock)
{
    int sock = *(int *)p_sock;   // 强制转换
    char buf[SIZE];//读出来每一行的数据的缓冲区
    int len(0);//检查标志位
    char headmed[16], url[256];// url存放目标网址, headmed存放命令类型

    //根据HTTP协议先把情报头部第一行读取出来,得到method和URL
    len = get_line(sock, buf, sizeof(buf));//只读一行,获取头部
    printf("Head is : %s\n", buf);
    if (len>0)//头部获取成功,程序只处理get请求并且获取URL
    {
        int i(0), j(0);
        while (!isspace(buf[j]) && i < sizeof(headmed) - 1) //buf[j]不是空格,且headmed还能存放的下命令字符
        {
            headmed[i] = buf[j];
            i++;
            j++;
        }
        j += 2;// 跳过buf内白空格,跳过第一个/符号
        headmed[i] = '\0';

        if (!strncasecmp(headmed, "GET", i)) //判断是否是GET方法 且不区分大小写
        {
            printf("method is : GET\n");
            i = 0;
            while (!isspace(buf[j]) && i < sizeof(url) - 1) //buf[j]不是空格 存放URL
            {
                url[i] = buf[j];
                i++;
                j++;
            }            
            char* p(strchr(url, '?')); //查找url中是否有?若有则把其替换成\0
            if (p)
            {
                *p = '\0';
            }
            else
            {
                url[i] = '\0';
            }

            printf("URL:%s\n", url);
            定位URL,存放到path中
            char path[256];
            sprintf(path, "./html_dock/%s", url);  //把path格式化成“./html_dock/%s”
            printf("Last The target issue path is :%s\n", path);
            //判断文件是否存在
            struct stat st;  //判断目标文件是否存在服务器内
            if (stat(path, &st) == -1)// 文件不存在
            {
                sprintf(path, "./html_dock/err.html"); //直接向客户端传输预备好的err.html
                fprintf(stderr, "stat %s failed ,reason: %s\n", path, strerror(errno));
                notfound(sock, path);//404函数,下面有代码
            }
            else
            {
                if (S_ISDIR(st.st_mode))// 若url内得到的是一个目录,自动为客户端传送index.html
                {
                    strcat(path, "/index.html");//给目录URL追加一个index.html,输出默认网址
                }
                do_http_respons(sock, path);  // 请求处理函数
            }
            //读取请求头部剩余的每一行
            while (len > 0)
            {
                len = get_line(sock, buf, sizeof(buf));
                per(len, "read1");
            }    
        }
        else // 非GET请求  返回给客户端501 Method not found
        {
            fprintf(stderr, "Error! Can not solove the request:%s", headmed);
            len = get_line(sock, buf, sizeof(buf));
            printf("read:%s\n", buf);
            not_501(sock); //510函数
        }
    }
    else
    {
        not_400(sock); //400函数
        len = get_line(sock, buf, sizeof(buf));
        per(len, "read1");
    }
    close(sock); 
    return NULL;
}
  1. 请求处理函数:GET:向浏览器输出指定的文件。

HTTP的响应的格式如下所示:主要包括响应头部(状态行+消息报头),响应正文两部分。

void do_http_respons(int client_sock, const char *path)
{

    FILE* re = NULL;
    re = fopen(path, "r"); //打开目标路径下的html文件
    if (re == NULL)
    {
        notfound(client_sock, path); //文件不存在,发送404
        return;
    }
    //发送http头部
    headers(client_sock, re);
    //发送httpbody
    body(client_sock, re);//发送正文,也就是html文件内容
    fclose(re);
}
  1. 发送http头部函数:

void headers(int client_sock, FILE* re)
{
    char buf[SIZE] = { 0 };
    strcpy(buf, "HTTP/1.0 200 OK\r\n");
    strcat(buf, "Server: Ve Server\r\n");
    strcat(buf, "Content-Type:text/html\r\n");
    strcat(buf, "Connection:Close\r\n");

    int fileid(fileno(re)); //返回re文件流的文件描述符,因为需要文件长度
    struct stat st;
    fstat(fileid, &st); //检查文件是否存在。
    
    char temp[64];
    sprintf(temp, "Content-Length: %ld\r\n\r\n", st.st_size); //文件长度
    strcat(buf, temp);
    printf("Headder:%s\n", buf);
    int len=send(client_sock, buf, strlen(buf), 0);
    if (!len)//如果发送失败
    {
        fprintf(stderr, "send failed. data: %s, reason: %s\n", buf, strerror(errno));
    }
}
  1. 发送html文件内容:

void body(int client_sock, FILE* re)
{
    char buf[SIZE];
    //读取文件所有内容发送给客户端
    do //按行读取并发送
    {
        fgets(buf, sizeof(buf), re);
        int len = write(client_sock, buf, strlen(buf));
        per(len, "write");
        printf("body: %s", buf);
    } while (!feof(re));//直到文件末尾
}
  1. 404函数:

void not_400(int client_sock)
{
    const char* reply = "HTTP/1.0 400  BAD REQUEST Error\r\n\
Content-Type: text/html\r\n\
\r\n\ 
<HTML lang=\"zh-CN\">\n\r\
< meta content = \"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\r\
    < head >\n\r\
        <meta http - equiv = \"Content-Type\" content = \"text/html;   charset=UTF-8\">\n\r\
        <title>BAD REQUEST< / title>\n\r\
        < / head>\n\r\
        <BODY>\n\r\
        <P>BROWSER sent a bad request!\n\r\
        < / BODY>\n\r\
        < / HTML>";
    int len(write(client_sock, reply, strlen(reply)));
    per(len, "write111");
}
  1. 501函数:

void not_501(int client_sock)
{
    const char* reply = "HTTP/1.0 501  Wrong Method\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\n\r\
<meta http - equiv = \"Content-Type\" content = \"text/html;   charset=UTF-8\">\n\r\
    < head >\n\r\
    \
        <title>BAD REQUEST< / title>\n\r\
        < / head>\n\r\
        <BODY>\n\r\
        <P>BROWSER Use Wrong Method\n\r\
        < / BODY>\n\r\
        < / HTML>";
    int len(write(client_sock, reply, strlen(reply)));
    per(len, "write");// < meta content = \"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\r
}
  1. 400函数:

void not_400(int client_sock)
{
    const char* reply = "HTTP/1.0 400  BAD REQUEST Error\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML lang=\"zh-CN\">\n\r\
< meta content = \"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\r\
    < head >\n\r\
        <meta http - equiv = \"Content-Type\" content = \"text/html;   charset=UTF-8\">\n\r\
        <title>BAD REQUEST< / title>\n\r\
        < / head>\n\r\
        <BODY>\n\r\
        <P>BROWSER sent a bad request!\n\r\
        < / BODY>\n\r\
        < / HTML>";
    int len(write(client_sock, reply, strlen(reply)));
    per(len, "write111");
}
  1. 最后的主函数:

#define SEVER_PORT 80
#define SIZE 256

int main()
{
    int sock_sever,res;
    res = server_get_sock(&sock_sever);
    per(res, "get_sock");
    int flag(1);
    while (flag)
    {
        int client_sock(client_get_sock(sock_sever));

        //开启线程,实现并发
        pthread_t p_id; //保存线程id用
        int err(pthread_create(&p_id, NULL, &do_http_request, (void *)&client_sock));
        per(err, "p_create");        
    }
    close(sock_sever);
    return 0;
}
  1. 如何让程序运行起来
  1. 直接linux下g++ server.cpp -o -pthread server

  1. 运行

  1. 另起一个终端,远程连接到服务器

  1. 打开浏览器输入ip地址+模板url

  1. 若输入错误网址

至此已经实现了并发访问服务器

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
【资源说明】 基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip 基于Reactor高并发服务器 c++源码+详细项目说明+sln解决方案.zip 基于`Reactor`高并发服务器 `C++` > 基于`Reactor`的高并发服务器,分为`反应堆模型`,`多线程`,`I/O模型`,`服务器`,`Http请求`和`响应`五部分 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载使用,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可直接用于毕设、课设、作业等。 欢迎下载,沟通交流,互相学习,共同进步!
高性能并发服务器项目实战是指在实际项目中开发和部署能够处理高并发请求的服务器。在这个项目中,我们需要考虑到服务器的性能和并发处理能力。 首先,我们需要选择适合的服务器框架和编程语言,例如Java或C++。这些语言具有较高的性能和并发处理能力,可以满足大量请求的需求。 其次,我们需要进行服务器的优化和扩展设计。可以通过使用多线程、线程池、异步IO等技术来提高服务器的并发处理能力。同时,还需要考虑服务器的物理资源和网络带宽等因素,以确保服务器能够支持高并发量。 然后,我们需要对服务器进行压力测试和性能优化。可以使用工具,如JMeter或wrk来模拟大量并发请求,并监测服务器的性能指标,如吞吐量、响应时间等。通过分析测试结果,我们可以找出性能瓶颈并进行相应的优化,如代码优化、数据库优化、缓存策略等。 最后,我们需要进行服务器集群和负载均衡的部署。通过将服务器部署在多台机器上,并使用负载均衡器来分发请求,可以进一步提高服务器的性能和可扩展性。同时,还需要考虑服务器的容灾和备份策略,以确保系统的高可用性和可靠性。 总之,高性能并发服务器项目实战是一个综合性的项目,需要综合考虑多个因素,包括服务器框架的选择、优化设计、性能测试与优化、集群部署等。只有通过不断的实践和优化,才能够开发出满足高并发请求的高性能服务器

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值