C语言使用http协议传输文件

1. 用C语言在Linux服务端中写一个http文件服务器

  1. 新建一些文件夹与文件并填充内容

    1
    2
    3
    4
    5
    6
    mkdir -p /home/kali/cc++/crawler/test
    cd /home/kali/cc++/crawler/test
    mkdir subdir
    echo "Hello, world!" > hello.txt
    echo "<html><body>Test Page</body></html>" > index.html
    echo "This is a subdirectory file" > subdir/subfile.txt
  2. 编写代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <dirent.h>

#define PORT 8080
#define BUFFER_SIZE 1024
#define ROOT_DIR "/home/kali/cc++/crawler/test"

void handle_client(int client_fd);
void send_response(int client_fd, const char *header, const char *content_type, const char *file_path);
void generate_index(const char *directory, const char *index_path);
const char *get_content_type(const char *path);

int main() {
int server_fd, client_fd;
struct sockaddr_in server_addr, client_addr;
socklen_t client_addr_len = sizeof(client_addr);

// 创建套接字
server_fd = socket(AF_INET, SOCK_STREAM, 0);
if (server_fd < 0) {
perror("Socket creation failed");
exit(EXIT_FAILURE);
}

// 设置服务器地址和端口
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(PORT);

int optval = 1;
// 设置 SO_REUSEADDR
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)) < 0) {
perror("setsockopt SO_REUSEADDR failed");
}

// 绑定套接字
if (bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("Bind failed");
close(server_fd);
exit(EXIT_FAILURE);
}

// 监听连接
if (listen(server_fd, 10) < 0) {
perror("Listen failed");
close(server_fd);
exit(EXIT_FAILURE);
}

printf("HTTP server is running on port %d\n", PORT);

// 接受客户端连接并处理请求
while (1) {
client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &client_addr_len);
if (client_fd < 0) {
perror("Accept failed");
continue;
}
handle_client(client_fd);
close(client_fd);
}

close(server_fd);
return 0;
}

void handle_client(int client_fd) {
char buffer[BUFFER_SIZE];
int bytes_read;

// 读取客户端请求
bytes_read = read(client_fd, buffer, BUFFER_SIZE - 1);
if (bytes_read < 0) {
perror("Read failed");
return;
}
buffer[bytes_read] = '\0';

// 解析请求行
char method[16], path[256], version[16];
sscanf(buffer, "%s %s %s", method, path, version);

// 只处理 GET 请求
if (strcmp(method, "GET") == 0) {
// 移除路径中的 '/' 前缀并构造完整路径
char full_path[BUFFER_SIZE] = ROOT_DIR;
if (path[0] == '/') {
strncat(full_path, path, sizeof(full_path) - strlen(full_path) - 1);
}

// 检查是否请求目录并动态生成index.html
if (full_path[strlen(full_path) - 1] == '/') {
generate_index(full_path, "index.html");
strncat(full_path, "index.html", sizeof(full_path) - strlen(full_path) - 1);
}

// 获取文件的内容类型
const char *content_type = get_content_type(full_path);

// 发送响应
send_response(client_fd, "HTTP/1.1 200 OK", content_type, full_path);
} else {
// 发送 405 Method Not Allowed 响应
send_response(client_fd, "HTTP/1.1 405 Method Not Allowed", "text/plain", NULL);
}
}

void send_response(int client_fd, const char *header, const char *content_type, const char *file_path) {
char buffer[BUFFER_SIZE];
int file_fd, bytes_read;

// 发送响应头
snprintf(buffer, sizeof(buffer), "%s\r\nContent-Type: %s\r\n\r\n", header, content_type);
write(client_fd, buffer, strlen(buffer));

// 发送文件内容
if (file_path) {
file_fd = open(file_path, O_RDONLY);
if (file_fd < 0) {
perror("File open failed");
return;
}
while ((bytes_read = read(file_fd, buffer, BUFFER_SIZE)) > 0) {
write(client_fd, buffer, bytes_read);
}
close(file_fd);
}
}

