从 socket 实现一个简化版 wget
结论速览
- 最小 wget 的流程是解析 URL、DNS、建立 TCP、发送 HTTP 请求、接收响应、去掉 Header、保存 Body。
- DNS 通常不是简单的内核 syscall,由 libc resolver 配合网络 socket 完成,接口是
getaddrinfo()。 socket(AF_INET, SOCK_STREAM, 0)里AF_INET是 IPv4、SOCK_STREAM是 TCP,内核返回文件描述符。connect()背后的 ARP、SYN/ACK、重传、IP、Ethernet 全部由内核处理,应用不用自己实现 TCP。- HTTP Header 与 Body 用
\r\n\r\n分隔;HTTPS 多一层 TLS,涉及证书、密钥交换、握手,学习阶段先用 HTTP 地址。
1. HTTP 下载本质
最简单的 wget 可以分成:
1 | 解析 URL |
例如:
1 | http://example.com/test.bin |
可解析成:
1 | Host = example.com |
2. DNS
可使用:
1 | getaddrinfo() |
例如:
1 | struct addrinfo *res; |
它负责把域名解析成网络地址。
DNS 并不是一个简单的 Kernel syscall,通常由 libc resolver 配合网络 socket 完成。
3. 创建 TCP socket
1 | int fd = socket(AF_INET, SOCK_STREAM, 0); |
其中:
1 | AF_INET |
Kernel 会返回一个文件描述符,例如:
1 | 3 |
4. 建立 TCP 连接
1 | connect(fd, ...); |
背后由 Kernel 处理:
1 | ARP |
应用不需要自己实现 TCP。
5. 发送 HTTP 请求
例如:
1 | const char *req = |
6. 接收响应
1 | while ((n = recv(fd, buf, sizeof(buf), 0)) > 0) { |
服务器返回:
1 | HTTP/1.1 200 OK |
HTTP Header 和 Body 通常用:
1 | \r\n\r\n |
分隔。
7. 保存文件
创建文件:
1 | int out = open( |
然后:
1 | write(out, data, len); |
最终调用链:
1 | recv |
8. 最小 wget 所需接口
核心 API:
1 | getaddrinfo |
因此通过实现一个简化 wget,可以一次学习:
1 | DNS |
9. 为什么先不做 HTTPS
HTTPS 比 HTTP 多一层 TLS:
1 | HTTP |
TLS 涉及:
1 | 证书 |
实际项目一般使用:
1 | OpenSSL |
学习阶段先做:
1 | http://192.168.x.x/file.bin |
更容易理解底层机制。
