0


HTTP(网络)

抓包工具

1.Http的基本代码

1.1 HttpServer.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <pthread.h>
  5. #include "Socket.hpp"
  6. #include "logs/ljwlog.h"
  7. using namespace std;
  8. static const int dafaultport = 8080;
  9. struct ThreadData
  10. {
  11. int sockfd;
  12. };
  13. class HttpServer
  14. {
  15. public:
  16. HttpServer(uint16_t port = dafaultport):port_(port)
  17. {}
  18. bool Start()
  19. {
  20. listensock_.Socket();
  21. listensock_.Bind(port_);
  22. listensock_.Listen();
  23. for(;;)
  24. {
  25. string clientip;
  26. uint16_t clientport;
  27. int sockfd = listensock_.Accept(&clientip, &clientport);
  28. pthread_t tid;
  29. ThreadData *td = new ThreadData();
  30. td->sockfd = sockfd;
  31. pthread_create(&tid, nullptr, ThreadRun, td);
  32. }
  33. }
  34. //把收到的信息打印出来
  35. static void *ThreadRun(void *args)
  36. {
  37. pthread_detach(pthread_self());
  38. ThreadData *td = static_cast<ThreadData*>(args);
  39. char buffer[10240];
  40. //跟read用法很像
  41. ssize_t n = recv(td->sockfd, buffer, sizeof(buffer), 0);
  42. if(n > 0)
  43. {
  44. buffer[n] = 0;
  45. cout<< buffer;
  46. }
  47. close(td->sockfd);
  48. delete td;
  49. return nullptr;
  50. }
  51. ~HttpServer()
  52. {}
  53. private:
  54. uint16_t port_;
  55. Sock listensock_;
  56. };

1.2 简单测试一下

1.3 用telnet测试一下

1.4 用浏览器访问

1.5 返回相应的过程(网页版本)

1.5.1 再次用浏览器访问

1.6 返回相应的过程(文件版本)

1.6.1网页

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" const="width=device-width,initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>hello world</h1>
  10. </body>
  11. </html>

1.6.2 测试

更改一下写的网页,服务器不用关,浏览器刷新一下就可以

1.7 反序列化截取

1.7.1 解析第一行 vector 0号下标里的字符串

1.7.1.1 stringstream分割符号

stringstream流式分隔符:stringstream - C++ Reference (cplusplus.com)

1.7.1.2 首页的设置

1.7.2 访问别的网页(这里有3张网页 )

先开启服务器

1.8 跳转网络链接

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" const="width=device-width,initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>hello world</h1>
  10. <h1>hello world</h1>
  11. <h1>hello world</h1>
  12. <h1>hello world</h1>
  13. <h1>hello world</h1>
  14. <h1>hello world</h1>
  15. <a href="https://blog.csdn.net/2401_83427936?spm=1000.2115.3001.5343">Ljw的博客链接</a>
  16. </body>
  17. </html>

1.8.1 跳转到自己创建的网页

2.Http的细节字段

3.HTML 表单,输入账号和密码

在这之间输入

HTML 标签_w3cschool

3.1 get方法(不私密)

get后会把自己输入的账号密码放进链接后面了

3.2 post方法

4. HTTP常见的Header

5.长短链接

HTTP协议 中,长连接和短连接的概念主要体现在连接的持久性上。具体来说,HTTP协议的长短连接关系到客户端和服务器之间的连接是否保持持久,连接是否在多个请求之间复用。

5.1 短连接(HTTP 1.0)

在HTTP/1.0中,默认情况下每个请求都建立一个新的连接,通信完成后连接就会立即关闭。这种方式被称为 短连接。每当客户端发起一个HTTP请求时,服务器会为这个请求创建一个新的TCP连接,并在响应发送完毕后立即关闭这个连接。对于每个请求和响应,都会有一次建立连接、传输数据和断开连接的过程。

短连接的特点:

  • 每个请求都需要建立新的TCP连接。
  • 每次请求响应后,连接都会被关闭。
  • 适用于请求量不大的场景,因为每次建立和断开连接会带来一定的性能开销。
C/C++ 中的短连接示例:

