目录
1.Http的基本代码
1.1 HttpServer.hpp
1.2 简单测试一下
1.3 用telnet测试一下
1.4 用浏览器访问
1.5 返回相应的过程(网页版本)编辑
1.5.1 再次用浏览器访问
1.6 返回相应的过程(文件版本)
1.6.1网页
1.6.2 测试
1.7 反序列化截取
1.7.1 解析第一行 vector 0号下标里的字符串
1.7.1.1 stringstream分割符号
1.7.1.2 首页的设置
1.7.2 访问别的网页(这里有3张网页 )
1.8 跳转网络链接编辑
1.8.1 跳转到自己创建的网页
2.Http的细节字段
3.HTML 表单,输入账号和密码
3.1 get方法(不私密)
3.2 post方法
4. HTTP常见的Header编辑
5.长短链接
http-10)-toc" style="margin-left:40px;">5.1 短连接(HTTP 1.0)
C/C++ 中的短连接示例:
http-11)-toc" style="margin-left:40px;">5.2 长连接(HTTP 1.1)
C/C++ 中的长连接示例:
5.3 关键区别总结
5.4 长短连接的选择
6.图片
编辑
8.HTTP协议
8.1 认识URL(网址)
8.2 urlencode和urldecode
9.HTTP协议格式
10.HTTP的方法
11.HTTP的状态码
12.HTTP常见Header
13.最简单的HTTP服务器
抓包工具
https://i-blog.csdnimg.cn/direct/b167d19fc22046cd84ffcb8deb708993.png" width="195" />
https://i-blog.csdnimg.cn/direct/94c9493a47f44f07adc29a483ba71329.png" width="118" />
1.Http的基本代码
https://i-blog.csdnimg.cn/direct/fd950bb6f9514e178bdb7ca60a792dc2.png" width="249" />
1.1 HttpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include "Socket.hpp"
#include "logs/ljwlog.h"using namespace std;
static const int dafaultport = 8080;struct ThreadData
{int sockfd;
};class HttpServer
{
public:HttpServer(uint16_t port = dafaultport):port_(port){}bool Start(){listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();for(;;){string clientip;uint16_t clientport;int sockfd = listensock_.Accept(&clientip, &clientport);pthread_t tid;ThreadData *td = new ThreadData();td->sockfd = sockfd;pthread_create(&tid, nullptr, ThreadRun, td);}}//把收到的信息打印出来static void *ThreadRun(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);char buffer[10240];//跟read用法很像ssize_t n = recv(td->sockfd, buffer, sizeof(buffer), 0);if(n > 0){buffer[n] = 0;cout<< buffer;}close(td->sockfd);delete td;return nullptr;}~HttpServer(){}
private:uint16_t port_;Sock listensock_;
};
1.2 简单测试一下
https://i-blog.csdnimg.cn/direct/67b8d940a4bc42f285b78379e0deb4af.png" width="1200" />
https://i-blog.csdnimg.cn/direct/3440c93d7e2e4bbc8d6445394c6c1878.png" width="1200" />
1.3 用telnet测试一下
https://i-blog.csdnimg.cn/direct/29d4190d36724fb799a3315f2a8205a9.png" width="1200" />
1.4 用浏览器访问
https://i-blog.csdnimg.cn/direct/f6fe6f42ef6c49fbadaed8c34faf5e75.png" width="661" />
1.5 返回相应的过程(网页版本)https://i-blog.csdnimg.cn/direct/087600e6117d42cb9e3f17b275e7fbbf.png" width="877" />
https://i-blog.csdnimg.cn/direct/a0497949eb744de7806861075c77535d.png" width="1200" />
1.5.1 再次用浏览器访问
https://i-blog.csdnimg.cn/direct/103c4a6deb4748a2a0d24692b3dbdef1.png" width="700" />
1.6 返回相应的过程(文件版本)
https://i-blog.csdnimg.cn/direct/5afbd5ea4a08403eb3c45f2a091aa23b.png" width="1155" />
1.6.1网页
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" const="width=device-width,initial-scale=1.0"><title>Document</title>
</head>
<body><h1>hello world</h1>
</body>
</html>
https://i-blog.csdnimg.cn/direct/7afce46979094587b4dcdc3497217aec.png" width="1200" />
https://i-blog.csdnimg.cn/direct/6be896d3b1cd4194966859d22c9462c4.png" width="1200" />
1.6.2 测试
https://i-blog.csdnimg.cn/direct/f24faafc054e40348ca2db293ffef5d0.png" width="928" />
更改一下写的网页,服务器不用关,浏览器刷新一下就可以
https://i-blog.csdnimg.cn/direct/cbd4285aed214ce8ba07abe7c4098dd5.png" width="1200" />
https://i-blog.csdnimg.cn/direct/6339d6d0704342a4a10231b08f14cb1c.png" width="858" />
https://i-blog.csdnimg.cn/direct/8626d1cde6604337aea5e124c9c3a4d0.png" width="621" />
1.7 反序列化截取
https://i-blog.csdnimg.cn/direct/ee607a17087844569e49a6426d185667.png" width="618" />
https://i-blog.csdnimg.cn/direct/2f8e2ce0865b4e1695bf1f90893c1d09.png" width="1200" />
https://i-blog.csdnimg.cn/direct/ca6f873602b246b381e3b3793a10ea1f.png" width="1177" />
1.7.1 解析第一行 vector 0号下标里的字符串
https://i-blog.csdnimg.cn/direct/3a49a5e4e5d84145b0b4bd81d9afd2f5.png" width="988" />
https://i-blog.csdnimg.cn/direct/64ecbf2e190d477f84c35653225030fc.png" width="972" /> https://i-blog.csdnimg.cn/direct/6647b8a67d634ee5bb6d4d6a165b706e.png" width="1030" />
https://i-blog.csdnimg.cn/direct/6095fcff78d347739d9cb3678bf5d247.png" width="1200" /> https://i-blog.csdnimg.cn/direct/abfada254327406293eed6690930aa79.png" width="990" />
1.7.1.1 stringstream分割符号
stringstream流式分隔符:stringstream - C++ Reference (cplusplus.com)
https://i-blog.csdnimg.cn/direct/78b5d28c09ab4841a81fe46230214594.png" width="972" />
1.7.1.2 首页的设置
https://i-blog.csdnimg.cn/direct/2a0184f4b645471ca8692740a5b4013c.png" width="1200" />
https://i-blog.csdnimg.cn/direct/2279672bd6844dd7af39d54620a7a921.png" width="934" />
https://i-blog.csdnimg.cn/direct/c3f8deb8501d4649a336c4c0323eaa6d.png" width="1200" />https://i-blog.csdnimg.cn/direct/816656610be84561b45d6182dd419e3e.png" width="1200" />
1.7.2 访问别的网页(这里有3张网页 )
https://i-blog.csdnimg.cn/direct/ddae1295c676430d8bd22ff5df76432f.png" width="220" />
https://i-blog.csdnimg.cn/direct/962c033778364e9f885e5a9432f15b8b.png" width="709" />
先开启服务器
https://i-blog.csdnimg.cn/direct/34105ee1db1a468785e873912be495c1.png" width="633" />
https://i-blog.csdnimg.cn/direct/aba74cbc35554f9c90cfa93988da51a8.png" width="1200" />
1.8 跳转网络链接https://i-blog.csdnimg.cn/direct/6f4898e12c9d47b0a077ac77a162d95a.png" width="1200" />
https://i-blog.csdnimg.cn/direct/36a8c85277b149efb5a5bad4e91efb9a.png" width="685" />
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" const="width=device-width,initial-scale=1.0"><title>Document</title>
</head>
<body><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><a href="https://blog.csdn.net/2401_83427936?spm=1000.2115.3001.5343">Ljw的博客链接</a>
</body>
</html>
1.8.1 跳转到自己创建的网页
https://i-blog.csdnimg.cn/direct/b78e448b1a7745c38f7bb3f22a6a396c.png" width="1200" />
https://i-blog.csdnimg.cn/direct/4bee52f7131e4bcda214998770d4f875.png" width="853" />
2.Http的细节字段
3.HTML 表单,输入账号和密码
在这之间输入
https://i-blog.csdnimg.cn/direct/4e9dbd5e969542ac99748ffa9bb97abd.png" width="1200" />
HTML <form> 标签_w3cschoolhttps://i-blog.csdnimg.cn/direct/f1c185c123a744a6bd8c7a347db4e716.png" width="1002" />
3.1 get方法(不私密)
https://i-blog.csdnimg.cn/direct/6b85c3eacb0d4ce6a0b26ea20ab89771.png" width="1200" />
get后会把自己输入的账号密码放进链接后面了
https://i-blog.csdnimg.cn/direct/af24caa20fed4ba4b92d839b8caefa04.png" width="1086" />
3.2 post方法
https://i-blog.csdnimg.cn/direct/9d62bddae17e49e7bb6a02b531865768.png" width="1200" />
https://i-blog.csdnimg.cn/direct/03b53bd6f57f45fd9284c2d10dec52af.png" width="553" />
4. HTTP常见的Headerhttps://i-blog.csdnimg.cn/direct/8347457a3f1e477dbb67542960933fb1.png" width="1200" />
https://i-blog.csdnimg.cn/direct/07326d529f9443ebadd49fa37055aea4.png" width="1107" />
5.长短链接
https://i-blog.csdnimg.cn/direct/6fded9eb810544aca5a31e56445f9e3e.png" width="516" />
https://i-blog.csdnimg.cn/direct/ff9dade0d30a4172a6cc68c4f56cb932.png" width="1200" />
https://i-blog.csdnimg.cn/direct/104a82cf0f454d3a86fde6ac9d146aa1.png" width="907" />
在 HTTP协议 中,长连接和短连接的概念主要体现在连接的持久性上。具体来说,HTTP协议的长短连接关系到客户端和服务器之间的连接是否保持持久,连接是否在多个请求之间复用。
http-10)">5.1 短连接(HTTP 1.0)
在HTTP/1.0中,默认情况下每个请求都建立一个新的连接,通信完成后连接就会立即关闭。这种方式被称为 短连接。每当客户端发起一个HTTP请求时,服务器会为这个请求创建一个新的TCP连接,并在响应发送完毕后立即关闭这个连接。对于每个请求和响应,都会有一次建立连接、传输数据和断开连接的过程。
短连接的特点:
- 每个请求都需要建立新的TCP连接。
- 每次请求响应后,连接都会被关闭。
- 适用于请求量不大的场景,因为每次建立和断开连接会带来一定的性能开销。
C/C++ 中的短连接示例:
假设你使用C/C++编写一个HTTP客户端发起HTTP请求并接收响应:
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>#define PORT 80int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {std::cerr << "Socket creation failed!" << std::endl;return -1;}sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = inet_addr("93.184.216.34"); // example.com IPif (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {std::cerr << "Connection failed!" << std::endl;return -1;}const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";send(sockfd, request, strlen(request), 0);char buffer[1024];int bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);buffer[bytes_received] = '\0';std::cout << "Response:\n" << buffer << std::endl;close(sockfd); // Connection is closed after responsereturn 0;
}
在这个短连接示例中,我们使用HTTP/1.0协议,每次请求完毕后,TCP连接会立即关闭。
http-11)">5.2 长连接(HTTP 1.1)
HTTP/1.1引入了 长连接(Keep-Alive) 的概念,在这种模式下,连接不会在每个请求后关闭,而是保持打开状态,可以复用相同的连接来处理多个请求。这意味着客户端和服务器之间的连接可以用于多个请求和响应,直到明确关闭连接。
默认情况下,HTTP/1.1会使用长连接,但可以通过设置请求头 Connection: close
来显式关闭连接。
长连接的特点:
- 连接在多个请求/响应之间保持活跃。
- 节省了每次请求建立和关闭连接的开销,适用于需要频繁通信的场景。
- 服务器会在一定时间内保持连接,如果长时间没有请求,连接会被关闭。
C/C++ 中的长连接示例:
#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>#define PORT 80int main() {int sockfd = socket(AF_INET, SOCK_STREAM, 0);if (sockfd < 0) {std::cerr << "Socket creation failed!" << std::endl;return -1;}sockaddr_in server_addr;server_addr.sin_family = AF_INET;server_addr.sin_port = htons(PORT);server_addr.sin_addr.s_addr = inet_addr("93.184.216.34"); // example.com IPif (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {std::cerr << "Connection failed!" << std::endl;return -1;}// Send multiple requests over the same connectionconst char *request1 = "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: keep-alive\r\n\r\n";send(sockfd, request1, strlen(request1), 0);char buffer[1024];int bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);buffer[bytes_received] = '\0';std::cout << "Response 1:\n" << buffer << std::endl;const char *request2 = "GET /about HTTP/1.1\r\nHost: example.com\r\nConnection: keep-alive\r\n\r\n";send(sockfd, request2, strlen(request2), 0);bytes_received = recv(sockfd, buffer, sizeof(buffer) - 1, 0);buffer[bytes_received] = '\0';std::cout << "Response 2:\n" << buffer << std::endl;close(sockfd); // Connection can be kept alive, but we close it herereturn 0;
}
在这个长连接的例子中,客户端向服务器发送两个HTTP请求,两个请求共享同一个TCP连接,直到手动关闭连接。通过在请求头中加入 Connection: keep-alive
,客户端表明希望连接保持活跃。
5.3 关键区别总结
- 短连接(HTTP/1.0):每个HTTP请求都建立一个新的TCP连接,通信结束后立即关闭,适用于请求量不大的情况。
- 长连接(HTTP/1.1):连接在多个请求之间保持活跃,可以复用连接,直到服务器或客户端明确关闭连接,适用于需要频繁通信的场景。
5.4 长短连接的选择
- 对于HTTP/1.0,通常使用短连接,尤其是在每个请求相对独立时。
- 对于HTTP/1.1,长连接更为常见,尤其是在需要频繁请求或维持长期连接的场景(如实时应用、文件下载、网页加载等)。
长连接有助于减少连接的建立与关闭的开销,提高通信效率,尤其是在大量小请求的场景中,而短连接则适合一次性的请求和响应模式。
6.图片
https://i-blog.csdnimg.cn/direct/9310ab6054c444d39003ab38cdef9930.png" width="802" />
https://i-blog.csdnimg.cn/direct/5198db9e07f04a24a95504755a9ae42b.png" width="1200" />
https://i-blog.csdnimg.cn/direct/39782104236f4772948bc6bfde556b8c.png" width="1200" />
文件后缀
https://i-blog.csdnimg.cn/direct/e0460792ba2546e8a66cedfdb802b7d0.png" width="1143" />
7.cookie文件(记住密码)
文件级:可以记住一段时间
内存级:关闭就忘记
https://i-blog.csdnimg.cn/direct/b6ec584c85ba4e2aab622cdf0a1c9953.png" width="1200" />
8.HTTP协议
虽然我们说, 应用层协议是我们程序猿自己定的.
但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)
就是其中之一.
8.1 认识URL(网址)
平时我们俗称的 "网址" 其实就是说的 URL
https://i-blog.csdnimg.cn/direct/148406bbf2684d5fbb88e749adb99fe7.png" width="1110" />
8.2 urlencode和urldecode
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式
例如:
https://i-blog.csdnimg.cn/direct/cc548e6befce42408bfdb085eec2736b.png" width="923" />
"+" 被转义成了 "%2B" urldecode就是urlencode的逆过程;
工具:URL 编码和解码 - 在线 (urlencoder.org)
9.HTTP协议格式
HTTP请求
https://i-blog.csdnimg.cn/direct/e94d4d84b2274854a670424630bdd556.png" width="1200" />
- 首行: [方法] + [url] + [版本]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
- Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;
HTTP响应
https://i-blog.csdnimg.cn/direct/9a6ffc6693e94826a61e8871f88750af.png" width="1200" />
- 首行: [版本号] + [状态码] + [状态码解释]
- Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\n分隔;遇到空行表示Header部分结束
- Body: 空行后面的内容都是Body. Body允许为空字符串. 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中.
10.HTTP的方法
https://i-blog.csdnimg.cn/direct/633dc70725224d5f8c0d70549d6e0013.png" width="1200" />
其中最常用的就是GET方法和POST方法.
11.HTTP的状态码
https://i-blog.csdnimg.cn/direct/9f1dd53804624d0087a005bf3dabffd9.png" width="1200" />
最常见的状态码, 比如 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协议的要求构造数据, 就很容易 能做到;
https://i-blog.csdnimg.cn/direct/96ee63501a0d4fb9ad83841dd9279602.png" width="235" />
HttpServer.cc
#include "HttpServer.hpp"
#include<memory>
#include<iostream>using namespace std;
void Usage(const string& proc)
{cout<<"\nUsage" << proc << "port\n\n" <<endl;
}int main(int argc, char* argv[])
{if(argc != 2){Usage(argv[0]);exit(0); }uint16_t port = stoi(argv[1]);unique_ptr<HttpServer> svr(new HttpServer(port));svr->Start();return 0;
}
HttpServer.hpp
#pragma once
#include <iostream>
#include <string>
#include <pthread.h>
#include <pthread.h>
#include <sstream>
#include <vector>
#include "Socket.hpp"
#include "logs/ljwlog.h"using namespace std;
static const int dafaultport = 8080;
const string wwwroot = "./wwwroot";//web根目录
const string sep = "\r\n";
//首页
const string homepage = "index.html";class ThreadData
{
public:ThreadData(int fd): sockfd(fd){}public:int sockfd;
};class HttpRequest
{
public://反序列化 这里把一个字符串变成多个字符串void Deserialize(string req){while(true){//找一行移动一行// 分隔符size_t pos = req.find(sep);if(pos == string::npos){break;}string temp = req.substr(0, pos);//读到空行就直接跳出if(temp.empty()) break;req_header.push_back(temp);req.erase(0, pos + sep.size());}text = req;}void DebugPrint(){cout<<"-----------------------"<<endl;for(auto& line:req_header){cout<< line << "\n\n";}cout<< "method: " << method << endl;cout<< "url" << url <<endl;cout<< "http_version" << http_version <<endl;cout << text;}//解析第一行 vector 0号下标里的字符串void Parse(){stringstream ss(req_header[0]);ss >> method >> url >> http_version;file_path = wwwroot; //if(url == "/" || url == "/index.html"){file_path += "/";file_path += homepage;// ./wwwroot/index.html}else{ file_path += url;// /a/b/c/d.html->./wwwroot/a/b/c/d.html }}public:vector<string> req_header;//把每一行push到req_headerstring text;//正文//解析后的结果 vector 0号下标里的字符串string method;string url;string http_version;string file_path;
};class HttpServer
{
public:HttpServer(uint16_t port = dafaultport):port_(port){}bool Start(){listensock_.Socket();listensock_.Bind(port_);listensock_.Listen();for(;;){string clientip;uint16_t clientport;int sockfd = listensock_.Accept(&clientip, &clientport);if(sockfd < 0) continue;pthread_t tid;ThreadData *td = new ThreadData(sockfd);pthread_create(&tid, nullptr, ThreadRun, td);}}static string ReadHtmlContent(const string& htmlpath){ifstream in(htmlpath);if(!in.is_open()) return "404";string content;string line;while(getline(in, line)){content += line;}in.close();return content;}static void HandlerHttp(int sockfd){char buffer[10240];//跟read用法很像ssize_t n = recv(sockfd, buffer, sizeof(buffer), 0);if(n > 0){buffer[n] = 0;cout<< buffer; //假设我们读到的是一个完整的请求,独立的Http请求HttpRequest req;req.Deserialize(buffer);req.Parse();//解析//req.DebugPrint();// //返回相应的过程string text = ReadHtmlContent(req.file_path);string response_line = "HTTP/1.0 200 OK\r\n ";string response_header = "Content-Length ";response_header = to_string(text.size());response_header += "\r\n";string blank_line = "\r\n";string response = response_line;response += response_header;response += blank_line;response += text;//和write类似send(sockfd, response.c_str(), response.size(), 0);}close(sockfd);}//把收到的信息打印出来static void *ThreadRun(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);HandlerHttp(td->sockfd);delete td;return nullptr;}~HttpServer(){}
private:uint16_t port_;Sock listensock_;
};
makefile
httpServer:HttpServer.ccg++ -g -o $@ $^ -std=c++11 -lpthread
.PHONT:clean
clean:rm -f httpServer
Socket.hpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <pthread.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <cstring>
#include "./logs/ljwlog.h"using namespace std;enum
{SocketErr = 2,BindErr,ListenErr
};
const int backlog = 10;
class Sock
{
public:Sock(){}~Sock(){}public:void Socket() // 创建套接字的接口{sockfd_ = socket(AF_INET, SOCK_STREAM, 0); // 流式套接字 第二个参数是协议类型if (sockfd_ < 0){FATAL("Socket errno,error:%d,errstring:%s", errno, strerror(errno));exit(SocketErr);}}void Bind(uint16_t port) // 绑定的接口{struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port); // 主机转网络local.sin_addr.s_addr = INADDR_ANY; // ip默认0.0.0.0if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0){FATAL("Bind errno,error:%d,errstring:%s", errno, strerror(errno));exit(BindErr);}}void Listen() // 监听状态的接口{if (listen(sockfd_, backlog) < 0){FATAL("Listen errno,error:%d,errstring:%s", errno, strerror(errno));exit(ListenErr);}}// 知道谁链接的我int Accept(string *clientip, uint16_t *clientport) // 获取连接的接口{struct sockaddr_in peer; // 远端的意思socklen_t len = sizeof(peer);int newfd = accept(sockfd_, (struct sockaddr *)&peer, &len);if (newfd < 0){WARN("accept error, %s: %d", strerror(errno), errno);return -1;}// 网络转主机// 拿出客户端的ip和端口号char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr)); // 网络转主机*clientip = ipstr; // 网络转主机*clientport = ntohs(peer.sin_port); // 网络转主机return newfd;}void Close(){close(sockfd_);}int Connect(const string &ip, const uint16_t &port) // 方便两个客户端和服务器都能使用这个Sock的这个公共方法{struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));int n = connect(sockfd_, (struct sockaddr *)&peer, sizeof(peer));if (n == -1){cerr << "connect to" << ip << "::" << port <<"error"<< endl;return false;}return true;}int Fd(){return sockfd_;}private:int sockfd_;
};
wwwroot/a/b/hello.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" const="width=device-width,initial-scale=1.0"><title>Document</title>
</head>
<body><h1>第二个网页</h1><h1>第二个网页</h1><h1>第二个网页</h1><h1>第二个网页</h1><h1>第二个网页</h1><h1>第二个网页</h1>
</body>
</html>
wwwroot/image
https://i-blog.csdnimg.cn/direct/789619fec8e34f9796e2423646a0c67d.png" width="295" />
wwwroot/index.html
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" const="width=device-width,initial-scale=1.0"><title>Document</title>
</head><body><!-- <form action="/a/b/hello.html" method="post">name: <input type="text" name="name"><br>password: <input type="password" name="password"><br><input type="submit" value="提交"></form> --><h1>这是我们的首页</h1><img src="image/OIP-C.jpg" alt="这是一个咖啡"></body></html><!-- <!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" const="width=device-width,initial-scale=1.0"><title>Document</title>
</head>
<body><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><h1>hello world</h1><a href="https://blog.csdn.net/2401_83427936?spm=1000.2115.3001.5343">Ljw的博客链接</a><a href="https://101.34.66.193:8080/a/b/hello.html">第二个网页</a>
</body>
</html> -->
wwwroot/x/y/world.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" const="width=device-width,initial-scale=1.0"><title>Document</title>
</head>
<body><h1>第三个网页</h1><h1>第三个网页</h1><h1>第三个网页</h1><h1>第三个网页</h1><h1>第三个网页</h1><h1>第三个网页</h1>
</body>
</html>