如何用C语言编写支持音视频进度调整的服务器?问题排查与解决
问题分析与解决方案
是的,你的视频无法拖动进度条、总是从头播放的核心原因就是服务器没有处理HTTP范围请求(Range Request),也没有返回对应的响应头。下面详细拆解原因和解决步骤:
为什么会出现这个问题?
当你拖动<video>的currentTime时,浏览器不会重新请求整个视频文件,而是会发送带有Range: bytes=xxx-的请求,要求服务器返回视频的某一段内容。但你的服务器现在不管什么请求都返回200 OK和完整的文件内容:
你的服务器响应头:
HTTP/1.1 200 OK Content-Type: video/mp4 Content-Length: 105270886
而Apache服务器正确处理了Range请求,返回206 Partial Content状态码,并带上了Content-Range等关键头,告诉浏览器返回的是哪一段内容:
Apache的响应头:
HTTP/1.1 206 Partial Content Accept-Ranges: bytes Content-Range: bytes 58392576-105270885/105270886 Content-Length: 46878310
浏览器拿到完整文件后,无法定位到指定的时间点,只能从头播放;而拿到指定片段后,才能正确加载到对应的位置。
具体解决步骤
你需要修改服务器代码,实现Range请求解析和206 Partial Content响应的支持,具体如下:
1. 添加Range请求解析函数
先写一个工具函数,从请求头中提取Range的起始和结束字节:
// 解析Range头,返回值:1表示有Range请求,0表示无,-1表示解析失败 int parse_range(const char *request_buf, int file_total_len, long *start_byte, long *end_byte) { const char *range_header = strstr(request_buf, "Range: bytes="); if (!range_header) { *start_byte = 0; *end_byte = file_total_len - 1; return 0; } range_header += strlen("Range: bytes="); char *dash_pos = strchr(range_header, '-'); if (!dash_pos) return -1; // 提取起始字节 *dash_pos = '\0'; *start_byte = atol(range_header); if (*start_byte < 0) *start_byte = 0; if (*start_byte >= file_total_len) *start_byte = file_total_len - 1; // 提取结束字节 range_header = dash_pos + 1; if (*range_header == '\0') { *end_byte = file_total_len - 1; } else { *end_byte = atol(range_header); if (*end_byte >= file_total_len) *end_byte = file_total_len - 1; if (*end_byte < *start_byte) *end_byte = *start_byte; } return 1; }
2. 修改send_file函数,支持范围请求
更新文件发送逻辑,根据Range请求返回对应的片段内容:
// 注意:新增request_buf参数,用于传递浏览器的请求头 void send_file(int c_sock, const char *file_path, const char *file_ext, const char *request_buf) { int fd = open(file_path, O_RDONLY); if (fd == -1) { // 返回404错误 char err_head[1024]; sprintf(err_head, "HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\n\r\nFile Not Found"); send(c_sock, err_head, strlen(err_head), 0); close(fd); return; } int file_total_len = lseek(fd, 0, SEEK_END); long start_byte, end_byte; int has_range = parse_range(request_buf, file_total_len, &start_byte, &end_byte); int send_content_len = end_byte - start_byte + 1; // 构建响应头 char response_head[2048]; const char *content_type = file_ext; if (strcmp(file_ext, ".mp4") == 0) { content_type = "video/mp4"; } if (has_range == 1) { // 发送206 Partial Content响应 sprintf(response_head, "HTTP/1.1 206 Partial Content\r\n" "Content-Type: %s\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %d\r\n" "Content-Range: bytes %ld-%ld/%d\r\n" "\r\n", content_type, send_content_len, start_byte, end_byte, file_total_len); } else { // 发送200 OK完整响应 sprintf(response_head, "HTTP/1.1 200 OK\r\n" "Content-Type: %s\r\n" "Accept-Ranges: bytes\r\n" "Content-Length: %d\r\n" "\r\n", content_type, file_total_len); } send(c_sock, response_head, strlen(response_head), 0); // 定位到文件的起始位置 lseek(fd, start_byte, SEEK_SET); char buf[4096]; int sent_len = 0; while (sent_len < send_content_len) { memset(buf, 0, sizeof(buf)); int read_size = read(fd, buf, sizeof(buf)); if (read_size <= 0) break; // 确保不超过需要发送的内容长度 if (sent_len + read_size > send_content_len) { read_size = send_content_len - sent_len; } send_helper(c_sock, buf, read_size); sent_len += read_size; } close(fd); }
3. 更新send_helper函数,传入客户端套接字
把全局的c_sock改成参数传递,避免全局变量的问题:
void send_helper(int c_sock, char *content, int size) { while (size > 0) { int sent = send(c_sock, content, size, 0); if (sent <= 0) return; size -= sent; content += sent; } }
4. 修改请求接收逻辑,传递请求头
在接收浏览器请求后,把请求缓冲区传递给send_file:
while (1) { int c_sock = accept(s_sock, NULL, NULL); if (c_sock != -1) { char request_buf[4096]; int nread = recv(c_sock, request_buf, sizeof(request_buf), 0); if (nread > 0) { request_buf[nread] = '\0'; // 确保字符串终止 // 这里需要你自己解析请求中的文件路径(比如从GET /source/1.mp4提取file_path) // 假设你已经解析出file_path和file_ext send_file(c_sock, file_path, file_ext, request_buf); } close(c_sock); } }
关键注意点
- 添加
Accept-Ranges: bytes头:这会告诉浏览器你的服务器支持范围请求,浏览器才会在拖动进度条时发送Range请求。 - 处理边界情况:比如Range的起始字节超过文件长度、结束字节小于起始字节等,需要做容错处理,避免崩溃。
- 正确计算发送长度:发送的内容长度应该是Range指定的片段长度,而不是整个文件的长度。
内容的提问来源于stack exchange,提问作者嬲你屋里娘
相关产品推荐
相关产品推荐

