• 网络通信:http协议


    虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议) 就是其中之一.

    认识URL

    统一资源定位符(Uniform Resource Locator,缩写:URL),是对资源的引用和访问该资源的方法。俗称网址。

    • 主机名:表示IP地址的注册名称(域名)或IP地址,用于识别连接到网络设备的数字标识符
    • 端口:跟在域名后面,不是一个URL必须的部分,如果省略端口会使用默认端口。
    80是http协议的默认端口,是在输入网站的时候其实浏览器(非IE)已经帮你输入协议了,所以你输入 http://baidu.com,其实是访问 http://baidu.com:80。而8080,一般用与webcahe,完全不一样的两个,比如linux服务器里 apache默认跑80端口,而apache-tomcat默认跑 8080端口,其实端口没有实际意义只是一个接口,主要是看服务的 监听端口。
    • 路径:表示服务器上资源的路径,过去这样的路径标记的是服务器上文件的物理路径,但是现在,路径表示的只是一个抽象地址,并不指代任何物理地址.
    • 参数:请求里提供的额外参数.这些参数是以键值对的形式,通过&符号分隔开来,服务器可以通过这些参数进行相应的个性化处理
    • :用于为页面上的标题提供快速链接,如锚点链接

     urlencode和urldecode

    像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现. 比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.

    转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY 格式

    leetcode中文C++   被转义成了   leetcode%E4%B8%AD%E6%96%87C%2B%2B

    HTTP协议

    HTTP的方法 

     

    创建html文件,输入!并按tab键,可以得到一个简单的模板。

    传输正文时中英文会乱码:

    在html文件的头标签中加入

    添加一个报头  Content-type: text/html

    要获取文件信息可以使用stat接口:

    1. //以文件尺寸为例
    2. struct stat st;
    3. int n = stat(path.c_str(), &st);
    4. if(n == 0) size = st.st_size;
    5. else size = -1;

     网页教程详情见:w3cschool官网 - 编程狮,随时随地学编程

    在Linux中可以使用

    wget  [域名]  

    获取网页数据。

    一个简单的网站服务器

    Util.hpp

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. using namespace std;
    6. class Util
    7. {
    8. public:
    9. // XXXX XXX XXX\r\nYYYYY
    10. static std::string getOneLine(std::string &buffer, const std::string &sep)
    11. {
    12. auto pos = buffer.find(sep);
    13. if(pos == std::string::npos) return "";
    14. std::string sub = buffer.substr(0, pos);
    15. buffer.erase(0, sub.size()+sep.size());
    16. return sub;
    17. }
    18. static bool ReadFile(const string& name, string& body)
    19. {
    20. auto pos = name.find(".");
    21. string typeofFile = name.substr(pos);
    22. ifstream fs(name, fstream::in | fstream::binary);
    23. char c;
    24. while(fs.get(c))
    25. {
    26. body += c;
    27. }
    28. fs.close();
    29. return true;
    30. }
    31. };

    Protocal.hpp

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include "Util.hpp"
    7. const std::string sep = "\r\n";
    8. const std::string default_root = "./wwwroot";
    9. const std::string home_page = "index.html";
    10. class HttpRequest
    11. {
    12. public:
    13. HttpRequest(){}
    14. ~HttpRequest(){}
    15. void parse()
    16. {
    17. // 1. 从inbuffer中拿到第一行,分隔符\r\n
    18. std::string line = Util::getOneLine(inbuffer, sep);
    19. if(line.empty()) return;
    20. // 2. 从请求行中提取三个字段
    21. // std::cout << "line: " << line << std::endl;
    22. std::stringstream ss(line);
    23. ss >> method >> url >> httpversion;
    24. // 3. 添加web默认路径
    25. path = default_root; // ./wwwroot,
    26. path += url; //./wwwroot/a/b/c.html, ./wwwroot/
    27. if(path[path.size()-1] == '/') path += home_page;
    28. // 4. 获取path对应的资源后缀
    29. // ./wwwroot/index.html
    30. // ./wwwroot/test/a.html
    31. // ./wwwroot/image/1.jpg
    32. auto pos = path.rfind(".");
    33. if (pos == std::string::npos)
    34. suffix = ".html";
    35. else
    36. suffix = path.substr(pos);
    37. }
    38. public:
    39. std::string inbuffer;
    40. // std::string reqline;
    41. // std::vector reqheader;
    42. // std::string body;
    43. std::string method;
    44. std::string url;
    45. std::string httpversion;
    46. std::string path;
    47. std::string suffix;
    48. };
    49. class HttpResponse
    50. {
    51. public:
    52. std::string outbuffer;
    53. };

    httpServer.hpp

    1. #pragma once
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #include
    10. #include
    11. #include
    12. #include
    13. #include
    14. #include "Protocol.hpp"
    15. namespace server
    16. {
    17. enum
    18. {
    19. USAGE_ERR = 1,
    20. SOCKET_ERR,
    21. BIND_ERR,
    22. LISTEN_ERR
    23. };
    24. static const uint16_t gport = 8080;
    25. static const int gbacklog = 5;
    26. using func_t = std::function<bool (const HttpRequest &, HttpResponse &)>;
    27. class HttpServer
    28. {
    29. public:
    30. HttpServer(func_t func, const uint16_t &port = gport) : _func(func), _listensock(-1), _port(port)
    31. {
    32. }
    33. void initServer()
    34. {
    35. // 1. 创建socket文件套接字对象
    36. _listensock = socket(AF_INET, SOCK_STREAM, 0);
    37. if (_listensock < 0)
    38. {
    39. exit(SOCKET_ERR);
    40. }
    41. // 2. bind绑定自己的网络信息
    42. struct sockaddr_in local;
    43. memset(&local, 0, sizeof(local));
    44. local.sin_family = AF_INET;
    45. local.sin_port = htons(_port);
    46. local.sin_addr.s_addr = INADDR_ANY;
    47. if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
    48. {
    49. exit(BIND_ERR);
    50. }
    51. // 3. 设置socket 为监听状态
    52. if (listen(_listensock, gbacklog) < 0) // 第二个参数backlog后面在填这个坑
    53. {
    54. exit(LISTEN_ERR);
    55. }
    56. }
    57. void HandlerHttp(int sock)
    58. {
    59. // 1. 读到完整的http请求
    60. // 2. 反序列化
    61. // 3. httprequst, httpresponse, _func(req, resp)
    62. // 4. resp序列化
    63. // 5. send
    64. char buffer[4096];
    65. HttpRequest req;
    66. HttpResponse resp;
    67. size_t n = recv(sock, buffer, sizeof(buffer)-1, 0); // 大概率我们直接就能读取到完整的http请求
    68. if(n > 0)
    69. {
    70. buffer[n] = 0;
    71. req.inbuffer = buffer;
    72. req.parse();
    73. _func(req, resp); // req -> resp
    74. send(sock, resp.outbuffer.c_str(), resp.outbuffer.size(), 0);
    75. }
    76. }
    77. void start()
    78. {
    79. for (;;)
    80. {
    81. // 4. server 获取新链接
    82. // sock, 和client进行通信的fd
    83. struct sockaddr_in peer;
    84. socklen_t len = sizeof(peer);
    85. int sock = accept(_listensock, (struct sockaddr *)&peer, &len);
    86. if (sock < 0)
    87. {
    88. continue;
    89. }
    90. // version 2 多进程版(2)
    91. pid_t id = fork();
    92. if (id == 0) // child
    93. {
    94. close(_listensock);
    95. if(fork()>0) exit(0);
    96. HandlerHttp(sock);
    97. close(sock);
    98. exit(0);
    99. }
    100. close(sock);
    101. // father
    102. waitpid(id, nullptr, 0);
    103. }
    104. }
    105. ~HttpServer() {}
    106. private:
    107. int _listensock; // 不是用来进行数据通信的,它是用来监听链接到来,获取新链接的!
    108. uint16_t _port;
    109. func_t _func;
    110. };
    111. } // namespace server

    httpServer.cc

    1. #include "httpServer.hpp"
    2. #include
    3. using namespace std;
    4. using namespace server;
    5. void Usage(std::string proc)
    6. {
    7. cerr << "Usage:\n\t" << proc << " port\r\n\r\n";
    8. }
    9. // 1. 服务器和网页分离,html
    10. // 2. url -> / : web根目录
    11. bool Get(const HttpRequest &req, HttpResponse &resp)
    12. {
    13. // for test
    14. cout << "----------------------http start---------------------------" << endl;
    15. std::cout << "method: " << req.method << std::endl;
    16. std::cout << "url: " << req.url << std::endl;
    17. std::cout << "httpversion: " << req.httpversion << std::endl;
    18. std::cout << "path: " << req.path << std::endl;
    19. cout << req.inbuffer << std::endl;
    20. cout << "----------------------http end---------------------------" << endl;
    21. std::string respline = "HTTP/1.1 200 OK\r\n";
    22. std::string respheader = "Content-Type: ";
    23. if (req.suffix == ".html")
    24. respheader += "text/html";
    25. else if (req.suffix == ".jpg")
    26. respheader += "application/x-jpg;image/jpeg";
    27. respheader += "\r\n";
    28. std::string respblank = "\r\n";
    29. std::string body;
    30. if(!Util::ReadFile(req.path, body))
    31. {
    32. Util::ReadFile("./wwwroot/404.html", body);
    33. }
    34. resp.outbuffer += respline;
    35. resp.outbuffer += respheader;
    36. resp.outbuffer += respblank;
    37. resp.outbuffer += body;
    38. return true;
    39. }
    40. // ./httpServer 8080
    41. int main(int argc, char *argv[])
    42. {
    43. if(argc != 2)
    44. {
    45. Usage(argv[0]);
    46. exit(0);
    47. }
    48. uint16_t port = atoi(argv[1]);
    49. unique_ptr httpsvr(new HttpServer(Get, port));
    50. httpsvr->initServer();
    51. httpsvr->start();
    52. return 0;
    53. }

    index.html

    1. "en">
    2. "UTF-8">
    3. "X-UA-Compatible" content="IE=edge">
    4. "viewport" content="width=device-width, initial-scale=1.0">
    5. main tittle
    6. 每天都刷leetcode

    7. "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fsafe-img.xhscdn.com%2Fbw1%2F83b66036-a374-424f-ab45-4a02aaf45a4f%3FimageView2%2F2%2Fw%2F1080%2Fformat%2Fjpg&refer=http%3A%2F%2Fsafe-img.xhscdn.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1688195268&t=19cab953a51d9bd85e9b057dc82d7345" alt="测试图片">
    8. "/image/1.jpg" alt="本地图片">
    9. "/image/2.jpg" alt="石榴花">

    3开头的状态——重定向

    重定向流程图:

    实现一个简单的临时重定向:

    1.将服务器返回的状态行改为

    std::string respline = "HTTP/1.1 307 Temporary Redirect\r\n";

     2.在响应报头中加入重定向的域名

    respheader += "Location: https://www.qq.com/\r\n";
    

    浏览器在访问我们的服务器时会自动跳转到QQ。

    表单

    姓名:

    密码:


     使用GET方法和POST方法的区别:

    http长连接

    HTTP长连接(HTTP keep-alive)是一种在单个TCP连接上可以发送多个HTTP请求和响应的机制。它的主要目的是减少建立和关闭TCP连接的开销,提高性能和效率。

    在传统的HTTP协议中,每个HTTP请求都需要建立一个TCP连接,并在请求完成后关闭连接。这意味着在请求的过程中,需要进行多次TCP的三次握手和四次挥手,这些操作会带来一定的开销。而HTTP长连接通过在同一个TCP连接上发送多个HTTP请求,可以避免这些开销,从而提高网络性能。

    HTTP长连接的实现方式如下:

    1. 建立连接:客户端通过发送一个HTTP请求到服务器来建立TCP连接。在请求头中包含"Connection: keep-alive"字段,告诉服务器希望使用长连接。

    2. 多个请求:在建立连接后,客户端可以通过同一个TCP连接发送多个HTTP请求。每个请求都与传统的HTTP请求相同,包括请求行、请求头和请求体。

    3. 响应:服务器接收到每个请求后,会发送对应的HTTP响应给客户端。每个响应包括响应行、响应头和响应体。

    4. 连接保持:在响应完成后,TCP连接并不关闭,而是继续保持打开状态。这样客户端可以通过这个连接发送下一个HTTP请求,而无需重新建立连接。

    5. 关闭连接:当客户端不再需要与服务器通信时,可以发送一个特殊的HTTP请求来关闭连接。在请求头中设置"Connection: close"字段,服务器收到该请求后会关闭TCP连接。

    需要注意的是,尽管HTTP长连接可以在一个TCP连接上发送多个请求和响应,但服务器和客户端都需要注意适当的管理连接,以避免连接过多导致资源浪费。一般来说,服务器会根据一定的策略(如超时时间)来关闭闲置的连接,或者客户端可以在不使用连接时主动关闭连接。

    HTTP长连接在减少网络延迟和提高性能方面具有显著的优势,特别是对于频繁请求的场景,例如网页浏览和API调用等。然而,需要注意的是,长时间的连接可能会占用服务器资源,因此在设计和实现时需要权衡和优化。

    http会话保持

    HTTP协议是无状态的,但是我们在网站上登陆,浏览器会保持登陆状态,这就是会话保持实现的。

    那么服务器是如何识别用户的。

    浏览器将用户信息保留,访问同一个网站时自动推送(cookie技术)

    cookie文件由客户端维护,会被木马盗取。

    新方法:服务端chanssession文件(包含用户信息——账号密码)和session id,浏览器保持session id发送请求时推送id。 

    postman和fiddler

    postman和fiddler都是调试工具。

    postman(模拟浏览器)查看网络信息。

    Fiddler是抓包工具,相当于作为浏览器的代理,隔绝的浏览器和服务器。

  • 相关阅读:
    Ubuntu部署K8S
    【linux】虚拟化
    009:字符串中的整数求和
    12、Mybatis框架-1
    ExtJS 组件查询(Component Query)
    使用Pyarmor保护Python脚本不被反向工程
    JDK1.8 JVM内存模型
    达梦数据库适配ServiceStack框架
    Android 通话常见错误码汇总
    tf.pad解析(参考官方文档)
  • 原文地址:https://blog.csdn.net/JDSZGLLL/article/details/130970540