假设你使用C/C++编写一个HTTP客户端发起HTTP请求并接收响应:

  1. #include <iostream>
  2. #include <sys/socket.h>
  3. #include <arpa/inet.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #define PORT 80
  7. int main() {
  8. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  9. if (sockfd < 0) {
  10. std::cerr << "Socket creation failed!" << std::endl;
  11. return -1;
  12. }
  13. sockaddr_in server_addr;
  14. server_addr.sin_family = AF_INET;
  15. server_addr.sin_port = htons(PORT);
  16. server_addr.sin_addr.s_addr = inet_addr("93.184.216.34"); // example.com IP
  17. if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
  18. std::cerr << "Connection failed!" << std::endl;
  19. return -1;
  20. }
  21. const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
  22. send(sockfd, request, strlen(request), 0);
  23. char buffer[1024];
  24. int bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
  25. buffer[bytes_received] = '\0';
  26. std::cout << "Response:\n" << buffer << std::endl;
  27. close(sockfd); // Connection is closed after response
  28. return 0;
  29. }

在这个短连接示例中,我们使用HTTP/1.0协议,每次请求完毕后,TCP连接会立即关闭。

5.2 长连接(HTTP 1.1)

HTTP/1.1引入了 长连接(Keep-Alive) 的概念,在这种模式下,连接不会在每个请求后关闭,而是保持打开状态,可以复用相同的连接来处理多个请求。这意味着客户端和服务器之间的连接可以用于多个请求和响应,直到明确关闭连接。

默认情况下,HTTP/1.1会使用长连接,但可以通过设置请求头

  1. Connection: close

来显式关闭连接。

长连接的特点:

  • 连接在多个请求/响应之间保持活跃。
  • 节省了每次请求建立和关闭连接的开销,适用于需要频繁通信的场景。
  • 服务器会在一定时间内保持连接,如果长时间没有请求,连接会被关闭。
C/C++ 中的长连接示例:
  1. #include <iostream>
  2. #include <sys/socket.h>
  3. #include <arpa/inet.h>
  4. #include <unistd.h>
  5. #include <string.h>
  6. #define PORT 80
  7. int main() {
  8. int sockfd = socket(AF_INET, SOCK_STREAM, 0);
  9. if (sockfd < 0) {
  10. std::cerr << "Socket creation failed!" << std::endl;
  11. return -1;
  12. }
  13. sockaddr_in server_addr;
  14. server_addr.sin_family = AF_INET;
  15. server_addr.sin_port = htons(PORT);
  16. server_addr.sin_addr.s_addr = inet_addr("93.184.216.34"); // example.com IP
  17. if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
  18. std::cerr << "Connection failed!" << std::endl;
  19. return -1;
  20. }
  21. // Send multiple requests over the same connection
  22. const char *request1 = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: keep-alive\r\n\r\n";
  23. send(sockfd, request1, strlen(request1), 0);
  24. char buffer[1024];
  25. int bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
  26. buffer[bytes_received] = '\0';
  27. std::cout << "Response 1:\n" << buffer << std::endl;
  28. const char *request2 = "GET /about HTTP/1.1\r\nHost: example.com\r\nConnection: keep-alive\r\n\r\n";
  29. send(sockfd, request2, strlen(request2), 0);
  30. bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
  31. buffer[bytes_received] = '\0';
  32. std::cout << "Response 2:\n" << buffer << std::endl;
  33. close(sockfd); // Connection can be kept alive, but we close it here
  34. return 0;
  35. }

在这个长连接的例子中,客户端向服务器发送两个HTTP请求,两个请求共享同一个TCP连接,直到手动关闭连接。通过在请求头中加入

  1. Connection: keep-alive

,客户端表明希望连接保持活跃。

5.3 关键区别总结

  • 短连接(HTTP/1.0):每个HTTP请求都建立一个新的TCP连接,通信结束后立即关闭,适用于请求量不大的情况。
  • 长连接(HTTP/1.1):连接在多个请求之间保持活跃,可以复用连接,直到服务器或客户端明确关闭连接,适用于需要频繁通信的场景。

5.4 长短连接的选择

  • 对于HTTP/1.0,通常使用短连接,尤其是在每个请求相对独立时。
  • 对于HTTP/1.1,长连接更为常见,尤其是在需要频繁请求或维持长期连接的场景(如实时应用、文件下载、网页加载等)。

长连接有助于减少连接的建立与关闭的开销,提高通信效率,尤其是在大量小请求的场景中,而短连接则适合一次性的请求和响应模式。

6.图片

