HTTP请求在网络中传输的完整路径

HTTP请求在网络中传输的完整路径

img

完整流程

客户端点击
    ↓
构造HTTP请求(GET /index.html HTTP/1.1...)
    ↓   # 发送给服务端:socket.writeAndFlush()
用户空间 → 内核空间(send()系统调用)
    ↓
传输层:添加TCP头(源端口、目标端口80、序列号等)
    ↓  
网络层:添加IP头(源IP、目标IP、TTL等)
    ↓
数据链路层:添加以太网帧头(源MAC、目标MAC)
    ↓
物理层:网卡将数字信号转为电信号/光信号
    ↓
--- 通过网络传输 ---
    ↓
服务器网卡收到电信号/光信号
    ↓
物理层:网卡将信号转为数字数据
    ↓
数据链路层:检查MAC地址,剥去帧头
    ↓
网络层:检查IP地址,判断是本地交付
    ↓
传输层:TCP处理,根据端口号找到对应sock结构
    ↓
将数据放入sock接收队列,唤醒等待进程
    ↓   # 接收:socket.getInputStream()
服务器应用程序从Socket读取数据
    ↓
解析HTTP请求,处理业务逻辑
    ↓
返回HTTP响应(逆向重复上述过程)
    ↓  # 发送给客户端:socket.writeAndFlush()
    ...

完整流程详解

1. 客户端点击 → 发送HTTP包

// 浏览器/客户端应用代码层面
public class Browser {
    public void onClick() {
        // 构造HTTP请求
        String httpRequest = "GET /index.html HTTP/1.1\r\n" +
                           "Host: www.example.com\r\n" +
                           "User-Agent: Mozilla/5.0\r\n" +
                           "\r\n";
        
        // 通过Socket发送
        Socket socket = new Socket("www.example.com", 80);
        OutputStream out = socket.getOutputStream();
        out.write(httpRequest.getBytes());
        out.flush();
    }
}

2. Socket连接发送给服务器

操作系统内核处理:

// Linux内核大致流程(简化)
int socket_send(struct socket *sock, const char *buf, size_t len) {
    // 传输层:添加TCP头
    struct tcphdr *tcp_header = build_tcp_header(sock->src_port, sock->dst_port);
    
    // 网络层:添加IP头  
    struct iphdr *ip_header = build_ip_header(sock->src_ip, sock->dst_ip);
    
    // 数据链路层:添加以太网头
    struct ethhdr *eth_header = build_eth_header(sock->src_mac, sock->dst_mac);
    
    // 通过网卡驱动发送
    netdevice_transmit(complete_packet);
}

3. 服务器网卡收到

网卡驱动处理:

// 网卡中断处理函数
irqreturn_t nic_interrupt_handler(int irq, void *dev_id) {
    // 1. DMA将数据包从网卡内存拷贝到内核内存
    struct sk_buff *skb = alloc_skb(packet_size);
    nic_dma_to_ram(skb->data, packet_size);
    
    // 2. 触发软中断,交给网络协议栈处理
    raise_softirq(NET_RX_SOFTIRQ);
}

4. 系统底层sock等处理

Linux网络协议栈处理:

// 网络协议栈处理流程
int netif_receive_skb(struct sk_buff *skb) {
    // 1. 数据链路层:检查MAC地址,剥去以太网头
    if (!is_my_mac_address(skb->eth_hdr->dest)) return DROP;
    skb_pull(skb, sizeof(struct ethhdr));
    
    // 2. 网络层:检查IP头,路由判断
    if (skb->ip_hdr->daddr != my_ip) {
        ip_forward(skb);  // 需要转发
    } else {
        ip_local_deliver(skb);  // 本地处理
    }
    
    // 3. 传输层:TCP/UDP处理
    if (skb->ip_hdr->protocol == IPPROTO_TCP) {
        tcp_v4_rcv(skb);  // 交给TCP处理
    }
}

// TCP层处理
int tcp_v4_rcv(struct sk_buff *skb) {
    // 根据IP+端口找到对应的sock结构
    struct sock *sk = __inet_lookup_skb(&tcp_hashinfo, skb);
    
    if (sk != NULL) {
        // 将数据包放入sock的接收队列
        __skb_queue_tail(&sk->sk_receive_queue, skb);
        
        // 唤醒等待该sock的进程
        wake_up_interruptible(sk->sk_sleep);
    }
}

5. 到达服务器的Socket连接

服务器应用程序处理:

// 服务器端Socket代码
public class WebServer {
    public void start() throws IOException {
        ServerSocket serverSocket = new ServerSocket(80);
        
        while (true) {
            // 等待客户端连接(内核唤醒进程)
            Socket clientSocket = serverSocket.accept();
            
            // 创建线程处理请求
            new Thread(() -> handleRequest(clientSocket)).start();
        }
    }
    
    private void handleRequest(Socket clientSocket) throws IOException {
        // 6. 获取数据内容
        InputStream in = clientSocket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        
        // 读取HTTP请求
        String requestLine = reader.readLine();
        System.out.println("收到请求: " + requestLine);
        
        // 读取请求头
        String header;
        while (!(header = reader.readLine()).isEmpty()) {
            System.out.println("请求头: " + header);
        }
        
        // 处理请求并返回响应
        OutputStream out = clientSocket.getOutputStream();
        String response = "HTTP/1.1 200 OK\r\n\r\nHello World";
        out.write(response.getBytes());
        out.flush();
        
        clientSocket.close();
    }
}
posted @ 2025-11-13 10:31  deyang  阅读(18)  评论(0)    收藏  举报