一、HTTP协议的工作流程及实例
1、HTTP协议工作流程
HTTP协议的基本工作流程。通过这种请求-响应模型,客户端和服务器之间可以进行有效的数据传输和通信。HTTP协议在互联网上广泛应用于Web浏览器和服务器之间的数据交互。
1)客户端发起请求
客户端(例如Web浏览器)向服务器发送HTTP请求。请求由请求行、请求头部和请求体组成。请求行包含请求方法(GET、POST等)、请求的URL和HTTP协议版本。
2)服务器接收请求
服务器接收到客户端发送的HTTP请求,并解析请求行和请求头部,获取请求的目标资源。
3)服务器处理请求
服务器根据请求的方法和URL,执行相应的处理逻辑。这可能涉及到查询数据库、处理业务逻辑等操作。
4)服务器生成响应
服务器生成一个HTTP响应,包含一个状态行、响应头部和响应体。状态行包含响应的状态码(例如200表示成功,404表示未找到资源),响应头部包含附加信息(例如内容类型、内容长度),响应体包含实际的响应数据。
5)服务器发送响应
服务器将生成的HTTP响应发送回客户端。响应经过网络传输到客户端。
6)客户端接收响应
客户端接收到服务器发送的HTTP响应。
7)客户端处理响应
客户端根据响应的状态码和响应头部进行相应的处理。例如,如果状态码为200,表示成功,客户端可以解析响应体中的数据并进行展示;如果状态码为404,表示未找到资源,客户端可以显示错误页面或者执行其他操作。
8)客户端关闭连接
客户端关闭与服务器的连接,完成一次HTTP请求-响应的交互。
HTTP协议的工作流程示意图:
+-------------+ +-------------+ | | | | | Client | | Server | | | | | +-------------+ +-------------+ | | | Step 1: 发起请求 | |-------------------------------------->| | | | Step 2: 接收请求 | |<--------------------------------------| | | | Step 3: 处理请求 | | | | | | Step 4: 生成响应 | | | | | | Step 5: 发送响应 | |-------------------------------------->| | | | Step 6: 接收响应 | |<--------------------------------------| | | | Step 7: 处理响应 | | | | | | Step 8: 关闭连接 | |-------------------------------------->| | |
注意:我们在浏览器输入网址,执行HTTP请求,浏览器就是客户端,服务端是相应网站的服务器。
2、HTTP协议简单工作流程实例
我们通过浏览器输入http://www.example.com/index.html
网址进行访问,一般简单的请求响应流程如下:
1)客户端(浏览器)发送一个HTTP请求给服务器
请求的格式如下:
GET /index.html HTTP/1.1 Host: www.example.com
2)服务器接收到请求后,解析请求行和请求头,并根据请求的路径和参数执行相应的处理逻辑
3)服务器生成一个HTTP响应,响应的格式如下:
HTTP/1.1 200 OK Content-Type: text/html Content-Length: 1234 <html> <body> <h1>Hello, World!</h1> </body> </html>
4)服务器将生成的响应发送回客户端。
5)客户端接收到响应后,解析响应头和响应体,并根据需要进行相应的处理,如渲染页面或执行其他操作。
二、HTTP 协议格式
HTTP协议的格式可以分为请求格式和响应格式两部分。HTTP 协议并没有规定报头部分的键值对有多少个,HTTP 报文中空行当于是报文的结束标记或报文和正文之间的分隔符。HTTP 在传输层依赖 TCP 协议,TCP 是面向字节流的。如果没有这个空行,就会出现”粘包问题“。
1、请求格式(Request Format)
请求格式由请求行(Request Line)、请求头(Headers)和请求体(Body)组成。
请求行的格式为:
METHOD PATH/URL HTTP/版本号
1)方法(method):
方法 | 说明 | 适用版本号 |
GET | 获取资源,GET 是最常用的 HTTP 方法, 常用于获取服务器上的某个资源。 GET 请求的特点: 首行里面的第一个部分就是 GET URL 里面的 query string 可以为空, 也可以不为空 %E8%9B%8B%E7%B3%95 GET 请求的 header 有若干个键值对结构 GET 请求的 body 一般是空的 | HTTP 1.0、HTTP 1.1 |
POST | 传输实体主体,POST 方法也是一种常见的方法, 多用于提交用户输入的数据给服务器(如登录页面)。 POST 请求的特点: 首行第一个部分就是 POST URL 里面的 query string 一般是空的 POST 请求的 header 里面有若干个键值对 POST 请求的 body 一般不为空(body 的具体数据格式, 由 header 中的 Content-Type 来描述; body 的具体数据长度, 由 header 中的 Content-Length 来描述 | HTTP 1.0、HTTP 1.1 |
PUT | 传输文件 | HTTP 1.0、HTTP 1.1 |
HEAD | 获得报文首部 | HTTP 1.0、HTTP 1.1 |
DELETE | 删除文件 | HTTP 1.0、HTTP 1.1 |
OPTIONS | 访问支持的方法 | HTTP 1.1 |
TRACE | 追踪路径 | HTTP 1.1 |
CONNECT | 要求用隧道协议连接代理 | HTTP 1.1 |
LINK | 建立和资源之间的联系 | HTTP 1.1 |
UNLINE | 断开连接关系 | HTTP 1.1 |
请求头包含了关于请求的附加信息,每个请求头由键值对组成,格式为:
Header-Name: Header-Value
请求体是可选的,用于传输请求的数据。
2)GET 和 POST 请求的区别
GET请求参数通过URL的查询字符串(Query String)进行传递,参数会附加在URL的末尾,以?开头,多个参数之间使用&符号连接。例如:http://www.example.com/path?param1=value1&m2=value2
,POST请求参数通过请求体(Request Body)进行传递,参数会包含在请求体中,并且不会显示在URL中。GET请求由于参数附加在URL中,受到URL长度的限制,浏览器和服务器对URL长度都有一定的限制,一般在2048个字符左右。POST请求请求体中的数据没有固定长度限制,可以传输较大的数据。
GET请求用于获取资源,请求的副作用较小,多用于查询数据。GET请求是幂等的,多次发送相同的GET请求会得到相同的结果,不会对服务器产生影响。POST请求用于提交数据,请求的副作用较大,多用于新增、修改或删除数据等操作。POST请求不是幂等的,多次发送相同的POST请求会产生不同的结果,可能对服务器产生影响。GET请求参数暴露在URL中,相对不太安全,不适合传递敏感信息。POST请求参数在请求体中,相对安全,适合传递敏感信息。
3)HTTP协议头(Header)
HTTP协议头(Header)是在HTTP请求和响应中用于传递附加信息的部分。它由一个或多个字段构成,每个字段由字段名和字段值组成,中间用冒号(:)分隔。HTTP协议头包含在请求和响应的起始行和实体主体之间。
字段名称 | 描述 |
Accept | 指定客户端能够处理的媒体类型, 用于告知服务器可以接受哪些内容类型。 |
Content-Type | 指定请求或响应的媒体类型, 用于告知对方发送的实体主体的类型。 |
Content-Length | 指定请求或响应的实体主体的长度,以字节为单位。 |
User-Agent | 指定发送请求的用户代理(浏览器、应用程序等)的标识信息。 |
Host | 指定请求的目标服务器的主机名和端口号。 |
Cookie | 用于在客户端和服务器之间传递会话状态信息的数据。 |
Authorization | 指定请求的身份验证凭证,用于在请求中包含用户凭据。 |
Cache-Control | 指定请求或响应的缓存机制和行为。 |
Location | 指定重定向的目标URL, 用于在响应中告知客户端进行重定向。 |
Referer | 指定当前请求的来源URL, 用于告知服务器当前请求的上一个页面的URL |
Content-Type 有以下三种请求中的数据格式:
application/x-www-form-urlencoded
是 form 表单提交的数据格式,此时 body 的格式就类似于 query string(是键值对的结构,键值对之间使用 & 分割,键与值之间使用 = 分割multipart/form-data
是 form 表单提交的数据格式(需要在 from 标签上加上 enctyped="multipart/form-data"),通常用于 HTML 提交图片或者文件application/json
表示 body 数据为 json 格式,json 格式就是源自 js 的对象的格式。用一个 { } 括住,里面有多个键值对,键值对之间使用逗号分割,键和值之间使用冒号分割
2、响应格式(Response Format)
响应格式由响应行(Response Line)、响应头(Headers)和响应体(Body)组成。
响应行的格式为:
HTTP/版本号 状态码 状态消息
响应头与请求头类似,包含了关于响应的附加信息。
响应体是可选的,用于传输响应的数据。
1)状态码
常见状态码 | 说明 |
200 OK | 这是一个最常见的状态码, 表示访问成功。抓包抓到的大部分结果都是 200 |
404 Not Found | 没有找到资源。URL 标识的资源不存在, 那么就会出现 404 |
403 Forbidden | 表示访问被拒绝。有的页面通常需要用户具有一定的权限才能访问(登陆后才能访问).。如果用户没有登陆直接访问, 就容易见到 403 |
405 Method Not Allowed | 我们学习了 HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等。但是对方的服务器不一定都支持所有的方法(或者不允许用户使用一些其他的方法). |
500 Internal Server Error | 服务器出现内部错误. 一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个状态码,一般很少见 |
504 Gateway Timeout | 当服务器负载比较大的时候, 服务器处理单条请求的时候消耗的时间就会很长, 就可能会导致出现超时的情况 |
302 Move temporarily | 临时重定向。在登陆页面中经常会见到 302. 用于实现登陆成功后自动跳转到主页 |
301 Moved Permanently | 永久重定向。当浏览器收到这种响应时, 后续的请求都会被自动改成新的地址。301 也是通过 Location 字段来表示要重定向到的新地址 |
2)协议头(header)
响应报头的基本格式和请求报头的格式基本相同,响应报头的 Content-Type 参数:
text/html
表示数据格式是 HTMLtext/css
表示数据格式是 CSSapplication/javascript
表示数据各式是 JavaScriptapplication/json
表示数据格式是 JSON
3、HTTP请求和响应的示例
1)请求示例
GET /index.html HTTP/1.1 Host: www.example.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.82 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
2)响应示例
HTTP/1.1 200 OK Content-Type: text/html Content-Length: 1234 Server: Apache/2.4.29 (Unix) Date: Tue, 20 Apr 2021 12:00:00 GMT <!DOCTYPE html> <html> <head> <title>Welcome to Example.com</title> </head> <body> <h1>Hello, World!</h1> </body> </html>
三、编程实现HTTP请求响应
通过Socket在C#中实现HTTP请求和响应,需要手动构建HTTP报文并发送到服务器,然后接收服务器的响应并解析。
1)HTTP客户端
using System; using System.Net.Sockets; using System.Text; public class Program { public static void Main() { // 设置请求信息 string host = "localhost"; int port = 80; string path = "/"; // 构建HTTP请求报文 string request = $"GET {path} HTTP/1.1\r\nHost: {host}\r\nConnection: close\r\n\r\n"; // 创建Socket连接 using (Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) { // 连接服务器 socket.Connect(host, port); // 发送请求 byte[] requestData = Encoding.ASCII.GetBytes(request); socket.Send(requestData); // 接收响应 byte[] buffer = new byte[1024]; int bytesRead = socket.Receive(buffer); // 解析响应并输出 string response = Encoding.ASCII.GetString(buffer, 0, bytesRead); Console.WriteLine(response); } } }
2)HTTP服务端
using System; using System.Net; using System.Net.Sockets; using System.Text; public class Program { public static void Main() { // 设置监听地址和端口 string ipAddress = "127.0.0.1"; int port = 8080; // 创建监听器 TcpListener listener = new TcpListener(IPAddress.Parse(ipAddress), port); listener.Start(); Console.WriteLine($"HTTP server started on {ipAddress}:{port}"); while (true) { // 接受客户端连接 TcpClient client = listener.AcceptTcpClient(); // 处理客户端请求 HandleClientRequest(client); // 关闭客户端连接 client.Close(); } } private static void HandleClientRequest(TcpClient client) { // 读取客户端请求 byte[] buffer = new byte[1024]; int bytesRead = client.GetStream().Read(buffer, 0, buffer.Length); // 解析HTTP请求报文 string request = Encoding.ASCII.GetString(buffer, 0, bytesRead); Console.WriteLine($"Received request:\n{request}"); // 构建HTTP响应报文 string response = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nHello, World!"; byte[] responseData = Encoding.ASCII.GetBytes(response); // 发送HTTP响应 client.GetStream().Write(responseData, 0, responseData.Length); Console.WriteLine($"Sent response:\n{response}"); } }