文件后缀

7.cookie文件(记住密码)

文件级:可以记住一段时间

内存级:关闭就忘记

8.HTTP协议

虽然我们说, 应用层协议是我们程序猿自己定的.

但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)

就是其中之一.

8.1 认识URL(网址)

平时我们俗称的 "网址" 其实就是说的 URL

8.2 urlencode和urldecode

像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.

比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.

转义的规则如下:

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

例如:

"+" 被转义成了 "%2B" urldecode就是urlencode的逆过程;

工具:URL 编码和解码 - 在线 (urlencoder.org)

9.HTTP协议格式

HTTP请求

  • 首行: [方法] + [url] + [版本]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;

HTTP响应

  • 首行: [版本号] + [状态码] + [状态码解释]
  • Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
  • Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.

10.HTTP的方法

其中最常用的就是GET方法和POST方法.

11.HTTP的状态码

最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)

12.HTTP常见Header

  • Content-Type: 数据类型(text/html等)
  • Content-Length: Body的长度
  • Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
  • User-Agent: 声明用户的操作系统和浏览器版本信息;
  • referer: 当前页面是从哪个页面跳转过来的;
  • location: 搭配3xx状态码使用, 告诉客户端接下来要去哪里访问;
  • Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;

13.最简单的HTTP服务器

实现一个最简单的HTTP服务器, 只在网页上输出 "hello world"; 只要我们按照HTTP协议的要求构造数据, 就很容易 能做到;

HttpServer.cc

  1. #include "HttpServer.hpp"
  2. #include<memory>
  3. #include<iostream>
  4. using namespace std;
  5. void Usage(const string& proc)
  6. {
  7. cout<<"\nUsage" << proc << "port\n\n" <<endl;
  8. }
  9. int main(int argc, char* argv[])
  10. {
  11. if(argc != 2)
  12. {
  13. Usage(argv[0]);
  14. exit(0);
  15. }
  16. uint16_t port = stoi(argv[1]);
  17. unique_ptr<HttpServer> svr(new HttpServer(port));
  18. svr->Start();
  19. return 0;
  20. }