void generate_index(const char *directory, const char *index_path) {
char full_index_path[BUFFER_SIZE];
snprintf(full_index_path, sizeof(full_index_path), "%s%s", directory, index_path);

FILE *file = fopen(full_index_path, "w");
if (!file) {
perror("Failed to create index.html");
return;
}

fprintf(file, "<html><head><title>Index of %s</title></head><body><h1>Index of %s</h1><ul>\n", directory, directory);

struct dirent *entry;
DIR *dp = opendir(directory);
if (dp == NULL) {
perror("opendir failed");
fclose(file);
return;
}

while ((entry = readdir(dp)) != NULL) {
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}

if (entry->d_type == DT_DIR) {
fprintf(file, "<li><a href=\"%s/\">%s/</a></li>\n", entry->d_name, entry->d_name);
} else {
fprintf(file, "<li><a href=\"%s\">%s</a></li>\n", entry->d_name, entry->d_name);
}
}

closedir(dp);

fprintf(file, "</ul></body></html>\n");
fclose(file);
}

const char *get_content_type(const char *path) {
const char *ext = strrchr(path, '.');
if (!ext) {
return "application/octet-stream";
}

if (strcmp(ext, ".html") == 0) {
return "text/html";
} else if (strcmp(ext, ".jpg") == 0) {
return "image/jpeg";
} else if (strcmp(ext, ".jpeg") == 0) {
return "image/jpeg";
} else if (strcmp(ext, ".png") == 0) {
return "image/png";
} else if (strcmp(ext, ".gif") == 0) {
return "image/gif";
} else if (strcmp(ext, ".css") == 0) {
return "text/css";
} else if (strcmp(ext, ".js") == 0) {
return "application/javascript";
} else if (strcmp(ext, ".json") == 0) {
return "application/json";
} else if (strcmp(ext, ".xml") == 0) {
return "application/xml";
} else if (strcmp(ext, ".pdf") == 0) {
return "application/pdf";
} else if (strcmp(ext, ".zip") == 0) {
return "application/zip";
} else if (strcmp(ext, ".mp3") == 0) {
return "audio/mpeg";
} else if (strcmp(ext, ".mp4") == 0) {
return "video/mp4";
} else if (strcmp(ext, ".avi") == 0) {
return "video/x-msvideo";
} else {
return "application/octet-stream";
}
}

  1. 编译运行。
  2. 使用浏览器输入网址浏览http://10.0.3.1:8080/nextdir/

2.用C语言在Windows中写一个http文件下载器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <stdbool.h>
#pragma comment(lib, "ws2_32.lib")
#pragma warning(disable:4996)

#define BUFFER_SIZE 4096

void download_file(const char* url, const char* output_file);

int main() {
const char* url = "http://10.0.3.1:8080/test/hello.txt"; // 替换为您的URL
const char* output_file = "hello.txt"; // 替换为输出文件名

// 初始化Winsock
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
printf("Winsock初始化失败\n");
return 1;
}

// 下载文件
download_file(url, output_file);

// 清理Winsock
WSACleanup();

return 0;
}

void download_file(const char* url, const char* output_file) {
char ip[256], path[256];
sscanf(url, "http://%255[^/]/%255[^\n]", ip, path);

struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080); // 使用固定端口 8080
inet_pton(AF_INET, ip, &server_addr.sin_addr.s_addr);

// 创建套接字
SOCKET sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == INVALID_SOCKET) {
printf("创建套接字失败\n");
return;
}

// 连接服务器
if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
printf("连接服务器失败\n");
closesocket(sockfd);
return;
}

// 发送HTTP请求
char request[BUFFER_SIZE];
snprintf(request, sizeof(request), "GET /%s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", path, ip);
send(sockfd, request, strlen(request), 0);

// 打开输出文件
FILE* file = fopen(output_file, "wb");
if (!file) {
printf("打开输出文件失败\n");
closesocket(sockfd);
return;
}

// 接收HTTP响应并写入文件
char buffer[BUFFER_SIZE];
int bytes_received;
bool header_passed = false;
while ((bytes_received = recv(sockfd, buffer, sizeof(buffer), 0)) > 0) {
if (!header_passed) {
// 跳过HTTP响应头
char* header_end = strstr(buffer, "\r\n\r\n");
if (header_end) {
header_end += 4; // 跳过"\r\n\r\n"
bytes_received -= (header_end - buffer);
memmove(buffer, header_end, bytes_received);
header_passed = true;
}
else {
// 尚未完整接收到HTTP头,继续接收
continue;
}
}
fwrite(buffer, 1, bytes_received, file);
}

// 关闭文件和套接字
fclose(file);
closesocket(sockfd);

printf("文件下载成功:%s\n", output_file);
}