HTTP请求在网络中传输的完整路径
目录
HTTP请求在网络中传输的完整路径
完整流程
客户端点击
↓
构造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();
}
}
浙公网安备 33010602011771号