Open nonocast opened 2 years ago
Transfer-Encoding: chunked表示本次http response的body长度不能确定,比如后面我们看到的httpflv正是利用这个特性。
Transfer-Encoding: chunked
文件下载: 如果是文件下载,比如2G的一个iso,这时候因为文件大小是确定的,所以content-length会直接给出,然后body不断输入bytes,所以chunked和内容的长度无关,只和是否知道长度有关。
app.js
require('net') .createServer(function (sock) { sock.on('data', function (data) { sock.write('HTTP/1.1 200 OK\r\n'); sock.write('Transfer-Encoding: chunked\r\n\r\n'); sock.write('5\r\n'); sock.write('hello\r\n'); sock.write('6\r\n'); sock.write(' world\r\n'); sock.write('0\r\n\r\n'); }); }) .listen(9090);
通过curl和telnet验证:
% curl localhost:9090 hello world% % % telnet localhost 9090 Trying ::1... telnet: connect to address ::1: Connection refused Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. GET / HTTP/1.1 HTTP/1.1 200 OK Transfer-Encoding: chunked 5 hello 6 world 0
httpflv本身并不是rfc, 所以讲的也不是很清楚,根据wireshark观察,需要构建一个完整的flv,即flv 9字节head,然后prev size,然后tag,再是prev size,但是没有规定每次分包的大小,我也是简单写了一下,通过ffplay能够播放,但是flv.js还是有问题,不深究了,简单拉通即可。
#include "flv.h" #include "flvhttpd.h" #include <arpa/inet.h> #include <string.h> #include <ctype.h> #include <netinet/in.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <unistd.h> #include <sys/types.h> #include <pthread.h> #define kBLACK "\x1B[0m" #define kRED "\x1B[31m" #define kGREEN "\x1B[32m" FLV flv; void error_die(const char *); void accept_request(void *arg); static void print_hex(const uint8_t *data, unsigned long len); int main(void) { uint16_t port = 3000; struct sockaddr_in name = {.sin_family = AF_INET, .sin_port = htons(port), .sin_addr.s_addr = htonl(INADDR_ANY)}; int on = 1; int httpd = socket(PF_INET, SOCK_STREAM, 0); if (httpd == -1) error_die("socket"); if (setsockopt(httpd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) { error_die("setsocket failed"); } if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0) error_die("bind"); if (listen(httpd, 5) < 0) error_die("listen"); printf("server start at %d\n", port); int client = -1; struct sockaddr_in client_name; socklen_t client_name_len = sizeof(client_name); pthread_t newthread; while (true) { int client = accept(httpd, (struct sockaddr *)&client_name, &client_name_len); if (client == -1) error_die("accept"); if (pthread_create(&newthread, NULL, (void *)accept_request, (void *)(intptr_t)client) != 0) perror("pthread_create"); } close(httpd); return 0; } void error_die(const char *sc) { perror("error: "); exit(1); } void accept_request(void *arg) { int client = (intptr_t)arg; char request[1024], method[10], uri[255]; read(client, request, sizeof(request)); char *p = request; while (*p++ != ' ') {} memcpy(method, request, p - request - 1); while (*p++ != ' ') {} memcpy(uri, request + strlen(method) + 1, p - request - strlen(method) - 1 - 1); printf("%s%s %s%s\n", kGREEN, method, kBLACK, uri); // send header char head[] = "HTTP/1.1 200 OK\nConnection: Keep-Alive\nContent-Type: video/x-flv\n\ Server: flvhttpd\nTransfer-Encoding: chunked\r\n\r\n"; write(client, head, strlen(head)); if (flv.file == NULL) { FLV_Open(&flv, "sample.flv"); FLV_Parse(&flv); printf("parse flv ok\n"); } char chunkline[64]; uint8_t *buffer = malloc(1024 * 1024 * 10); uint8_t *prev = malloc(4); memset(prev, 0x00, 4); for (int i = 0; i < flv.tagCount; ++i) { FLVTag *t = flv.tags[i]; if (i == 0) { // send FLV 9 bytes head sprintf(chunkline, "%x\r\n", 9); printf("%s", chunkline); write(client, chunkline, strlen(chunkline)); fseek(flv.file, 0, SEEK_SET); fread(buffer, 1, 9, flv.file); print_hex(buffer, 9); write(client, buffer, 9); write(client, "\r\n", 2); sprintf(chunkline, "%x\r\n", 4); printf("%s", chunkline); write(client, chunkline, strlen(chunkline)); print_hex(prev, 4); write(client, prev, 4); write(client, "\r\n", 2); } // write tag sprintf(chunkline, "%lx\r\n", t->size); printf("%s", chunkline); write(client, chunkline, strlen(chunkline)); fseek(flv.file, t->offset, SEEK_SET); fread(buffer, 1, t->size, flv.file); print_hex(buffer, t->size); write(client, buffer, t->size); write(client, "\r\n", 2); // write prev sprintf(chunkline, "%x\r\n", 4); printf("%s", chunkline); write(client, chunkline, strlen(chunkline)); fread(prev, 1, 4, flv.file); print_hex(prev, 4); write(client, prev, 4); write(client, "\r\n", 2); usleep(1000000 / 30); } write(client, "0\r\n\r\n", 5); close(client); free(buffer); } static void print_hex(const uint8_t *data, unsigned long len) { if (len <= 32) { for (int i = 0; i < len; ++i) { printf("%02x ", data[i]); } printf("\n"); } else { for (int i = 0; i < 24; ++i) { printf("%02x ", data[i]); } printf("...... "); for (int i = 4; i > 0; --i) { printf("%02x ", data[len - i]); } printf("\n"); } }
http chunk
Transfer-Encoding: chunked
表示本次http response的body长度不能确定,比如后面我们看到的httpflv正是利用这个特性。文件下载: 如果是文件下载,比如2G的一个iso,这时候因为文件大小是确定的,所以content-length会直接给出,然后body不断输入bytes,所以chunked和内容的长度无关,只和是否知道长度有关。
app.js
通过curl和telnet验证:
httpflv
httpflv本身并不是rfc, 所以讲的也不是很清楚,根据wireshark观察,需要构建一个完整的flv,即flv 9字节head,然后prev size,然后tag,再是prev size,但是没有规定每次分包的大小,我也是简单写了一下,通过ffplay能够播放,但是flv.js还是有问题,不深究了,简单拉通即可。