阅读导航
- 引言
- 一、认识URL
- 二、URL编码和解码
-
- [1. Urlencode(URL编码)](#1. Urlencode(URL编码))
- [2. Urldecode(URL解码)](#2. Urldecode(URL解码))
- 三、HTTP的方法
- 四、HTTP的状态码
- 五、HTTP常见Header
- 六、最简单的HTTP服务器
- 温馨提示
引言
在上一篇文章中,我们深入探讨了"自定义协议"的概念、原理及其应用,并通过一个实际案例------跨网络计算器,展示了自定义协议如何有效地促进不同网络环境下的设备间通信和数据交换。自定义协议的灵活性和高效性为特定场景下的通信提供了强有力的支持。
今天,我们将转换视角,聚焦于互联网世界中最为广泛使用的一种协议------HTTP协议。HTTP(超文本传输协议)是构建万维网(WWW)的基础,它定义了客户端与服务器之间如何进行数据的传输和沟通。通过本文的介绍,我们将了解HTTP协议的基本结构、工作原理以及它在网络通信中的重要性。让我们一起开始这段关于HTTP协议的学习之旅吧。
一、认识URL
URL 代表着是统一资源定位符(Uniform Resource Locator)。URL 无非就是一个给定的独特资源在 Web 上的地址。理论上说,每个有效的 URL 都指向一个唯一的资源。这个资源可以是一个 HTML 页面,一个 CSS 文档,一幅图像,等等。而在实际中,也有一些例外,最常见的情况就是一个 URL 指向了不存在的或是被移动过的资源。由于通过 URL 呈现的资源和 URL 本身由 Web 服务器处理,因此 web 服务器的拥有者需要认真地维护资源以及与它关联的 URL。
一个 URL 由不同的部分组成,其中一些是必须的,而另一些是可选的。让我们以下面这个 URL 为例看看其中最重要的部分:
http
是协议。它表明了浏览器必须使用何种协议。它通常都是 HTTP 协议或是 HTTP 协议的安全版,即 HTTPS。Web 需要它们二者之一,浏览器也知道如何处理其他协议,比如mailto
:(打开邮件客户端)或者ftp
:(处理文件传输),所以当你看到这些协议时,不必惊讶。www.example.com
是域名。它表明正在请求哪个 Web 服务器。或者,可以直接使用IP address,但是因为它不太方便,所以它不经常在网络上使用。:80
是端口。它表示用于访问 Web 服务器上的资源的技术"门"。如果 Web 服务器使用 HTTP 协议的标准端口(HTTP 为 80,HTTPS 为 443)来授予其资源的访问权限,则通常会被忽略。否则是强制性的。/path/to/myfile.html
是网络服务器上资源的路径。在 Web 的早期阶段,像这样的路径表示 Web 服务器上的物理文件位置。如今,它主要是由没有任何物理现实的 Web 服务器处理的抽象。?key1=value1&key2=value2
是提供给网络服务器的额外参数。这些参数是用&
符号分隔的键/值对列表。在返回资源之前,Web 服务器可以使用这些参数来执行额外的操作。每个 Web 服务器都有自己关于参数的规则,唯一可靠的方式来知道特定 Web 服务器是否处理参数是通过询问 Web 服务器所有者。#SomewhereInTheDocument
是资源本身的另一部分的锚点。锚点表示资源中的一种"书签",给浏览器显示位于该"加书签"位置的内容的方向。例如,在 HTML 文档上,浏览器将滚动到定义锚点的位置;在视频或音频文档上,浏览器将尝试转到锚代表的时间。
🚨注意 :#
后面的部分(也称为片段标识符)从来没有发送到请求的服务器 。上面内容摘自:MDN
在网络通信中,尤其是通过HTTP协议进行数据传输时,经常需要对URL中的某些字符进行编码和解码,以确保数据的正确性和安全性。urlencode
和urldecode
就是这样两种常用的技术。
二、URL编码和解码
1. Urlencode(URL编码)
urlencode
是一种编码机制,用于将非ASCII字符和一些特殊字符转换为可以在URL中安全传输的格式。这是因为URL只允许一小部分字符直接显示,而其他字符可能会引起歧义或被错误地解释。例如,空格' '
在URL中通常被视为参数分隔符,而不是字符串的一部分。因此,urlencode
会将空格转换为+
,或者将其转换为%20
这样的百分比编码形式。
在进行urlencode
时,以下字符会被保留,不需要编码:
- 字母(
a-z
,A-Z
) - 数字(
0-9
) - _ . ! ~ * ' ( )
所有其他字符都会被编码为%
后跟两位十六进制数的形式。
2. Urldecode(URL解码)
与urlencode
相对应,urldecode
是将编码后的URL转换回原始格式的过程。例如,%20
会被解码回空格' '
,+
会被解码回空格,其他编码的字符也会被转换回其原始表示。
-
应用场景
- 表单提交 :在HTML表单中,用户输入的数据需要通过URL或POST请求发送到服务器。
urlencode
用于确保这些数据在传输过程中不会被误解或损坏。 - URL参数 :URL中的查询字符串参数经常需要包含特殊字符,使用
urlencode
可以确保这些参数能够正确地被服务器解析。 - 数据传输 :在网络应用中,为了确保数据的完整性和准确性,发送方需要对数据进行
urlencode
,接收方在接收到数据后需要进行urldecode
以还原数据的原始形式。
- 表单提交 :在HTML表单中,用户输入的数据需要通过URL或POST请求发送到服务器。
-
示例
- 原始字符串:
Hello, World!
urlencode
后:Hello%2C%20World!
urldecode
后:Hello, World!
- 原始字符串:
通过urlencode
和urldecode
,我们可以确保在网络中传输的数据不会因为特殊字符而遭到破坏,从而保证了数据的完整性和安全性。这两种技术在日常网络应用中扮演着至关重要的角色。
三、HTTP的方法
方法 | 简介 | 支持HTTP协议版本 |
---|---|---|
GET | 请求从服务器获取指定资源的数据。GET请求应该是幂等的,多次执行相同的GET请求应得到相同的结果。 | HTTP/1.1 |
POST | 向服务器提交数据,请求服务器处理并可能创建新资源。POST请求可能会导致服务器上数据的改变。 | HTTP/1.1 |
PUT | 请求服务器存储一个资源,并用请求体中的内容替换目标资源的所有当前内容。 | HTTP/1.1 |
DELETE | 请求服务器删除指定的资源。 | HTTP/1.1 |
HEAD | 请求获取与GET请求相同的响应,但没有响应体。HEAD请求主要用于检查资源的元数据。 | HTTP/1.1 |
OPTIONS | 请求查询服务器的性能,如询问哪些HTTP方法该服务器支持。 | HTTP/1.1 |
PATCH | 请求对资源应用部分修改。PATCH请求主要用于更新资源的一部分,而不是整个资源。 | HTTP/1.1, HTTP/2 |
CONNECT | 用于代理服务器,建立到目标服务器的隧道。主要用于HTTPS代理。 | HTTP/1.1 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 | HTTP/1.1 |
四、HTTP的状态码
下表是一些常见的HTTP状态码
状态码 | 说明 | 适用场景 |
---|---|---|
100 | Continue | 服务端已接收到请求的初始部分,要求客户端继续发送剩余部分。 |
200 | OK | 请求成功。常用的状态码之一。 |
201 | Created | 请求成功并且服务器创建了新的资源。 |
202 | Accepted | 服务器已接受请求,但尚未处理完成。 |
203 | Non-Authoritative Information | 服务器已成功处理请求,但返回的信息可能来自另一来源。 |
204 | No Content | 服务器成功处理请求,但没有返回任何内容。 |
205 | Reset Content | 服务器成功处理请求,且要求客户端重置其文档视图。 |
206 | Partial Content | 服务器成功处理了部分请求。 |
300 | Multiple Choices | 指示请求的资源有一系列可供选择的回馈信息,用户或浏览器能够自行选择一个首选的地址进行重定向。 |
301 | Moved Permanently | 请求的资源已永久移动到新位置,以后应使用资源现在所指的URI。 |
302 | Found | 请求的资源现在临时从不同的URI响应请求。 |
303 | See Other | 与302类似,但要求始终使用头字段中给出的新的URI进行后续请求。 |
304 | Not Modified | 自从上次请求后,请求的资源未修改过。 |
305 | Use Proxy | 被请求的资源必须通过指定的代理才能被访问。 |
307 | Temporary Redirect | 请求的资源临时从不同的URI响应请求。 |
400 | Bad Request | 服务器无法理解请求的格式。 |
401 | Unauthorized | 请求要求身份验证。 |
402 | Payment Required | 保留有效,但尚未使用。 |
403 | Forbidden | 服务器拒绝请求。 |
404 | Not Found | 请求失败,服务器找不到请求的资源。 |
405 | Method Not Allowed | 禁用请求中指定的方法。 |
406 | Not Acceptable | 服务器无法提供请求的响应,因为客户端不接受服务器可以生成的响应格式。 |
407 | Proxy Authentication Required | 要求代理身份验证。 |
408 | Request Timeout | 服务器在等待客户端发送请求时超时。 |
409 | Conflict | 服务器在尝试处理请求时发生冲突。 |
410 | Gone | 请求的资源永久性地从服务器上移除了。 |
411 | Length Required | 服务器拒绝请求,因为请求没有包含有效的长度头部。 |
412 | Precondition Failed | 服务器未满足请求者在请求中设置的其中一个前提条件。 |
413 | Payload Too Large | 服务器无法处理请求,因为请求的主体过大。 |
414 | URI Too Long | 请求的URI过长,服务器无法处理。 |
415 | Unsupported Media Type | 服务器无法处理请求,因为请求的媒体类型不被支持。 |
416 | Range Not Satisfiable | 无法满足请求的Range头所指定的范围。 |
417 | Expectation Failed | 服务器无法满足请求者在请求中设置的期望值。 |
500 | Internal Server Error | 服务器遇到错误,无法完成请求。 |
501 | Not Implemented | 服务器不具备完成请求的功能。 |
502 | Bad Gateway | 服务器作为网关或代理,从上游服务器收到无效响应。 |
503 | Service Unavailable | 服务器目前无法使用(由于超载或停机维护)。 |
504 | Gateway Timeout | 服务器作为网关或代理,但是没有及时从上游服务器收到请求。 |
505 | HTTP Version Not Supported | 服务器不支持请求中所用的HTTP协议版本。 |
⭕ 这些状态码是HTTP协议中定义的一部分,用于告知客户端请求的处理结果。状态码分为五个类别:1xx(信息性状态码),2xx(成功状态码),3xx(重定向状态码),4xx(客户端错误状态码)和5xx(服务器错误状态码)。每个状态码都有其特定的含义和用途,帮助开发者和用户理解HTTP请求的结果,并采取相应的措施。
五、HTTP常见Header
头部名称 | 描述 | 请求/响应 | 常见值示例 |
---|---|---|---|
Host | 指定请求的服务器域名和端口号 | 请求 | example.com:80 |
User-Agent | 客户端的用户代理信息 | 请求 | Mozilla/5.0 (Windows NT 10.0; Win64; x64) |
Accept | 客户端可接受的媒体类型 | 请求 | text/html,application/xhtml+xml,application/xml |
Accept-Language | 客户端偏好的语言 | 请求 | en-US,en;q=0.5 |
Accept-Encoding | 客户端可接受的压缩格式 | 请求 | gzip, deflate |
Connection | 指定连接的选项 | 请求 | keep-alive 或 close |
Content-Type | 发送数据的媒体类型 | 请求/响应 | application/x-www-form-urlencoded |
Content-Length | 请求体的长度 | 请求/响应 | 123 |
Authorization | 认证信息 | 请求 | Basic YWRtaW46YWRtaW4= (基本认证示例) |
Cache-Control | 请求和响应的缓存指令 | 请求/响应 | no-cache, no-store, must-revalidate |
Cookie | 客户端发送的cookie信息 | 请求 | sessionid=abc123; username=johndoe |
Server | 服务器软件信息 | 响应 | Apache/2.4.7 (Unix) |
Set-Cookie | 服务器设置新的cookie | 响应 | sessionid=abc123; expires=Sat, 10 Apr 2021 08:00:00 GMT |
WWW-Authenticate | 认证挑战信息 | 响应 | Basic realm="Restricted Area" |
Date | 响应生成的日期和时间 | 响应 | Tue, 07 Apr 2025 16:00:00 GMT |
Last-Modified | 资源最后修改的时间 | 响应 | Tue, 07 Apr 2025 15:00:00 GMT |
ETag | 资源的特定版本标识符 | 响应 | "xyz123" |
请注意,上表中的"常见值示例"列仅提供了可能的值,并不是完整的头部字段值。实际的头部字段值可能会根据具体的应用场景和需求而有所不同。
六、最简单的HTTP服务器
cpp
#include <sys/socket.h> // 引入套接字相关的头文件
#include <netinet/in.h> // 引入处理IPv4地址的头文件
#include <arpa/inet.h> // 引入INET相关函数的头文件
#include <unistd.h> // 引入UNIX标准函数,如close()
#include <stdio.h> // 引入标准输入输出头文件
#include <string.h> // 引入字符串处理函数的头文件
#include <stdlib.h> // 引入标准库函数,如atoi()
// 打印服务器的使用方法
void Usage() {
printf("usage: ./server [ip] [port]\n");
}
int main(int argc, char* argv[]) {
// 确保命令行参数数量正确(应为3个:程序名、IP地址和端口号)
if (argc != 3) {
Usage();
return 1;
}
// 创建一个基于IPv4的TCP套接字
int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
perror("socket"); // 如果创建失败,打印错误信息
return 1;
}
struct sockaddr_in addr; // 定义一个地址结构体
addr.sin_family = AF_INET; // 设置为IPv4地址族
addr.sin_addr.s_addr = inet_addr(argv[1]); // 设置IP地址
addr.sin_port = htons(atoi(argv[2])); // 设置端口号,并转换为网络字节序
// 将套接字绑定到指定的IP地址和端口
int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
if (ret < 0) {
perror("bind"); // 如果绑定失败,打印错误信息
return 1;
}
// 开始监听传入的连接,允许最多10个连接同时等待
ret = listen(fd, 10);
if (ret < 0) {
perror("listen"); // 如果监听失败,打印错误信息
return 1;
}
// 无限循环,持续接受客户端的连接
for (;;) {
struct sockaddr_in client_addr; // 定义客户端地址结构体
socklen_t len = sizeof(client_addr); // 定义长度变量
// 接受一个客户端连接,并将客户端的地址信息存储在client_addr中
int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);
if (client_fd < 0) {
perror("accept"); // 如果接受连接失败,打印错误信息
continue; // 继续下一次循环
}
// 定义一个缓冲区,用于存储从客户端读取的数据
char input_buf[1024 * 10] = {0};
// 从客户端读取数据,最多读取缓冲区大小-1字节
ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);
if (read_size < 0) {
perror("read"); // 如果读取失败,打印错误信息
close(client_fd); // 关闭客户端套接字
continue; // 继续下一次循环
}
// 打印接收到的请求
printf("[Request] %s\n", input_buf);
// 定义一个缓冲区,用于存储响应数据
char buf[1024] = {0};
// 定义要发送的HTML内容
const char* hello = "<h1>hello world</h1>";
// 格式化HTTP响应消息,包括HTTP头部和HTML内容
sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello), hello);
// 将响应消息发送回客户端
write(client_fd, buf, strlen(buf));
// 关闭客户端套接字
close(client_fd);
}
// 关闭服务器套接字
close(fd);
return 0; // 正常退出
}
这段代码是一个简单的HTTP服务器实现,它监听指定的IP地址和端口上的TCP连接,并响应每个连接以"Hello, World!"页面,服务器使用标准的套接字API来处理网络通信。
温馨提示
感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!