HttpServer.hpp

  1. #pragma once
  2. #include <iostream>
  3. #include <string>
  4. #include <pthread.h>
  5. #include <pthread.h>
  6. #include <sstream>
  7. #include <vector>
  8. #include "Socket.hpp"
  9. #include "logs/ljwlog.h"
  10. using namespace std;
  11. static const int dafaultport = 8080;
  12. const string wwwroot = "./wwwroot";//web根目录
  13. const string sep = "\r\n";
  14. //首页
  15. const string homepage = "index.html";
  16. class ThreadData
  17. {
  18. public:
  19. ThreadData(int fd): sockfd(fd)
  20. {}
  21. public:
  22. int sockfd;
  23. };
  24. class HttpRequest
  25. {
  26. public:
  27. //反序列化 这里把一个字符串变成多个字符串
  28. void Deserialize(string req)
  29. {
  30. while(true)
  31. {
  32. //找一行移动一行
  33. // 分隔符
  34. size_t pos = req.find(sep);
  35. if(pos == string::npos)
  36. {
  37. break;
  38. }
  39. string temp = req.substr(0, pos);
  40. //读到空行就直接跳出
  41. if(temp.empty()) break;
  42. req_header.push_back(temp);
  43. req.erase(0, pos + sep.size());
  44. }
  45. text = req;
  46. }
  47. void DebugPrint()
  48. {
  49. cout<<"-----------------------"<<endl;
  50. for(auto& line:req_header)
  51. {
  52. cout<< line << "\n\n";
  53. }
  54. cout<< "method: " << method << endl;
  55. cout<< "url" << url <<endl;
  56. cout<< "http_version" << http_version <<endl;
  57. cout << text;
  58. }
  59. //解析第一行 vector 0号下标里的字符串
  60. void Parse()
  61. {
  62. stringstream ss(req_header[0]);
  63. ss >> method >> url >> http_version;
  64. file_path = wwwroot; //
  65. if(url == "/" || url == "/index.html")
  66. {
  67. file_path += "/";
  68. file_path += homepage;// ./wwwroot/index.html
  69. }
  70. else
  71. {
  72. file_path += url;// /a/b/c/d.html->./wwwroot/a/b/c/d.html
  73. }
  74. }
  75. public:
  76. vector<string> req_header;//把每一行push到req_header
  77. string text;//正文
  78. //解析后的结果 vector 0号下标里的字符串
  79. string method;
  80. string url;
  81. string http_version;
  82. string file_path;
  83. };
  84. class HttpServer
  85. {
  86. public:
  87. HttpServer(uint16_t port = dafaultport):port_(port)
  88. {}
  89. bool Start()
  90. {
  91. listensock_.Socket();
  92. listensock_.Bind(port_);
  93. listensock_.Listen();
  94. for(;;)
  95. {
  96. string clientip;
  97. uint16_t clientport;
  98. int sockfd = listensock_.Accept(&clientip, &clientport);
  99. if(sockfd < 0) continue;
  100. pthread_t tid;
  101. ThreadData *td = new ThreadData(sockfd);
  102. pthread_create(&tid, nullptr, ThreadRun, td);
  103. }
  104. }
  105. static string ReadHtmlContent(const string& htmlpath)
  106. {
  107. ifstream in(htmlpath);
  108. if(!in.is_open()) return "404";
  109. string content;
  110. string line;
  111. while(getline(in, line))
  112. {
  113. content += line;
  114. }
  115. in.close();
  116. return content;
  117. }
  118. static void HandlerHttp(int sockfd)
  119. {
  120. char buffer[10240];
  121. //跟read用法很像
  122. ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);
  123. if(n > 0)
  124. {
  125. buffer[n] = 0;
  126. cout<< buffer; //假设我们读到的是一个完整的请求,独立的Http请求
  127. HttpRequest req;
  128. req.Deserialize(buffer);
  129. req.Parse();//解析
  130. //req.DebugPrint();
  131. // //返回相应的过程
  132. string text = ReadHtmlContent(req.file_path);
  133. string response_line = "HTTP/1.0 200 OK\r\n ";
  134. string response_header = "Content-Length ";
  135. response_header = to_string(text.size());
  136. response_header += "\r\n";
  137. string blank_line = "\r\n";
  138. string response = response_line;
  139. response += response_header;
  140. response += blank_line;
  141. response += text;
  142. //和write类似
  143. send(sockfd, response.c_str(), response.size(), 0);
  144. }
  145. close(sockfd);
  146. }
  147. //把收到的信息打印出来
  148. static void *ThreadRun(void *args)
  149. {
  150. pthread_detach(pthread_self());
  151. ThreadData *td = static_cast<ThreadData*>(args);
  152. HandlerHttp(td->sockfd);
  153. delete td;
  154. return nullptr;
  155. }
  156. ~HttpServer()
  157. {}
  158. private:
  159. uint16_t port_;
  160. Sock listensock_;
  161. };

makefile

  1. httpServer:HttpServer.cc
  2. g++ -g -o $@ $^ -std=c++11 -lpthread
  3. .PHONT:clean
  4. clean:
  5. rm -f httpServer

