Windows网络编程
6、HTTP协议
1、课程目标
- 深入理解HTTP协议结构
- 掌握HTTP请求和响应格式
- 学会使用原始套接字实现HTTP
- 理解HTTP在安全工具中的应用
2、名词解释
| 术语 | 解释 |
|---|---|
| HTTP | HyperText Transfer Protocol |
| Request Line | 请求行(方法 URI 版本) |
| Status Line | 状态行(版本 状态码 原因) |
| Header | 头部字段 |
| Body | 消息体 |
3、技术原理
3.1、HTTP请求格式
┌─────────────────────────────────────────────────────────────┐
│ HTTP请求结构 │
│ │
│ GET /path/resource HTTP/1.1\r\n ← 请求行 │
│ Host: www.example.com\r\n ← 头部字段 │
│ User-Agent: Mozilla/5.0\r\n │
│ Accept: text/html\r\n │
│ Connection: keep-alive\r\n │
│ \r\n ← 空行 │
│ [Request Body] ← 请求体(POST) │
│ │
│ HTTP方法:GET POST PUT DELETE HEAD OPTIONS PATCH │
└─────────────────────────────────────────────────────────────┘
4、代码实现
4.1、示例1:手动构造HTTP请求
// RawHttp.c - 原始HTTP实现
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#pragma comment(lib, "ws2_32.lib")
// HTTP GET请求
BOOL HttpGetRaw(const char* host, int port, const char* path, char** response, int* respLen) {
SOCKET sock;
struct sockaddr_in serverAddr;
char request[2048], buffer[4096];
int totalRecv = 0, recvLen;
int capacity = 8192;
*response = (char*)malloc(capacity);
if (!*response) return FALSE;
// 创建连接
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct hostent* hostInfo = gethostbyname(host);
if (!hostInfo) return FALSE;
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
memcpy(&serverAddr.sin_addr, hostInfo->h_addr, hostInfo->h_length);
if (connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) != 0) {
closesocket(sock);
return FALSE;
}
// 构造HTTP请求
sprintf_s(request, sizeof(request),
"GET %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: CustomClient/1.0\r\n"
"Accept: */*\r\n"
"Connection: close\r\n"
"\r\n",
path, host);
// 发送请求
send(sock, request, strlen(request), 0);
// 接收响应
while ((recvLen = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
if (totalRecv + recvLen >= capacity) {
capacity *= 2;
*response = (char*)realloc(*response, capacity);
}
memcpy(*response + totalRecv, buffer, recvLen);
totalRecv += recvLen;
}
(*response)[totalRecv] = '\0';
*respLen = totalRecv;
closesocket(sock);
return TRUE;
}
// HTTP POST请求
BOOL HttpPostRaw(const char* host, int port, const char* path,
const char* contentType, const char* body,
char** response, int* respLen) {
SOCKET sock;
struct sockaddr_in serverAddr;
char request[4096];
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
struct hostent* hostInfo = gethostbyname(host);
serverAddr.sin_family = AF_INET;
serverAddr.sin_port = htons(port);
memcpy(&serverAddr.sin_addr, hostInfo->h_addr, hostInfo->h_length);
connect(sock, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
// 构造POST请求
sprintf_s(request, sizeof(request),
"POST %s HTTP/1.1\r\n"
"Host: %s\r\n"
"User-Agent: CustomClient/1.0\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"\r\n"
"%s",
path, host, contentType, strlen(body), body);
send(sock, request, strlen(request), 0);
// 接收响应
int capacity = 8192, totalRecv = 0, recvLen;
char buffer[4096];
*response = (char*)malloc(capacity);
while ((recvLen = recv(sock, buffer, sizeof(buffer), 0)) > 0) {
if (totalRecv + recvLen >= capacity) {
capacity *= 2;
*response = (char*)realloc(*response, capacity);
}
memcpy(*response + totalRecv, buffer, recvLen);
totalRecv += recvLen;
}
(*response)[totalRecv] = '\0';
*respLen = totalRecv;
closesocket(sock);
return TRUE;
}
// 解析HTTP响应
void ParseHttpResponse(const char* response, int* statusCode, char** headers, char** body) {
// 找到状态码
const char* p = strstr(response, "HTTP/1.");
if (p) {
p = strchr(p, ' ');
if (p) *statusCode = atoi(p + 1);
}
// 找到头部结束位置
const char* headerEnd = strstr(response, "\r\n\r\n");
if (headerEnd) {
int headerLen = headerEnd - response;
*headers = (char*)malloc(headerLen + 1);
strncpy_s(*headers, headerLen + 1, response, headerLen);
*body = _strdup(headerEnd + 4);
}
}
int main() {
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
char* response;
int respLen;
if (HttpGetRaw("httpbin.org", 80, "/get", &response, &respLen)) {
printf("Response (%d bytes):\n%s\n", respLen, response);
free(response);
}
WSACleanup();
return 0;
}
4.2、示例2:HTTP头部解析
// HttpParser.c - HTTP解析器
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct _HTTP_HEADER {
char* name;
char* value;
} HTTP_HEADER;
typedef struct _HTTP_REQUEST {
char method[16];
char path[1024];
char version[16];
HTTP_HEADER headers[50];
int headerCount;
char* body;
int bodyLength;
} HTTP_REQUEST;
// 解析HTTP请求
BOOL ParseHttpRequest(const char* data, HTTP_REQUEST* request) {
char* line, *saveptr;
char* dataCopy = _strdup(data);
memset(request, 0, sizeof(HTTP_REQUEST));
// 解析请求行
line = strtok_s(dataCopy, "\r\n", &saveptr);
if (!line) return FALSE;
sscanf_s(line, "%s %s %s", request->method, 16, request->path, 1024, request->version, 16);
// 解析头部
while ((line = strtok_s(NULL, "\r\n", &saveptr)) != NULL) {
if (strlen(line) == 0) break; // 空行,头部结束
char* colon = strchr(line, ':');
if (colon && request->headerCount < 50) {
*colon = '\0';
request->headers[request->headerCount].name = _strdup(line);
char* value = colon + 1;
while (*value == ' ') value++;
request->headers[request->headerCount].value = _strdup(value);
request->headerCount++;
}
}
// 获取Body
const char* bodyStart = strstr(data, "\r\n\r\n");
if (bodyStart) {
request->body = _strdup(bodyStart + 4);
request->bodyLength = strlen(request->body);
}
free(dataCopy);
return TRUE;
}
// 获取指定头部
const char* GetHeader(HTTP_REQUEST* request, const char* name) {
for (int i = 0; i < request->headerCount; i++) {
if (_stricmp(request->headers[i].name, name) == 0) {
return request->headers[i].value;
}
}
return NULL;
}
// 构建HTTP响应
void BuildHttpResponse(int statusCode, const char* contentType,
const char* body, char* response, int maxLen) {
const char* statusText;
switch (statusCode) {
case 200: statusText = "OK"; break;
case 404: statusText = "Not Found"; break;
case 500: statusText = "Internal Server Error"; break;
default: statusText = "Unknown"; break;
}
sprintf_s(response, maxLen,
"HTTP/1.1 %d %s\r\n"
"Content-Type: %s\r\n"
"Content-Length: %d\r\n"
"Connection: close\r\n"
"\r\n"
"%s",
statusCode, statusText, contentType, strlen(body), body);
}
5、课后作业
- 实现HTTP分块传输解析
- 添加Cookie处理
- 实现HTTP代理
- 编写HTTP流量分析工具
6、扩展阅读
- RFC 2616 HTTP/1.1
- HTTP/2协议
- HTTPS和TLS