精华内容
下载资源
问答
  • C++web服务器开发

    千次阅读 2019-08-14 08:55:56
    C++web服务器开发 1. 首先介绍一下这个项目;这个项目是通过C++11编写的web服务器,模型为Reactor+非阻塞I/O(epoll作为I/O多路复用实现方式)+线程池,支持get、head请求,支持HTTP长连接,并实现了优雅关闭连接。 ...

    C++web服务器开发

    1. 首先介绍一下这个项目;这个项目是通过C++11编写的web服务器,模型为Reactor+非阻塞I/O(epoll作为I/O多路复用实现方式)+线程池,支持get、head请求,支持HTTP长连接,并实现了优雅关闭连接。

    I/O多路复用是什么技术呢? I/O多路复用(multiplexing)的本质是通过一种机制(系统内核缓冲I/O数据),让单个进程可以监视多个文件描述符,一旦某个描述符就绪(一般是读就绪或写就绪),能够通知程序进行相应的读写操作。(包括select、poll、epoll)

    文件描述符 文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引用的抽象化概念。
    文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。

    select、poll和epoll的区别 select函数只能轮询linux操作系统提供的fd_set这个类型的变量,poll函数轮询的是pollfd这个类型的数组,这个数组可以自己定义长度,而不是上面select函数只能轮询定长为FD_SETSIZE的fd_set变量。epoll不再是轮询,而是给每个文件描述符上发生的io事件设置一个回调函数。

    2.定时器是怎么实现的?还有什么实现方式?
    定时器用来处理超时的请求和长时间不活跃的连接,使用stl里的priority_queue,基于小根堆的定时器关闭超时请求,并采用惰性删除的方式,时间的到来不会唤醒线程,而是每次循环的最后进行检查,如果超时了再删,因为这里对超时的要求并不会很高,如果此时线程忙,那么检查时间队列的间隔也会短,如果不忙,也给了超时请求更长的等待时间。还可以使用STL中的set。

    3.实现一个无锁队列? 用原子操作

    4.eventfd是什么?有什么好处?
    eventfd是系统提供的一个轻量级的进程间通信的系统调用,eventfd实现了线程的异步唤醒(eventfd()会返回一个文件描述符,如果该进程被fork的时候,这个文件描述符也会复制过去,这时候就会有多个的文件描述符指向同一个eventfd对象)

    5.双缓冲区异步日志是什么?为什么要这样做?对这个日志系统有没有进行压力测试?
    基本思路是准备两块buffer:A和B, 前端负责往buffer A填数据(日志消息), 后端负责将buffer B的数据写入文件;当buffer A写满之后, 交换A和B, 让后端将buffer A的数据写入文件, 而前端则往buffer B填入新的日志消息, 如此往复。
    好处:前端不是将一条条日志消息分别送给后端,而是将多条日志消息拼接成一个大的buffer传送给后端,相当于批处理,减少了线程唤醒的开销。

    6.什么是优雅关闭连接?
    就是read()到0,要透明的传递这个行为而不是直接暴力close()

    7.epoll的边沿触发(et)和水平触发(lt)有什么区别? lt模式下,默认不可读,只有epoll通知你可读才是可读,否则不可读。
    et模式下,默认可读。你可以随便读,直到发生EAGAIN。可读时读和不读,怎么读都由你自己决定,中间epoll不管。
    EAGAIN后不可读了,等到再次可读,epoll会再通知一次。
    ET模式要比LE复杂许多,它对用户提出了更高的要求,即每次读,必须读到不能再读(出现EAGAIN),每次写,写到不能再写(出现EAGAIN)。而LT则简单的多,可以选择也这样做,也可以为编程方便,比如每次只read一次(muduo就是这样做的,这样可以减少系统调用次数)。

    8.epoll为什么高效,相比select和poll

    1. 减少了用户态和内核态之间的文件描述符拷贝
    2. 减少了对就绪文件描述符的遍历

    9.假如服务器要升级,又不想让用户感觉到服务器升级了,该怎么做?
    不间断的提供服务,参考nginx的平滑升级

    10.Reactor模式是什么?
    反应器设计模式(Reactor pattern)是一种为处理并发服务请求,并将请求提交到一个或
    者多个服务处理程序的事件设计模式。当客户端请求抵达后,服务处理程序使用多路分配策略,由一个非阻塞的线程来接收所有的请求,然后派发这些请求至相关的工作线程进行处理。

    展开全文
  • C++ Poco创建个非常简单web服务器,需要先安装Poco库,配置好Poco头文件和库的路径,window和Linux下都可以编译运行,运行后在浏览器上输入网址:http://127.0.0.1:9980/ Linux环境下G++编译代码 g++ main1.cpp -o ...
  • 本文给大家分享的是使用C++简单实现web服务器的代码,虽然非常的简陋,功能也很少,主要是为了更好的理解WEB服务器的工作原理,推荐给大家,也希望对大家能够有所帮助。
  • C++开发web服务框架之HTTP Web框架的设计与实现 基于vs2015与boost的程序
  • vs2013 c++实现上传的客户端,服务端为java写的web工程,模拟post方法,可以上传大文件
  • 测试方式(依次在浏览器输入一下内容进行不同情形的测试): 127.0.0.1:9988 ...127.0.0.1:9988/haha?x=10&y=20 也可以下载fiddler工具进行更全面的模拟测试! ...1、支持GET/POST方法 ...感兴趣的同学可以继续完善相关功能.
  • 测试的web服务器端代码如下: sprintf_s(customhtml2, sizeof(customhtml2), "HTTP/1.1 200 OK\r\n <set-Cookie:sid=%u;path=/> ", u_session_id); string strhtml1 = string(customhtml2); ...
  • c++实现简单的web服务器搭建

    千次阅读 2021-01-25 16:33:22
    本文使用c++ socket编程进行简单的web服务器搭建,出来了GET请求

    c++简单的web服务器搭建

    web 服务器与 Http 协议

    Web 浏览器(Web Browser)是一个用于文档检索和显示的客户应用程序,并通过超文本传输协议

    Http(Hyper Text Transfer Protocol)与 Web 服务器相连。

    通用的、低成本的浏览器节省了两层结构的 C/S 模式客户端软件的开发和维护费用。

    HTTP 协议工作流程

    1. 首先客户机与服务器需要建立连接。只要单击某个超级链接,HTTP 的工作就开始了。

    2. 建立连接后,客户机发送一个请求给服务器,请求方式的格式为:统一资源标识符(URL)、 协议版本号,后边是 MIME 信息:包括请求修饰符、客户机信息和可能的内容。

    3. 服务器接到请求后,给予相应的响应信息,其格式为一个状态行,包括信息的协议版本号、 一个成功或错误的代码,后边是 MIME 信息包括服务器信息、实体信息和可能的内容。

    4. 客户端接收服务器所返回的信息通过浏览器显示在用户的显示屏上,然后客户机与服务器断开连接。

    客户端请求方法

    方法描述
    get请求读一个万维网页
    head请求读一个万维网页的頭部
    put请求存储一个万维网页
    post附加一个命名的资源
    delete删除万维网页
    link连接两个已有资源
    unlink切断两个已有资源间的连接

    服务器的搭建

    服务器搭建需要对winsock版本以及套接字进行初始化,接着将本机的信息包括IP地址,端口进行绑定。

    这样不仅本机,在局域网内的机器也是可以对服务器进行请求数据。

    客户端请求的解析(请求方式以及请求资源的解析)

    客户端请求的解析需要通过获取客户端的请求头来进行解析,如下图所示,Request URL是客户端请求的地址,Request Method 为 请求的方式,因此只需要拿到客户端的请求头解析出请求内容以及请求方式即可。

    在这里插入图片描述

    HTTP 消息结构

    HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。

    一个HTTP"客户端"是一个应用程序(Web浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个HTTP的请求的目的。

    一个HTTP"服务器"同样也是一个应用程序(通常是一个Web服务,如Apache Web服务器或IIS服务器等),通过接收客户端的请求并向客户端发送HTTP响应数据。

    HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。

    一旦建立连接后,数据消息就通过类似Internet邮件所使用的格式[RFC5322]和多用途Internet邮件扩展(MIME)[RFC2045]来传送。

    客户端请求消息

    客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成,下图给出了请求报文的一般格式。

    在这里插入图片描述

    服务器响应消息

    HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文。

    在这里插入图片描述

    HTTP 响应头信息

    HTTP请求头提供了关于请求,响应或者其他的发送实体的信息。

    1. Content-Encoding
      文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader(“Accept-Encoding”))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。

    2. Content-Length
      表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入 ByteArrayOutputStream,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。

    3. Content-Type
      表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentType。

    4. Date
      当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。

    5. Expires
      应该在什么时候认为文档已经过期,从而不再缓存它?

    6. Last-Modified
      文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(Not Modified)状态。Last-Modified也可用setDateHeader方法来设置。

    7. Location
      表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。

    8. Refresh
      表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader(“Refresh”, “5; URL=http://host/path”)让浏览器读取指定的页面。
      注意这种功能通常是通过设置HTML页面HEAD区的<META HTTP-EQUIV=“Refresh” CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。

      注意Refresh的意义是"N秒之后刷新本页面或访问指定页面",而不是"每隔N秒刷新本页面或访问指定页面"。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<META HTTP-EQUIV=“Refresh” …>。

      注意Refresh头不属于HTTP 1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。

    9. Server
      服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。

    10. Set-Cookie
      设置和页面关联的Cookie。Servlet不应使用response.setHeader(“Set-Cookie”, …),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。

    HTTP 状态码

    HTTP 响应头信息HTTP content-type
    HTTP 状态码
    当浏览者访问一个网页时,浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前,此网页所在的服务器会返回一个包含 HTTP 状态码的信息头(server header)用以响应浏览器的请求。

    HTTP 状态码的英文为 HTTP Status Code。
    常见的 HTTP 状态码:
    200 - 请求成功
    301 - 资源(网页等)被永久转移到其它URL
    404 - 请求的资源(网页等)不存在
    500 - 内部服务器错误

    详细设计

    对客户端的请求头进行解析

    recv(client_fd,buff,99,0)  
    

    服务器的建立(包括初始化winsock版本,初始化套接字)

        char recbuf[2048];
        WSADATA wsaData;
        int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        SOCKADDR_IN svr_addr, cli_addr;
        int sin_len = sizeof(cli_addr);
    
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            cout<< "无法开启socket";
            exit(0);
        }
    
        int port = 81;
        svr_addr.sin_family = AF_INET;
        svr_addr.sin_addr.s_addr = INADDR_ANY;
        svr_addr.sin_port = htons(port);
    
        if (bind(sock, ( SOCKADDR *) &svr_addr, sizeof(svr_addr)) == -1)
        {
            closesocket(sock);
            cout<<"无法绑定\n";
        }
    
        listen(sock, 5);
    

    获得客户端的请求头

            if((numbytes = recv(client_fd,buff,99,0)) == -1)
            {
                perror("recv");
                exit(1);
            }
    

    对请求头进行解析(分析出其中的请求方式)

        char data[1000];
        char cd[500];
        char args[500];
        strcpy(args,"./");
        if(sscanf(buff, "%s%s", cd, args+2)!=2)
        {
            return;
        }
     //  cout<<" 111 "<<cd<<endl;
      //  cout<<" 111 "<<args<<endl;
        if(strcmp(cd,"GET")!=0)
        {
            cout<<"请求类型错误"<<endl;
            return;
        }
    

    对请求的资源进行分析

    char response[] = "HTTP/1.1 200 OK\r\n"
                      "Content-Type: text/html; charset=UTF-8\r\n\r\n"
                      "<!DOCTYPE html>"
                      "<html lang=\"zh-CN\">"
                      "<head>"
                      "<meta charset=\"utf-8\">"
                      "<title>Hello World</title>"
                      "<style>body {width: 35em;margin: 200px auto;font-family: Tahoma, Verdana, Arial, sans-serif;}"
                      "</style>"
                      "</head>"
                      "<body>"
                      "<p>This is a simple webserver<p>"
                      "<p><em>And this html does not support ZH-CN</em></p>"
                      "</body></html>\r\n";
    
    
    string s;
        char str[100];
        cout<<args<<endl;
        if(!strcmp(args,".//index.html"))
        {
            FILE * fp;
            if((fp=fopen(args,"rt"))==NULL)
            {
              //  cout<<"不行"<<endl;
            }
            else
            {
            //    cout<<"行"<<endl;
                while((fgets(str,1024,fp))!=NULL)
                {
                    s += str;
                    cout<<s<<endl;
                }
              //  cout<<"我giao"<<endl;
            }
            fclose(fp);
        cout<<s<<endl;
       // cout<<response<<endl;
        cout<< send(client_fd, r, sizeof(r), 0)<<"Bytes已发送" <<endl; // 发送请求的资源
        }else{
            cout<<response<<endl;
            cout<< send(client_fd, response, sizeof(response), 0)<<"Bytes已发送" <<endl;
        }
    

    完整代码(这里只是对GET请求进行处理)

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include<bits/stdc++.h>
    #include <WinSock2.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<fcntl.h>
    #include<assert.h>
    #include<unistd.h>
    
    using namespace std;
    int client_fd;
    int numbytes;
    char buff[100];
    char response[] = "HTTP/1.1 200 OK\r\n"
                      "Content-Type: text/html; charset=UTF-8\r\n\r\n"
                      "<!DOCTYPE html>"
                      "<html lang=\"zh-CN\">"
                      "<head>"
                      "<meta charset=\"utf-8\">"
                      "<title>Hello World</title>"
                      "<style>body {width: 35em;margin: 200px auto;font-family: Tahoma, Verdana, Arial, sans-serif;}"
                      "</style>"
                      "</head>"
                      "<body>"
                      "<p>This is a simple webserver<p>"
                      "<p><em>And this html does not support ZH-CN</em></p>"
                      "</body></html>\r\n";
    
    
    char r[] = "HTTP/1.1 200 OK\r\n""Content-Type: text/html; charset=UTF-8\r\n\r\n""<!DOCTYPE html><html><head><meta charset='utf-8'><</head><body>    <h1>yyy</h1>    <p>111</p></body></html>";
    
    void solve()
    {
        char data[1000];
        char cd[500];
        char args[500];
        strcpy(args,"./");
        if(sscanf(buff, "%s%s", cd, args+2)!=2)
        {
            return;
        }
     //  cout<<" 111 "<<cd<<endl;
      //  cout<<" 111 "<<args<<endl;
        if(strcmp(cd,"GET")!=0)
        {
            cout<<"请求类型错误"<<endl;
            return;
        }
        // int fd = open(args,O_RDONLY);
        // int x = send(client_fd,response, sizeof(response)-1,0);
    //    send(client_fd,fd,NULL,2500);
        string s;
        char str[100];
        cout<<args<<endl;
        if(!strcmp(args,".//index.html"))
        {
            FILE * fp;
            if((fp=fopen(args,"rt"))==NULL)
            {
              //  cout<<"不行"<<endl;
            }
            else
            {
            //    cout<<"行"<<endl;
                while((fgets(str,1024,fp))!=NULL)
                {
                    s += str;
                    cout<<s<<endl;
                }
              //  cout<<"我giao"<<endl;
            }
            fclose(fp);
        cout<<s<<endl;
       // cout<<response<<endl;
        cout<< send(client_fd, r, sizeof(r), 0)<<"Bytes已发送" <<endl;
        }else{
            cout<<response<<endl;
            cout<< send(client_fd, response, sizeof(response), 0)<<"Bytes已发送" <<endl;
        }
        //  FILE * fp = open(args,O_RDONLY);
    
        // cout<< pBuf <<endl;
        return;
    }
    int main()
    {
        char recbuf[2048];
        WSADATA wsaData;
        int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
        SOCKADDR_IN svr_addr, cli_addr;
        int sin_len = sizeof(cli_addr);
    
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0)
        {
            cout<< "无法开启socket";
            exit(0);
        }
    
        int port = 81;
        svr_addr.sin_family = AF_INET;
        svr_addr.sin_addr.s_addr = INADDR_ANY;
        svr_addr.sin_port = htons(port);
    
        if (bind(sock, ( SOCKADDR *) &svr_addr, sizeof(svr_addr)) == -1)
        {
            closesocket(sock);
            cout<<"无法绑定\n";
        }
    
        listen(sock, 5);
        while (1)
        {
            int len = sizeof(SOCKADDR);
            client_fd = accept(sock, (SOCKADDR *) &cli_addr, &sin_len);
            printf("客户端已连接\n");
            cout<<inet_ntoa(cli_addr.sin_addr)<<endl;
            if (client_fd == -1)
            {
                perror("accept()客户端连接出错");
                continue;
            }
            if((numbytes = recv(client_fd,buff,99,0)) == -1)
            {
                perror("recv");
                exit(1);
            }
            cout<<buff<<endl;
    
            solve();
    
            // cout<<response<<endl;
        }
        closesocket(client_fd);
        WSACleanup();
    }
    

    运行截图

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • c++简单Web服务器的实现

    千次阅读 2019-11-21 15:54:43
    实现一个最简单的Web服务器: 用本机实现。ip:127.0.0.1 服务器程序接收到访问请求,然后返回给他一个网页open函数。 */ /* 实现一个最简单的Web服务器: 用本机实现。ip:127.0.0.1 服务器程序接收到访问...
     
    

    整体思路:

    1.建立一个socket。
    2.创建结构体地址,初始化这个地址为本机地址(“127.0.0.1”)。
    3.然后把socket和这个地址绑定。(bind函数),因为采用TCP形式通信,所以绑定后为,ip地址+端口号,这里设置的是8888。
    4.上面创建好的地址是我收东西的地址,我开始监听这个端口(listen)。
    5.while(1)循环监听,创建一个新的地址,放accept里面可以显示地址来自哪里,这里accept相当于接收到了传过来的socket。
    6.返回信息,有很多返回信息的方法,有read,write什么的,我初学觉得recv和send还是比较好用的。一个接收客户端传来的信息,一个向发送方发送信息。
    这里有关于这两个函数的详解:here
    7.这个程序放在linux运行,运行成功后,打开浏览器访问本机地址。
    http://localhost:8888/

    /*
    	实现一个最简单的Web服务器:
    	用本机实现。ip:127.0.0.1
    	服务器程序接收到访问请求,然后返回给他一个网页open函数。
    */
    /*
    	实现一个最简单的Web服务器:
    	用本机实现。ip:127.0.0.1
    	服务器程序接收到访问请求,然后返回给他一个网页open函数。
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<assert.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<netinet/in.h>
    #include<arpa/inet.h> 
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/sendfile.h>
    using namespace std;
    #define MYPORT 8888
    int main(){
    	struct sockaddr_in ads; //设置地址
    	bzero(&ads, sizeof(ads));
    	ads.sin_family = AF_INET; //ipv4形式
    	ads.sin_port = htons(MYPORT); //主机字节序转换成网络字节序 其实就是高位和低位的反转
    	//ads.sin_addr.s_addr = inet_addr("127.0.0.1"); //从哪个ip地址来的
    	ads.sin_addr.s_addr = htonl(INADDR_ANY); //从哪个ip地址来的
    	int sid = socket(AF_INET,SOCK_STREAM,0);  //设置套接字
    	assert(sid>=0);
    	cout << "socket successful" << endl;
    
    	int bind_ok = bind(sid,(struct sockaddr*)&ads,sizeof(struct sockaddr));
    	//绑定了ip地址和套接字  那么就可以接收了
    	assert(bind_ok != -1);
    	int listen_ok=listen(sid, 10); //开始监听是否有连接
    	assert(listen_ok !=-1);
    	cout << "listen successful" << endl;
    	while (1) {   //不断执行  你发我还会执行
    		struct sockaddr_in client;
    		socklen_t client_addrlength = sizeof(client);
    		int connfd = accept(sid, (struct sockaddr*)&client, &client_addrlength);
    		//一直接收,那个地址存储接收到的信息来自的地址
    		if (connfd < 0){
    			printf("errno\n");
    		}
    		else{
    			char res[1025];
    			string now = "wcx";
    			recv(connfd,res,1024,0);
    			//收到的存储在这里面  在这里接收信息
    			printf("susuccesseful!\n");
    			//char buf[520] = "HTTP/1.1 200 ok\r\nconnection: close\r\n\r\n";//HTTP响应
    			//int s = send(connfd, buf, strlen(buf), 0);//发送响应
    			send(connfd,now.c_str(),strlen(now.c_str()),0);
    			//int fd = open("hello.html", O_RDONLY);//消息体
    			/*
    			o_rdonly read only 只读
    			o_wronly write only 只写
    			o_rdwr read write 可读可写
    			o_trunc 若文件存在则长度被截为0(属性不变)
    			*/
    			//sendfile(connfd, fd, NULL, 2500);//零拷贝发送消息体
    			close(connfd);
    		}
    	}
    	return 0;
    }
    
    

    运行结果:

    正在监听:
    在这里插入图片描述访问本地端口
    在这里插入图片描述
    也可以用curl直接访问,这是个url工具,可以在终端直接获取对应url的信息:

    在这里插入图片描述访问成功。

    现在我们尝试传送一个html文件给客户端

    /*
    	实现一个最简单的Web服务器:
    	用本机实现。ip:127.0.0.1
    	服务器程序接收到访问请求,然后返回给他一个网页open函数。
    */
    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<assert.h>
    #include<stdlib.h>
    #include<fcntl.h>
    #include<unistd.h>
    #include<netinet/in.h>
    #include<arpa/inet.h> 
    #include<sys/socket.h>
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<sys/sendfile.h>
    using namespace std;
    #define MYPORT 8888
    int main(){
    	struct sockaddr_in ads; //设置地址
    	bzero(&ads, sizeof(ads));
    	ads.sin_family = AF_INET; //ipv4形式
    	ads.sin_port = htons(MYPORT); //主机字节序转换成网络字节序 其实就是高位和低位的反转
    	//ads.sin_addr.s_addr = inet_addr("127.0.0.1"); //从哪个ip地址来的
    	ads.sin_addr.s_addr = htonl(INADDR_ANY); //从哪个ip地址来的
    	int sid = socket(AF_INET,SOCK_STREAM,0);  //设置套接字
    	assert(sid>=0);
    	cout << "socket successful" << endl;
    
    	int bind_ok = bind(sid,(struct sockaddr*)&ads,sizeof(struct sockaddr));
    	//绑定了ip地址和套接字  那么就可以接收了
    	assert(bind_ok != -1);
    	int listen_ok=listen(sid, 10); //开始监听是否有连接
    	assert(listen_ok !=-1);
    	cout << "listen successful" << endl;
    	while (1) {   //不断执行  你发我还会执行
    		struct sockaddr_in client;
    		socklen_t client_addrlength = sizeof(client);
    		int connfd = accept(sid, (struct sockaddr*)&client, &client_addrlength);
    		//一直接收,那个地址存储接收到的信息来自的地址
    		if (connfd < 0){
    			printf("errno\n");
    		}
    		else{
    			char res[1025];
    			string now = "wcx";
    			recv(connfd,res,1024,0);
    			//收到的存储在这里面  在这里接收信息
    			printf("susuccesseful!\n");
    			char buf[520] = "HTTP/1.1 200 ok\r\nconnection: close\r\n\r\n";//HTTP响应
    			int s = send(connfd, buf, strlen(buf), 0);//发送响应
    			//send(connfd,now.c_str(),strlen(now.c_str()),0);
    			int fd = open("hello.html", O_RDONLY);//消息体 当然也可以写别的如txt文件
    			/*
    			o_rdonly read only 只读
    			o_wronly write only 只写
    			o_rdwr read write 可读可写
    			o_trunc 若文件存在则长度被截为0(属性不变)
    			*/
    			sendfile(connfd, fd, NULL, 2500);//零拷贝发送消息体  这里改变了
    			close(connfd);
    		}
    	}
    	return 0;
    }
    

    和上面的程序差别只是在发送程序中的open函数和sendfile函数的应用,以及http响应报文,不建立连接可以传送。但是没有明显制定协议,没法解析成html会直接按照文本形式显示,先连接后用http协议可以方便解析html代码(个人理解),主要是建立连接的重要性。
    先写好一个简单的html程序:

    <html>
        <body bgcolor="blue">
         this is the html.
         <hr>
         <p>hello word! waste young! </p><br>
        </body>
    </html>
    

    在这里插入图片描述
    和主函数在一个文件夹中,然后剩下的运行步骤和上面一样,如果用网页打开,浏览器会识别html语言:

    用浏览器去访问:在这里插入图片描述
    用curl去访问(不会识别返回内容):
    在这里插入图片描述

    参考博客:here

    展开全文
  • 基于linux通过c实现web服务器,适合新手 学习了知识点,然后通过项目巩固一下知识点,也可以了解http协议。
  • c++ Web服务器程序 调试已通过 C++初学者可以看看的
  • C++写一个http服务器/web服务器

    万次阅读 2021-02-02 18:39:49
    其实这篇文章一直想写,苦于没有时间,想一气呵成写完,在离回家前一天晚上,在上海的小宾馆里面异常兴奋,写一下如何用C++搭建一个简易的http服务器。 我相信大部分人都希望渴望一个自己的网站(哪怕在破在破也是...

    开篇废话

    其实这篇文章一直想写,苦于没有时间,想一气呵成写完,在离回家前一天晚上,在上海的小宾馆里面异常兴奋,写一下如何用C++搭建一个简易的http服务器。

    我相信大部分人都希望渴望一个自己的网站(哪怕在破在破也是自己做的),我当初是就是这么渴望的,记得那是2020年的8月,我查阅的各种资料终于学会了socket,于是迫不及待的写了一个聊天程序,一直在研究如何端口映射。那时候还很傻很天真,来了一个连接就开一根线程,离开就销毁这根线程,那会也知道线程创建和销毁的开销很大,但是一直没有想到其他的好办法呀。现在回头看这个问题会显得很蠢,咋就没想到用线程池技术呢?学习就是一步一步进化的过程,不去羡慕谁、不去跟任何人比,只跟自己比!

    本篇文章不会涉及到很多复杂的概念,也没有写很难读懂的模板函数,代码简单可读,本篇文章送给每一个想自己用C++写一个http服务器的小伙伴!高手们、大佬们当然可以不用看的啦,因为我目前还是个菜鸟~我会把我再学习http遇到的问题、想法都写在本章中。

    正文

    怎么写一个简单的http服务器阿?很简单,只需要返回最基本的3个东西即可。

    1. 状态码
    2. 发送文件的长度
    3. 发送文件的类型

    状态码如200(找到请求文件)、404(未找到请求文件),我实现的也比较简单,就实现了这两个状态码。

    文件长度比如客户请求的是index.html页面,浏览器如何知道收到的这个文件什么时候结束呢?就靠是文件的长度

    文件类型 html的类型是html格式,css的是css格式,图片有图片的格式,zip有zip格式文件格式对应的文件类型表

    返回给客户端三个这种东西加上请求的文件即可(存在请求文件的情况下)可以了

    还想再复杂的话,可以看这篇介绍http各个参数的文章文章链接

    Http工作流程

    在这个部分大概介绍一下大概的http的工作流程。客户端通过网址访问到你的网站(一定要记住客户端是主动请求连接的,服务端是被动连接的),实则就是通过ip+port访问的,只不过http的默认端口号是80。比如你还没有域名、云服务器这些东西,那如何在本地测试呢?就是在浏览框输入ip:port,例如127.0.0.1:9996,按下回车就可以在本地访问自己的http服务器了。当然了http要给客户(请求者)一个首页,当客户没有指定网页,单纯的打出域名或者127.0.0.1:9996,就给他一个默认的首页,这也是我们要实现的事情。客户写了请求文件,我们来判断是否存在,存在就返回状态码200和请求文件的内容。不存在就直接返回404。那我们如何判断啊,拿最简单的GET为例子吧,其他也大同小异,有兴趣的同学可以自行研究其他的。我们利用正则表达式来解析客户发来的请求是GET还是POST还是其他请求,在解析出来要请求文件。再利用状态机思想来文件是否存在,若存在在判断文件的类型。大概的流程就是这些。

    Http.h

    #pragma once
    #include <string>
    #include <unordered_map>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>
    
    
    class TcpClient;
    
    class Http{
            // 文件的根目录
            const std::string path_ = "./www/dxgzg_src";
            std::string filePath_;// 具体文件的绝对路径
            std::string fileType_;// 请求文件的类型
            std::string header_;  // http头response
            int fileSize_;
            int fileFd_;
    
            struct stat fileStat_;
    
            // POST请求的话将留言保存到本地文件中
            bool isPostMode_ = false;
    public:
            Http() = default;
            void addHeader(const std::string& head);
            void Header(bool flag);
            // 把一些头文件的信息都加进来,只有成功的时候调用这个函数,
            // 并返回文件中的数据
            void processHead();
    
            // 把请求文件的路径加上
            void addFilePath(const std::string& requestFile);
    
            // 获取文件的类型
            void analyseFileType(const std::string& requestFile);
    
    
            bool analyseFile(const std::string& request);
    
            void SendFile(int clientFd,bool isRequestOk);
    
            bool fileIsExist();
    
            // 用户自定义的回调函数要正确的处理异常和自己负责关闭套接字
            void ReadCallback(TcpClient* t);
    };
    

    Http.cpp

    #include "Http.h"
    #include "TcpClient.h"
    #include "Logger.h"
    
    
    #include <regex>
    #include <sys/socket.h>
    #include <sys/sendfile.h>
    #include <fcntl.h>
    #include <unordered_map>
    // 用于test
    #include <iostream>
    
    using namespace std;
    void Http::addHeader(const string& head)
    {
        if (!head.empty())
        {
            header_ += head;
            header_ += "\r\n";
        // Console.WriteLine("我在这里 head!= null" + header_);
        }
        // 自动加个结尾
        else
        {
            header_ += "\r\n";
        // Console.WriteLine("我在这里 head == null" + header_);
        }
    }
    
    void Http::Header(bool flag)
    {
        // 判断要发送的头部 true 表示200 false 表示404
        if(flag == true)
        { 
            header_ = "HTTP/1.1 200 OK\r\n";
        }
        else
        {
            header_ = "HTTP/1.1 404 NOTFOUND\r\nContent-Length:0\r\n\r\n";
        }
    }
    
    void Http::processHead()
    {
        string ContentType = "Content-Type:";
        if (fileType_ == "html")
        {
            ContentType += "text/html";
        }
        else if(fileType_ == "js")
        {
            ContentType += "application/x-javascript";
        }
        else if(fileType_ == "css")
        {
            ContentType += "text/css";
        }
        else if(fileType_=="jpg" || fileType_== "png")
        {
            ContentType += "image/" + fileType_;
        }
        else if (fileType_== "zip" || fileType_ == "tar")
        {
            ContentType += "application/" + fileType_;
        }
        addHeader(ContentType);
    
        // 代完善,要打开文件 filePath_是请求文件的路径
        fileSize_= fileStat_.st_size;
        string ContentLength = "Content-Length:" + to_string(fileSize_);
        addHeader(ContentLength);
        // 最后加了一个结尾
        addHeader("");
        // Console.WriteLine("process fileContent_:" + );
    }
    
    void Http::addFilePath(const string& requestFile)
    {
        filePath_ += requestFile;
    }
    
    void Http::analyseFileType(const string& requestFile)
    {
        for (int i = 0; i < requestFile.size(); ++i)
        {
            if (requestFile[i] == '.')
            {
                // 获取请求文件以什么结尾的
                fileType_ = requestFile.substr(i + 1);
            }
        }
    }
    
    bool Http::fileIsExist(){
        fileFd_ = ::open(filePath_.c_str(),O_CLOEXEC | O_RDWR);
        if (fileFd_ < 0)
        {   // 说明为找到请求的文件
            return false;
        }
        return true;
    }
    
    bool Http::analyseFile(const string& request)
    {
        // 调用header的
        // 在[]的^是以什么什么开头,放在[]里面的是非的意思
        string pattern = "^([A-Z]+) ([A-Za-z./1-9-]*)";
        regex reg(pattern);
        smatch mas;
        regex_search(request,mas,reg);
        // 因为下标0是代表匹配的整体
        if(mas.size() < 3){
            LOG_INFO("不是正常请求");
            // 啥都不是直接返回false
            return false;
        }
        string requestMode = mas[1];
        if(requestMode == "POST"){
            isPostMode_ = true;
            cout << "POST请求!!!!!" << endl;
        }
        // 请求的具体文件
        string requestFile = mas[2];
        // 先获取请求的文件
    
        bool flag;
        if (requestFile == "/")
        { // 如果是/的话就给默认值
            filePath_.clear(); // 先清个零
            filePath_ = path_;
            filePath_ += "/run.html";
            // 文件的类型也要给人家加上
            fileType_ = "html"; 
        }
        else
        {
            filePath_.clear(); // 先清个零
            filePath_ = path_;
            addFilePath(requestFile);
            // 利用open函数
            
        }
        flag = fileIsExist();
        // 未找到文件的话
        if(!flag){
            LOG_INFO("未找到客户要的文件");
            cout << filePath_ << endl;
            return false;
        }
        ::fstat(fileFd_,&fileStat_);
        // 如果文件不存在的话也就不需要解析类型
        analyseFileType(requestFile);
        return true;
    }
    
    
    void Http::SendFile(int clientFd,bool isRequestOk)
    {
        long len = 0;
        // 头部一定是有的。
        while(len < header_.size()){
            len += ::send(clientFd,header_.c_str(),header_.size(),0);
            cout << "len header" << header_ <<endl;
        }
        // 发完了头,在发请求文件的信息。如果是404这里是没有的
        if (isRequestOk == true)
        {
            len = 0;
    	int num = 0;
           	int tmpLen = 0;// 连续好几次没变的话就加一个num
    	 while (len < fileSize_)
            {
    	    // 发送的文件个数已经写入在len当中了 
                ::sendfile(clientFd,fileFd_,(off_t*)&len,fileStat_.st_size- len);
                cout << "len sendfile" <<"len:" << len << "fileSize" << fileSize_ <<endl;
                if(len <= 0 ){
    		break;
    	    }
    	    if(tmpLen == len){
    		++num;
    		if(num > 10){
    			break;
    		}
    	    }
    	    tmpLen = len;
    	}
    
        }
    
    }
    
    void Http::ReadCallback(TcpClient* t){
        cout << "ReadCallback" << endl;
        int  sockFd = t->getFd();
        char buff[1024];
        int r = ::recv(sockFd,buff,sizeof(buff),0);
        if (r == 0)
        {
            t->CloseCallback();
            return;
        }
        buff[r] = '\0';
        string str = buff;
        cout << str << endl;
        // 未找到文件直接回应404.
        bool flag = analyseFile(str);
        Header(flag);
        if(!flag){
            SendFile(sockFd,false);
           // t->CloseCallback();
            return ;
        }
        // 这个修改头文件的,先调用这个
        processHead();
        //这是文件找到了发送的
        SendFile(sockFd,true);
    
        if(isPostMode_){
            int fd = ::open("./postLog/message.txt",O_RDWR);
            if(fd < 0){
                LOG_ERROR("未找到文件");
                
            }
            else{
                // 文件偏移到末尾
                ::lseek(fd,0,SEEK_END);
                ::write(fd,str.c_str(),str.size());
                close(fd);
            }
            isPostMode_ = true;
        }
    
        // 关闭文件套接字
        close(fileFd_);
        // 发完就关闭连接,主要是为了多去几个线程还能跑的快一些
        //t->CloseCallback();
    }
    
    

    不考虑高并发的情况,设计一个同步阻塞的epoll即可,看完http必备的三要素已经能够写出一个服务器了,我的底层socket采用的是自己封装的网络库,Reactor模型,one loop per thread的代码文件比较多,所以就没有放上来,但只要把状态码、文件类型(那一大段if)、文件的长度这三个实现了就可以搭建一个简易的http服务器了。可以利用sendfile零拷贝来发送文件

    在补充一点就是,http协议是\r\n结尾。最后还有一个\r\n,就比如404,HTTP/1.1 404 NOTFOUND\r\nContent-Length:0\r\n\r\n,最后面再跟一个\r\n,结束一段跟一个\r\n

    个人网站链接

    个人网站

    源码地址

    2021.10.25更,最近终于有空把自己的代码重构了一下,感觉写的还可以,附上链接github链接有兴趣可以点个星哈哈哈哈,可以去github上看一看

    展开全文
  • 简单web服务器的实现(C++

    万次阅读 多人点赞 2018-09-20 20:41:25
    一、具体功能实现 GET方法请求解析 POST方法请求解析 返回请求资源页面 利用GET方法实现加减法 ...也未用到RAII等机制,纯属是披着C++皮的C语言项目,类的封装也不是太好。也未经过压力、性能等测试。 二、什么...
  • C++Http上传文件到web服务器,解决中文参数乱码问题,微软提供的Chttpconnect 类在请求http头参数是中文时出现乱码
  • c++ 实现,在 linux 平台测试通过,windows 上原理是一样的,下载上传大文件没有问题,
  • C++Web通讯接口

    2018-12-17 13:58:17
    windows平台C++web平台通讯接口,可使用域名或ip+port的形式访问web端,通过参数决定使用POST还是GET方法
  • 使用c++通过http协议调用web接口,可以调用基于http协议的所有web接口,支持linux和windows,同时有测试样例在文件中。
  • c++实现web服务框架

    千次阅读 2017-12-14 11:43:00
    lamada表达式  声明一个返回数组指针的函数  返回指针数组的函数形式如下所示:    括号必须存在        注意->后不能加() Lambda表达式       ... ...
  • Web服务器C++实现

    热门讨论 2013-02-25 10:46:33
    Web服务器C++实现, Web server, webserver实现,
  • 作者初衷是编写一个web框架支持C++开发cgi程序,于是cppweb诞生了。 作者希望cppweb是一个大一统的框架,即可用于传统服务端编程也可用于web编程,作者希望http协议能做的更多,框架包括以下两个核心服务web...
  • c++框架crow搭建web服务入门

    千次阅读 2020-06-18 17:11:51
    环境 Ubuntu gcc 7.5.0 按照github官方的安装 cd /usr/local git clone https://github.com/ipkn/crow.git cd crow mkdir build cd build cmake .. ...[ 4%] Built target amalgamation ...[ 19%] Buil
  • Linux C++ 实现简易小型的Web服务器httpd

    万次阅读 多人点赞 2016-12-29 22:04:47
    在这个简易版的Web服务器中,实现了GET静态数据,以及动态数据。  下面的是代码,其中有注释,可以很容易看明白。  #include #include #include #include #include #include #include #include #include...
  • C++11实现的高性能静态web服务器

    千次阅读 2019-02-21 21:30:08
    这是个人的C++网络编程项目,在开始做这个项目之前,花时间读了一些C++后台方向的书,包括 《C++Primer》 《深度探索C++对象模型》 《Effective C++》 《STL源码剖析》 《UNIX环境高级编程》 《TCP/IP网络编程》 ...
  • C++ 编写WebService服务

    千次阅读 2018-10-15 10:05:05
    绝大多数的C++web服务工具包提供一组API函数类库来处理特定的SOAP数据结构,这样就使得用户必须改变程序结构来适应相关的类库。这里我主要通过C/S模式调用GSOAP来实现服务端和客户端。首先我的目标是提供一组简单...
  • WebsocketPP是一个使用C++编写的开源Web服务器框架,具体实现则是用的大名鼎鼎的boost::asio。ASIO是一个跨平台的网络库,Windows上底层实现使用的是重叠I/O。
  • Linux下C++轻量级Web服务器,助力初学者快速实践网络编程,搭建属于自己的服务器. 使用 线程池 + 非阻塞socket + epoll(ET和LT均实现) + 事件处理(Reactor和Proactor均实现) 的并发模型 使用状态机解析HTTP请求报文...
  • 本文承接上文《使用C++制作简单的web服务器》,把web服务器做的功能稍微强大些,主要增加的功能是从文件中读取网页并返回给客户端,而不是把网页代码写死在代码中,有需要的小伙伴来参考下吧。
  • C++ webserver接口开发示例代码(详细注释),网上搜索大量资源后整理出来分享给大家,内附详细教程,资源宝贵!
  • C++ asio实现的简单web server。
  • C++ 开发 Web 服务框架 - HTTPS 的原理及其 Web 框架的设计与实现 一、概述 项目介绍 服务器开发中 Web 服务是一个基本的代码单元,将服务端的请求和响应部分的逻辑抽象出来形成框架,能够做到最高级别的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 277,452
精华内容 110,980
关键字:

c++web服务

c++ 订阅