Socket.hpp

  1. #pragma once
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <stdio.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #include <sys/socket.h>
  8. #include <pthread.h>
  9. #include <arpa/inet.h>
  10. #include <sys/types.h>
  11. #include <sys/wait.h>
  12. #include <netinet/in.h>
  13. #include <cstring>
  14. #include "./logs/ljwlog.h"
  15. using namespace std;
  16. enum
  17. {
  18. SocketErr = 2,
  19. BindErr,
  20. ListenErr
  21. };
  22. const int backlog = 10;
  23. class Sock
  24. {
  25. public:
  26. Sock()
  27. {
  28. }
  29. ~Sock()
  30. {
  31. }
  32. public:
  33. void Socket() // 创建套接字的接口
  34. {
  35. sockfd_ = socket(AF_INET, SOCK_STREAM, 0); // 流式套接字 第二个参数是协议类型
  36. if (sockfd_ < 0)
  37. {
  38. FATAL("Socket errno,error:%d,errstring:%s", errno, strerror(errno));
  39. exit(SocketErr);
  40. }
  41. }
  42. void Bind(uint16_t port) // 绑定的接口
  43. {
  44. struct sockaddr_in local;
  45. memset(&local, 0, sizeof(local));
  46. local.sin_family = AF_INET;
  47. local.sin_port = htons(port); // 主机转网络
  48. local.sin_addr.s_addr = INADDR_ANY; // ip默认0.0.0.0
  49. if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0)
  50. {
  51. FATAL("Bind errno,error:%d,errstring:%s", errno, strerror(errno));
  52. exit(BindErr);
  53. }
  54. }
  55. void Listen() // 监听状态的接口
  56. {
  57. if (listen(sockfd_, backlog) < 0)
  58. {
  59. FATAL("Listen errno,error:%d,errstring:%s", errno, strerror(errno));
  60. exit(ListenErr);
  61. }
  62. }
  63. // 知道谁链接的我
  64. int Accept(string *clientip, uint16_t *clientport) // 获取连接的接口
  65. {
  66. struct sockaddr_in peer; // 远端的意思
  67. socklen_t len = sizeof(peer);
  68. int newfd = accept(sockfd_, (struct sockaddr *)&peer, &len);
  69. if (newfd < 0)
  70. {
  71. WARN("accept error, %s: %d", strerror(errno), errno);
  72. return -1;
  73. }
  74. // 网络转主机
  75. // 拿出客户端的ip和端口号
  76. char ipstr[64];
  77. inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr)); // 网络转主机
  78. *clientip = ipstr; // 网络转主机
  79. *clientport = ntohs(peer.sin_port); // 网络转主机
  80. return newfd;
  81. }
  82. void Close()
  83. {
  84. close(sockfd_);
  85. }
  86. int Connect(const string &ip, const uint16_t &port) // 方便两个客户端和服务器都能使用这个Sock的这个公共方法
  87. {
  88. struct sockaddr_in peer;
  89. memset(&peer, 0, sizeof(peer));
  90. peer.sin_family = AF_INET;
  91. peer.sin_port = htons(port);
  92. inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));
  93. int n = connect(sockfd_, (struct sockaddr *)&peer, sizeof(peer));
  94. if (n == -1)
  95. {
  96. cerr << "connect to" << ip << "::" << port <<"error"<< endl;
  97. return false;
  98. }
  99. return true;
  100. }
  101. int Fd()
  102. {
  103. return sockfd_;
  104. }
  105. private:
  106. int sockfd_;
  107. };

wwwroot/a/b/hello.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" const="width=device-width,initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>第二个网页</h1>
  10. <h1>第二个网页</h1>
  11. <h1>第二个网页</h1>
  12. <h1>第二个网页</h1>
  13. <h1>第二个网页</h1>
  14. <h1>第二个网页</h1>
  15. </body>
  16. </html>

wwwroot/image

wwwroot/index.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" const="width=device-width,initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <!-- <form action="/a/b/hello.html" method="post">
  10. name: <input type="text" name="name"><br>
  11. password: <input type="password" name="password"><br>
  12. <input type="submit" value="提交">
  13. </form> -->
  14. <h1>这是我们的首页</h1>
  15. <img src="image/OIP-C.jpg" alt="这是一个咖啡">
  16. </body>
  17. </html>
  18. <!-- <!DOCTYPE html>
  19. <html lang="en">
  20. <head>
  21. <meta charset="UTF-8">
  22. <meta name="viewport" const="width=device-width,initial-scale=1.0">
  23. <title>Document</title>
  24. </head>
  25. <body>
  26. <h1>hello world</h1>
  27. <h1>hello world</h1>
  28. <h1>hello world</h1>
  29. <h1>hello world</h1>
  30. <h1>hello world</h1>
  31. <h1>hello world</h1>
  32. <a href="https://blog.csdn.net/2401_83427936?spm=1000.2115.3001.5343">Ljw的博客链接</a>
  33. <a href="https://101.34.66.193:8080/a/b/hello.html">第二个网页</a>
  34. </body>
  35. </html> -->

wwwroot/x/y/world.html

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <meta name="viewport" const="width=device-width,initial-scale=1.0">
  6. <title>Document</title>
  7. </head>
  8. <body>
  9. <h1>第三个网页</h1>
  10. <h1>第三个网页</h1>
  11. <h1>第三个网页</h1>
  12. <h1>第三个网页</h1>
  13. <h1>第三个网页</h1>
  14. <h1>第三个网页</h1>
  15. </body>
  16. </html>

本文转载自: https://blog.csdn.net/2401_83427936/article/details/144066036
版权归原作者 Ljw... 所有, 如有侵权,请联系我们删除。

“HTTP(网络)”的评论:

还没有评论