杰瑞科技汇

Java Socket如何模拟HTTP请求?

下面我将分步进行讲解,从最基础的原理开始,到具体的代码实现,最后给出一个更完整的工具类。

Java Socket如何模拟HTTP请求?-图1
(图片来源网络,侵删)

核心原理

HTTP 协议是构建在 TCP/IP 协议之上的应用层协议,当我们使用浏览器访问一个网站时,浏览器实际上就是扮演了一个 HTTP 客户端的角色,它通过 Socket 与服务器的 80 (HTTP) 或 443 (HTTPS) 端口建立 TCP 连接,然后按照 HTTP 协议的格式发送请求,并接收服务器的响应。

模拟 HTTP 请求,就是手动完成浏览器做的事情:

  1. 创建 Socket 连接:指定目标主机(域名或IP)和端口。
  2. 获取输出流:通过 Socket 获取 OutputStream,用于向服务器发送 HTTP 请求报文。
  3. 构建 HTTP 请求报文:按照 HTTP 协议的格式(请求行、请求头、空行、请求体)手动拼接字符串,并将其转换为字节写入输出流。
  4. 获取输入流:通过 Socket 获取 InputStream,用于读取服务器返回的 HTTP 响应报文。
  5. 解析 HTTP 响应报文:从输入流中读取数据,并按照 HTTP 协议的格式解析响应状态码、响应头和响应体。
  6. 关闭资源:关闭 Socket 和相关的输入输出流。

HTTP 请求/响应报文格式

在写代码前,我们必须清楚 HTTP 报文长什么样。

HTTP 请求报文示例 (GET 请求)

GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: MyCustomJavaClient/1.0
Accept: */*
(空行)
  • 请求行: GET /index.html HTTP/1.1
    • GET: 请求方法 (还有 POST, PUT, DELETE 等)
    • /index.html: 请求的 URI (统一资源标识符)
    • HTTP/1.1: HTTP 协议版本
  • 请求头: Host: ..., User-Agent: ... 等,每个请求头占一行,以 Key: Value 的形式出现。
  • 空行: 一个单独的回车换行符 \r\n,这是请求头和请求体之间的分隔符,对于 GET 请求,请求体为空,所以空行后直接结束。

HTTP 请求报文示例 (POST 请求)

POST /api/login HTTP/1.1
Host: www.example.com
Content-Type: application/json
Content-Length: 38
{"username":"admin","password":"123456"}
  • 请求体: 空行之后的内容就是请求体,对于 POST 请求,这里通常包含要提交给服务器的数据。Content-Length 头部非常重要,它告诉服务器请求体有多少个字节。

HTTP 响应报文示例

HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1276
Connection: keep-alive
Date: Wed, 27 Oct 2025 10:00:00 GMT
<!DOCTYPE html>
<html>
<head>Welcome to Example.com</title>
</head>
<body>
    <h1>Hello, World!</h1>
    ...
</body>
</html>
  • 状态行: HTTP/1.1 200 OK
    • HTTP/1.1: 协议版本
    • 200: 状态码 (表示成功)
    • OK: 状态码描述
  • 响应头: Content-Type: ..., Content-Length: ... 等。
  • 空行: 分隔响应头和响应体。
  • 响应体: 空行之后的内容,即服务器返回的实际数据(HTML, JSON, 图片等)。

Java 代码实现

下面我们来实现一个简单的 HTTP 客户端,它可以发送 GET 和 POST 请求。

Java Socket如何模拟HTTP请求?-图2
(图片来源网络,侵删)

第1步:发送一个简单的 GET 请求

这个例子会向 httpbin.org/get 发送一个 GET 请求,该网站会返回我们请求的详细信息,非常适合测试。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class SimpleHttpClientGet {
    public static void main(String[] args) {
        String host = "httpbin.org";
        int port = 80;
        String path = "/get";
        try (Socket socket = new Socket(host, port);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            // 1. 构建HTTP请求报文
            StringBuilder request = new StringBuilder();
            request.append("GET " + path + " HTTP/1.1\r\n");
            request.append("Host: " + host + "\r\n");
            request.append("Accept: */*\r\n");
            request.append("Connection: close\r\n"); // 请求结束后关闭连接
            request.append("\r\n"); // 空行,代表请求头结束
            // 2. 发送请求
            out.print(request.toString());
            out.flush(); // 确保数据被立即发送
            System.out.println("Request sent:\n" + request.toString());
            // 3. 读取并打印响应
            String line;
            System.out.println("\n--- Response from Server ---");
            // 先读取响应头,直到遇到空行
            while ((line = in.readLine()) != null && !line.isEmpty()) {
                System.out.println(line);
            }
            // 空行之后是响应体
            System.out.println("\n--- Response Body ---");
            StringBuilder responseBody = new StringBuilder();
            while ((line = in.readLine()) != null) {
                System.out.println(line);
                responseBody.append(line);
            }
            // 在实际应用中,你可以在这里解析JSON响应体
            // System.out.println("Response Body JSON: " + responseBody.toString());
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host " + host);
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to " +
                    host);
            e.printStackTrace();
        }
    }
}

第2步:发送一个 POST 请求

这个例子会向 httpbin.org/post 发送一个 POST 请求,并附带 JSON 格式的请求体。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class SimpleHttpClientPost {
    public static void main(String[] args) {
        String host = "httpbin.org";
        int port = 80;
        String path = "/post";
        String jsonBody = "{\"username\":\"testuser\",\"password\":\"password123\"}";
        try (Socket socket = new Socket(host, port);
             PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
             BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) {
            // 1. 构建HTTP请求报文
            StringBuilder request = new StringBuilder();
            request.append("POST " + path + " HTTP/1.1\r\n");
            request.append("Host: " + host + "\r\n");
            request.append("Content-Type: application/json\r\n"); // 告诉服务器我们发送的是JSON
            request.append("Content-Length: " + jsonBody.length() + "\r\n"); // 告诉服务器请求体长度
            request.append("Accept: */*\r\n");
            request.append("Connection: close\r\n");
            request.append("\r\n"); // 空行
            request.append(jsonBody); // 请求体
            // 2. 发送请求
            out.print(request.toString());
            out.flush();
            System.out.println("Request sent:\n" + request.toString());
            // 3. 读取并打印响应
            System.out.println("\n--- Response from Server ---");
            String line;
            // 读取响应头
            while ((line = in.readLine()) != null && !line.isEmpty()) {
                System.out.println(line);
            }
            // 读取响应体
            System.out.println("\n--- Response Body ---");
            while ((line = in.readLine()) != null) {
                System.out.println(line);
            }
        } catch (UnknownHostException e) {
            System.err.println("Don't know about host " + host);
            e.printStackTrace();
        } catch (IOException e) {
            System.err.println("Couldn't get I/O for the connection to " + host);
            e.printStackTrace();
        }
    }
}

封装成一个可重用的工具类

上面的例子虽然能工作,但每次使用都很冗长,我们可以将其封装成一个简单的 HttpClient 工具类,方便调用。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException
Java Socket如何模拟HTTP请求?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